From 4f9666da561b317729964a402c3f05309778fe90 Mon Sep 17 00:00:00 2001 From: Sami Merila Date: Fri, 22 Jan 2010 17:06:05 +0200 Subject: S60Style: Add support for animations to style Add support of theme animations to AVKON-based QS60Style. Currently only QProgressBar's have animations, but adding others with same kind of S60 theming (mainly note animations) would be really easy to do. Task-number: QTBUG-4050 Reviewed-by: Alessandro Portale --- src/gui/styles/qs60style.cpp | 143 ++++++++++++++++---- src/gui/styles/qs60style.h | 3 + src/gui/styles/qs60style_p.h | 105 ++++++++++++++- src/gui/styles/qs60style_s60.cpp | 237 ++++++++++++++++++++++++++++++++- src/gui/styles/qs60style_simulated.cpp | 5 + src/gui/styles/styles.pri | 2 +- 6 files changed, 464 insertions(+), 31 deletions(-) diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index 100303e..498c6ef 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -564,9 +564,11 @@ QPixmap QS60StylePrivate::cachedPart(QS60StyleEnums::SkinParts part, const QSize &size, QPainter *painter, SkinElementFlags flags) { QPixmap result; + const int animationFrame = (flags & SF_Animation) ? currentAnimationFrame(part) : 0; + const QString cacheKey = - QString::fromLatin1("S60Style: SkinParts=%1 QSize=%2|%3 SkinPartFlags=%4") - .arg((int)part).arg(size.width()).arg(size.height()).arg((int)flags); + QString::fromLatin1("S60Style: SkinParts=%1 QSize=%2|%3 SkinPartFlags=%4 AnimationFrame=%5") + .arg((int)part).arg(size.width()).arg(size.height()).arg((int)flags).arg(animationFrame); if (!QPixmapCache::find(cacheKey, result)) { result = QS60StylePrivate::part(part, size, painter, flags); QPixmapCache::insert(cacheKey, result); @@ -813,13 +815,13 @@ QSize QS60StylePrivate::partSize(QS60StyleEnums::SkinParts part, SkinElementFlag //ratio of 1:2 for horizontal tab bars (and 2:1 for vertical ones). result.setWidth(result.height() >> 1); break; - + case QS60StyleEnums::SP_QgnGrafNsliderEndLeft: case QS60StyleEnums::SP_QgnGrafNsliderEndRight: case QS60StyleEnums::SP_QgnGrafNsliderMiddle: result.setWidth(result.height() >> 1); break; - + case QS60StyleEnums::SP_QgnGrafNsliderMarker: case QS60StyleEnums::SP_QgnGrafNsliderMarkerSelected: result.scale(pixelMetric(QStyle::PM_SliderLength), @@ -948,11 +950,11 @@ void QS60Style::drawComplexControl(ComplexControl control, const QStyleOptionCom //Highlight /* if (optionSlider->state & QStyle::State_HasFocus) drawPrimitive(PE_FrameFocusRect, optionSlider, painter, widget);*/ - + //Groove graphics if (QS60StylePrivate::hasSliderGrooveGraphic()) { - const QS60StylePrivate::SkinElements grooveElement = horizontal ? - QS60StylePrivate::SE_SliderGrooveHorizontal : + const QS60StylePrivate::SkinElements grooveElement = horizontal ? + QS60StylePrivate::SE_SliderGrooveHorizontal : QS60StylePrivate::SE_SliderGrooveVertical; QS60StylePrivate::drawSkinElement(grooveElement, painter, sliderGroove, flags); } else { @@ -975,7 +977,7 @@ void QS60Style::drawComplexControl(ComplexControl control, const QStyleOptionCom if (optionSlider->state & QStyle::State_Sunken) handleElement = horizontal ? QS60StylePrivate::SE_SliderHandleSelectedHorizontal : QS60StylePrivate::SE_SliderHandleSelectedVertical; - else + else handleElement = horizontal ? QS60StylePrivate::SE_SliderHandleHorizontal : QS60StylePrivate::SE_SliderHandleVertical; QS60StylePrivate::drawSkinElement(handleElement, painter, sliderHandle, flags); @@ -994,7 +996,7 @@ void QS60Style::drawComplexControl(ComplexControl control, const QStyleOptionCom buttonOption.QStyleOption::operator=(*cmb); const int maxHeight = cmbxFrame.height(); const int maxWidth = cmbxFrame.width() - cmbxEditField.width(); - const int topLeftPoint = direction ? + const int topLeftPoint = direction ? (cmbxEditField.right() + 1) : (cmbxEditField.left() + 1 - maxWidth); const QRect buttonRect(topLeftPoint, cmbxEditField.top(), maxWidth, maxHeight); buttonOption.rect = buttonRect; @@ -1648,17 +1650,20 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, // busy indicator const QS60StylePrivate::SkinElementFlag orientationFlag = optionProgressBar->orientation == Qt::Horizontal ? QS60StylePrivate::SF_PointNorth : QS60StylePrivate::SF_PointWest; - QS60StylePrivate::drawSkinPart(QS60StyleEnums::SP_QgnGrafBarWait, painter, progressRect, flags | orientationFlag); + + QS60StylePrivate::drawSkinPart(QS60StyleEnums::SP_QgnGrafBarWaitAnim, + painter, progressRect, flags | orientationFlag | QS60StylePrivate::SF_Animation ); } else { const qreal progressFactor = (optionProgressBar->minimum == optionProgressBar->maximum) ? 1.0 : (qreal)optionProgressBar->progress / optionProgressBar->maximum; + const int frameWidth = pixelMetric(PM_DefaultFrameWidth, option, widget); if (optionProgressBar->orientation == Qt::Horizontal) { progressRect.setWidth(int(progressRect.width() * progressFactor)); if(optionProgressBar->direction == Qt::RightToLeft) - progressRect.translate(optionProgressBar->rect.width()-progressRect.width(), 0); - progressRect.adjust(1, 0, -1, 0); + progressRect.translate(optionProgressBar->rect.width() - progressRect.width(), 0); + progressRect.adjust(frameWidth, 0, -frameWidth, 0); } else { - progressRect.adjust(0, 1, 0, -1); + progressRect.adjust(0, frameWidth, 0, -frameWidth); progressRect.setTop(progressRect.bottom() - int(progressRect.height() * progressFactor)); } @@ -1925,8 +1930,7 @@ void QS60Style::drawControl(ControlElement element, const QStyleOption *option, break; case CE_MenuScroller: break; - case CE_FocusFrame: - { + case CE_FocusFrame: { // The pen width should nearly fill the layoutspacings around the widget const int penWidth = qMin(pixelMetric(QS60Style::PM_LayoutVerticalSpacing), pixelMetric(QS60Style::PM_LayoutHorizontalSpacing)) @@ -2004,8 +2008,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti } break; #endif // QT_NO_LINEEDIT - case PE_IndicatorCheckBox: - { + case PE_IndicatorCheckBox: { // Draw checkbox indicator as color skinned graphics. const QS60StyleEnums::SkinParts skinPart = (option->state & QStyle::State_On) ? QS60StyleEnums::SP_QgnIndiCheckboxOn : QS60StyleEnums::SP_QgnIndiCheckboxOff; @@ -2077,7 +2080,7 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti case PE_PanelButtonCommand: case PE_PanelButtonTool: case PE_PanelButtonBevel: - case PE_FrameButtonBevel: { + case PE_FrameButtonBevel: if (QS60StylePrivate::canDrawThemeBackground(option->palette.base())) { const bool isPressed = option->state & QStyle::State_Sunken; const QS60StylePrivate::SkinElements skinElement = @@ -2085,7 +2088,6 @@ void QS60Style::drawPrimitive(PrimitiveElement element, const QStyleOption *opti QS60StylePrivate::drawSkinElement(skinElement, painter, option->rect, flags); } else { commonStyleDraws = true; - } } break; #ifndef QT_NO_TOOLBUTTON @@ -2361,8 +2363,7 @@ QSize QS60Style::sizeFromContents(ContentsType ct, const QStyleOption *opt, if (const QStyleOptionFrame *f = qstyleoption_cast(opt)) sz += QSize(2 * f->lineWidth, 4 * f->lineWidth); break; - case CT_TabBarTab: - { + case CT_TabBarTab: { const QSize naviPaneSize = QS60StylePrivate::naviPaneSize(); sz = QCommonStyle::sizeFromContents(ct, opt, csz, widget); if (naviPaneSize.height() > sz.height()) @@ -2835,6 +2836,12 @@ void QS60Style::polish(QWidget *widget) if (!widget) return; + //Currently we only support animations in QProgressBar. +#ifndef QT_NO_PROGRESSBAR + if (qobject_cast(widget)) + widget->installEventFilter(this); +#endif + if (false #ifndef QT_NO_SCROLLBAR || qobject_cast(widget) @@ -2867,6 +2874,8 @@ void QS60Style::polish(QWidget *widget) */ void QS60Style::unpolish(QWidget *widget) { + Q_D(QS60Style); + if (false #ifndef QT_NO_SCROLLBAR || qobject_cast(widget) @@ -2893,6 +2902,12 @@ void QS60Style::unpolish(QWidget *widget) if (widget) widget->setPalette(QPalette()); +#ifndef QT_NO_PROGRESSBAR + if (QProgressBar *bar = qobject_cast(widget)) { + widget->removeEventFilter(this); + d->m_bars.removeAll(bar); + } +#endif QCommonStyle::unpolish(widget); } @@ -2923,11 +2938,25 @@ void QS60Style::unpolish(QApplication *application) */ bool QS60Style::event(QEvent *e) { -#ifdef QT_KEYPAD_NAVIGATION - if (QS60StylePrivate::isTouchSupported()) - return false; Q_D(QS60Style); + +#ifdef QT_KEYPAD_NAVIGATION + const QEvent::Type eventType = e->type(); + if ((eventType == QEvent::FocusIn || + eventType == QEvent::FocusOut || + eventType == QEvent::EnterEditFocus || + eventType == QEvent::LeaveEditFocus) && + QS60StylePrivate::isTouchSupported()) + return false; +#endif + switch (e->type()) { + case QEvent::Timer: { + QTimerEvent *te = static_cast(e); + timerEvent(te); + } + break; +#ifdef QT_KEYPAD_NAVIGATION case QEvent::FocusIn: if (QWidget *focusWidget = QApplication::focusWidget()) { if (!d->m_focusFrame) @@ -2946,12 +2975,10 @@ bool QS60Style::event(QEvent *e) if (d->m_focusFrame) d->m_focusFrame->update(); break; +#endif default: break; } -#else - Q_UNUSED(e) -#endif return false; } @@ -3045,6 +3072,68 @@ QIcon QS60Style::standardIconImplementation(StandardPixmap standardIcon, QCommonStyle::standardIconImplementation(standardIcon, option, widget) : QIcon(cachedPixMap); } +/*! + \internal + Animate indeterminate progress bars only when visible +*/ +bool QS60Style::eventFilter(QObject *object, QEvent *event) +{ +#ifdef Q_WS_S60 +#ifndef QT_NO_PROGRESSBAR + Q_D(QS60Style); + switch(event->type()) { + case QEvent::StyleChange: + case QEvent::Show: + if (QProgressBar *bar = qobject_cast(object)) { + if (!d->m_bars.contains(bar)) + d->m_bars << bar; + if (d->m_bars.size() == 1) //only start with first animated progressbar + d->startAnimation(QS60StyleEnums::SP_QgnGrafBarWaitAnim); + } + break; + case QEvent::Destroy: + case QEvent::Hide: + d->stopAnimation(QS60StyleEnums::SP_QgnGrafBarWaitAnim); + d->m_bars.removeAll(reinterpret_cast(object)); + break; + default: + break; + } +#endif // QT_NO_PROGRESSBAR +#endif // Q_WS_S60 + return QStyle::eventFilter(object, event); +} + +void QS60Style::timerEvent(QTimerEvent *event) +{ +#ifdef Q_WS_S60 +#ifndef QT_NO_PROGRESSBAR + Q_D(QS60Style); + + QS60StyleAnimation *progressBarAnimation = + QS60StylePrivate::animationDefinition(QS60StyleEnums::SP_QgnGrafBarWaitAnim); + + if (event->timerId() == progressBarAnimation->timerId()) { + + Q_ASSERT(progressBarAnimation->interval() > 0); + + if (progressBarAnimation->currentFrame() == progressBarAnimation->frameCount() ) + if (progressBarAnimation->playMode() == QS60StyleEnums::AM_Looping) + progressBarAnimation->setCurrentFrame(0); + else + d->stopAnimation(progressBarAnimation->animationId()); + + foreach (QProgressBar *bar, d->m_bars) { + if ((bar->minimum() == 0 && bar->maximum() == 0)) + bar->update(); + } + progressBarAnimation->setCurrentFrame(progressBarAnimation->currentFrame() + 1); + } +#endif // QT_NO_PROGRESSBAR +#endif // Q_WS_S60 + event->ignore(); +} + extern QPoint qt_s60_fill_background_offset(const QWidget *targetWidget); bool qt_s60_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush) diff --git a/src/gui/styles/qs60style.h b/src/gui/styles/qs60style.h index adcb313..82cc21c 100644 --- a/src/gui/styles/qs60style.h +++ b/src/gui/styles/qs60style.h @@ -94,6 +94,9 @@ protected Q_SLOTS: QIcon standardIconImplementation( StandardPixmap standardIcon, const QStyleOption * option = 0, const QWidget * widget = 0 ) const; +protected: + void timerEvent(QTimerEvent *event); + bool eventFilter(QObject *o, QEvent *e); private: Q_DISABLE_COPY(QS60Style) friend class QStyleFactory; diff --git a/src/gui/styles/qs60style_p.h b/src/gui/styles/qs60style_p.h index 1417552..2d2f2e1 100644 --- a/src/gui/styles/qs60style_p.h +++ b/src/gui/styles/qs60style_p.h @@ -93,6 +93,29 @@ class QS60StyleEnums #endif // !Q_WS_S60 public: + + // S60 definitions within theme + enum ThemeDefinitions { + TD_AnimationData, + }; + + //Defines which values are contained within animation data (retrieved using TD_AnimationData). + //Additionally defines the order in which the items are given out in QList. + enum AnimationData { + AD_Interval = 0, + AD_NumberOfFrames, + AD_AnimationPlayMode, //currently not used as themes seem to contain invalid data + }; + + // Animation modes + enum AnimationMode { + AM_PlayOnce = 0, //animation is played exactly once + AM_Looping, //animation is repeated until stopped + AM_Bounce //animation is played repeatedly until stopped, + //but frames are played in reverse order every second time + //(no support yet) + }; + // S60 look-and-feel font categories enum FontCategories { FC_Undefined, @@ -104,7 +127,7 @@ public: }; enum SkinParts { - SP_QgnGrafBarWait, + SP_QgnGrafBarWaitAnim, SP_QgnGrafBarFrameCenter, SP_QgnGrafBarFrameSideL, SP_QgnGrafBarFrameSideR, @@ -287,7 +310,70 @@ public: }; }; +#ifdef Q_WS_S60 +class CAknBitmapAnimation; +NONSHARABLE_CLASS (AnimationData) : public QObject +{ +public: + AnimationData(const QS60StyleEnums::SkinParts part, int frames, int interval); + + const QS60StyleEnums::SkinParts m_id; + int m_frames; + int m_interval; + QS60StyleEnums::AnimationMode m_mode; +}; + + +NONSHARABLE_CLASS (AnimationDataV2) : public AnimationData +{ +public: + AnimationDataV2(const AnimationData &data); + ~AnimationDataV2(); + + CAknBitmapAnimation *m_animation; + int m_currentFrame; + bool m_resourceBased; + int m_timerId; +}; + + +class QS60StyleAnimation : public QObject +{ +public: + QS60StyleAnimation(const QS60StyleEnums::SkinParts part, int frames, int interval); + ~QS60StyleAnimation(); + +public: + QS60StyleEnums::SkinParts animationId() const {return m_currentData->m_id;} + int frameCount() const { return m_currentData->m_frames;} + int interval() const {return m_currentData->m_interval;} + QS60StyleEnums::AnimationMode playMode() const {return m_currentData->m_mode;} + CAknBitmapAnimation* animationObject() const {return m_currentData->m_animation;} + bool isResourceBased() const {return m_currentData->m_resourceBased;} + int timerId() const {return m_currentData->m_timerId;} + int currentFrame() const {return m_currentData->m_currentFrame;} + + void setFrameCount(const int &frameCount) {m_currentData->m_frames = frameCount;} + void setInterval(const int &interval) {m_currentData->m_interval = interval;} + void setAnimationObject(CAknBitmapAnimation* animation); + void setResourceBased(bool resourceBased) {m_currentData->m_resourceBased = resourceBased;} + void setTimerId(const int &timerId) {m_currentData->m_timerId = timerId;} + void setCurrentFrame(const int ¤tFrame) {m_currentData->m_currentFrame = currentFrame;} + + void resetToDefaults(); + +private: //data members + //TODO: consider changing these to non-pointers as the classes are rather small anyway + AnimationData *m_defaultData; + AnimationDataV2 *m_currentData; +}; + +#endif //Q_WS_S60 + + class QFocusFrame; +class QProgressBar; +class QS60StyleAnimation; // Private class #ifdef Q_OS_SYMBIAN @@ -371,6 +457,7 @@ public: SF_StateEnabled = 0x0010, // Enabled = the default SF_StateDisabled = 0x0020, SF_ColorSkinned = 0x0040, // pixmap is colored with foreground pen color + SF_Animation = 0x0080, }; enum CacheClearReason { @@ -455,6 +542,16 @@ public: //so that theme graphic background can be drawn. static bool canDrawThemeBackground(const QBrush &backgroundBrush); + static int currentAnimationFrame(QS60StyleEnums::SkinParts part); +#ifdef Q_WS_S60 + + //No support for animations on emulated style + void startAnimation(QS60StyleEnums::SkinParts animation); + void stopAnimation(QS60StyleEnums::SkinParts animation); + static QS60StyleAnimation* animationDefinition(QS60StyleEnums::SkinParts part); + +#endif + private: static void drawPart(QS60StyleEnums::SkinParts part, QPainter *painter, const QRect &rect, SkinElementFlags flags = KDefaultSkinElementFlags); @@ -497,6 +594,12 @@ private: QPalette m_originalPalette; QPointer m_focusFrame; + +#ifdef Q_WS_S60 + //list of progress bars having animation running + QList m_bars; +#endif + }; QT_END_NAMESPACE diff --git a/src/gui/styles/qs60style_s60.cpp b/src/gui/styles/qs60style_s60.cpp index d03574f..5f3a499 100644 --- a/src/gui/styles/qs60style_s60.cpp +++ b/src/gui/styles/qs60style_s60.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #if !defined(QT_NO_STYLE_S60) || defined(QT_PLUGIN) @@ -72,6 +73,7 @@ enum TDrawType { EDrawIcon, EDrawGulIcon, EDrawBackground, + EDrawAnimation, ENoDraw }; @@ -97,6 +99,47 @@ typedef struct { int newMinorSkinId; } partMapEntry; +AnimationData::AnimationData(const QS60StyleEnums::SkinParts part, int frames, int interval) : m_id(part), + m_frames(frames), m_interval(interval), m_mode(QS60StyleEnums::AM_Looping) +{ +} + +AnimationDataV2::AnimationDataV2(const AnimationData &data) : AnimationData(data.m_id, data.m_frames, data.m_interval), + m_resourceBased(false), m_animation(0), m_timerId(0) +{ +} +AnimationDataV2::~AnimationDataV2() +{ + delete m_animation; +} + +QS60StyleAnimation::QS60StyleAnimation(const QS60StyleEnums::SkinParts part, int frames, int interval) +{ + QT_TRAP_THROWING(m_defaultData = new (ELeave) AnimationData(part, frames, interval)); + QT_TRAP_THROWING(m_currentData = new (ELeave) AnimationDataV2(*m_defaultData)); +} + +QS60StyleAnimation::~QS60StyleAnimation() +{ + delete m_currentData; + delete m_defaultData; +} + +void QS60StyleAnimation::setAnimationObject(CAknBitmapAnimation* animation) +{ + Q_ASSERT(animation); + if (m_currentData->m_animation) + delete m_currentData->m_animation; + m_currentData->m_animation = animation; +} + +void QS60StyleAnimation::resetToDefaults() +{ + delete m_currentData; + m_currentData = 0; + QT_TRAP_THROWING(m_currentData = new (ELeave) AnimationDataV2(*m_defaultData)); +} + class QS60StyleModeSpecifics { public: @@ -113,6 +156,8 @@ public: static QSize naviPaneSize(); static TAknsItemID partSpecificThemeId(int part); + static QVariant themeDefinition(QS60StyleEnums::ThemeDefinitions definition, QS60StyleEnums::SkinParts part); + private: static QPixmap createSkinnedGraphicsLX(QS60StyleEnums::SkinParts part, const QSize &size, QS60StylePrivate::SkinElementFlags flags); @@ -128,7 +173,7 @@ private: }; const partMapEntry QS60StyleModeSpecifics::m_partMap[] = { - /* SP_QgnGrafBarWait */ {KAknsIIDQgnGrafBarWaitAnim, EDrawIcon, ES60_All, -1,-1}, + /* SP_QgnGrafBarWaitAnim */ {KAknsIIDQgnGrafBarWaitAnim, EDrawAnimation, ES60_All, -1,-1}, /* SP_QgnGrafBarFrameCenter */ {KAknsIIDQgnGrafBarFrameCenter, EDrawIcon, ES60_All, -1,-1}, /* SP_QgnGrafBarFrameSideL */ {KAknsIIDQgnGrafBarFrameSideL, EDrawIcon, ES60_All, -1,-1}, /* SP_QgnGrafBarFrameSideR */ {KAknsIIDQgnGrafBarFrameSideR, EDrawIcon, ES60_All, -1,-1}, @@ -371,7 +416,7 @@ QPixmap QS60StyleModeSpecifics::colorSkinnedGraphics( void QS60StyleModeSpecifics::fallbackInfo(const QS60StyleEnums::SkinParts &stylePart, TInt &fallbackIndex) { switch(stylePart) { - case QS60StyleEnums::SP_QgnGrafBarWait: + case QS60StyleEnums::SP_QgnGrafBarWaitAnim: fallbackIndex = EMbmAvkonQgn_graf_bar_wait_1; break; case QS60StyleEnums::SP_QgnGrafBarFrameCenter: @@ -709,6 +754,69 @@ QPixmap QS60StyleModeSpecifics::createSkinnedGraphicsLX( // QS60WindowSurface::lockBitmapHeap(); break; } + case EDrawAnimation: { + CFbsBitmap* animationFrame; + CFbsBitmap* frameMask; + CAknBitmapAnimation* aknAnimation = 0; + TBool constructedFromTheme = ETrue; + + QS60StyleAnimation* animation = QS60StylePrivate::animationDefinition(part); //ownership is not passed + if (animation) { + if (!animation->animationObject() && !animation->isResourceBased()) {// no pre-made item exists, create new animation + CAknBitmapAnimation* newAnimation = CAknBitmapAnimation::NewL(); + CleanupStack::PushL(newAnimation); + if (newAnimation) + constructedFromTheme = newAnimation->ConstructFromSkinL(skinId); + if (constructedFromTheme && newAnimation->BitmapAnimData()->FrameArray().Count() > 0) { + animation->setResourceBased(false); + animation->setAnimationObject(newAnimation); //animation takes ownership + } + CleanupStack::Pop(newAnimation); + } + //fill-in stored information + aknAnimation = animation->animationObject(); + constructedFromTheme = !animation->isResourceBased(); + } + + const int currentFrame = QS60StylePrivate::currentAnimationFrame(part); + if (constructedFromTheme && aknAnimation && aknAnimation->BitmapAnimData()->FrameArray().Count() > 0) { + //Animation was created succesfully and contains frames, just fetch current frame + if(currentFrame >= aknAnimation->BitmapAnimData()->FrameArray().Count()) + User::Leave(KErrOverflow); + const CBitmapFrameData* frameData = aknAnimation->BitmapAnimData()->FrameArray().At(currentFrame); + if (frameData) { + animationFrame = frameData->Bitmap(); + frameMask = frameData->Mask(); + } + } else { + //Theme does not contain animation theming, create frames from resource file + TInt fallbackGraphicID = -1; + fallbackInfo(part, fallbackGraphicID); + fallbackGraphicID = fallbackGraphicID + (currentFrame * 2); //skip masks + TInt fallbackGraphicsMaskID = + (fallbackGraphicID == KErrNotFound) ? KErrNotFound : fallbackGraphicID + 1; //masks are auto-generated as next in mif files + if (fallbackGraphicsMaskID != KErrNotFound) + fallbackGraphicsMaskID = fallbackGraphicsMaskID + (currentFrame * 2); //skip actual graphics + + //Then draw animation frame + AknsUtils::CreateIconL( + skinInstance, + KAknsIIDDefault, //animation is not themed, lets force fallback graphics + animationFrame, + frameMask, + AknIconUtils::AvkonIconFileName(), + fallbackGraphicID , + fallbackGraphicsMaskID); + } + result = fromFbsBitmap(animationFrame, frameMask, flags, targetSize); + if (!constructedFromTheme) { + delete animationFrame; + animationFrame = 0; + delete frameMask; + frameMask = 0; + } + break; + } } if (!result) result = QPixmap(); @@ -984,8 +1092,13 @@ void QS60StylePrivate::setActiveLayout() m_pmPointer = data[activeLayoutIndex]; } +Q_GLOBAL_STATIC(QList, m_animations) + QS60StylePrivate::QS60StylePrivate() { + //Animation defaults need to be created when style is instantiated + QS60StyleAnimation* progressBarAnimation = new QS60StyleAnimation(QS60StyleEnums::SP_QgnGrafBarWaitAnim, 7, 100); + m_animations()->append(progressBarAnimation); // No need to set active layout, if dynamic metrics API is available setActiveLayout(); } @@ -1186,6 +1299,11 @@ void QS60StylePrivate::handleSkinChange() setThemePalette(topLevelWidget); topLevelWidget->ensurePolished(); } +#ifndef QT_NO_PROGRESSBAR + //re-start animation timer + stopAnimation(QS60StyleEnums::SP_QgnGrafBarWaitAnim); //todo: once we have more animations, we could say "stop all running ones" + startAnimation(QS60StyleEnums::SP_QgnGrafBarWaitAnim); //and "re-start all previously running ones" +#endif } QSize QS60StylePrivate::naviPaneSize() @@ -1205,6 +1323,121 @@ QSize QS60StyleModeSpecifics::naviPaneSize() return QSize(0,0); } +int QS60StylePrivate::currentAnimationFrame(QS60StyleEnums::SkinParts part) +{ + QS60StyleAnimation *animation = animationDefinition(part); + // todo: looping could be done in QS60Style::timerEvent + if (animation->frameCount() == animation->currentFrame()) + animation->setCurrentFrame(0); + return animation->currentFrame(); +} + +QS60StyleAnimation* QS60StylePrivate::animationDefinition(QS60StyleEnums::SkinParts part) +{ + int i = 0; + const int animationsCount = m_animations()->isEmpty() ? 0 : m_animations()->count(); + for(; i < animationsCount; i++) { + if (part == m_animations()->at(i)->animationId()) + break; + } + return m_animations()->at(i); +} + +void QS60StylePrivate::startAnimation(QS60StyleEnums::SkinParts animationPart) +{ + Q_Q(QS60Style); + + //Query animation data from theme and store values to local struct. + QVariant themeAnimationDataVariant = QS60StyleModeSpecifics::themeDefinition( + QS60StyleEnums::TD_AnimationData, animationPart); + QList themeAnimationData = themeAnimationDataVariant.toList(); + + QS60StyleAnimation *animation = QS60StylePrivate::animationDefinition(animationPart); + if (animation) { + if (themeAnimationData.at(QS60StyleEnums::AD_Interval).toInt() != 0) + animation->setInterval(themeAnimationData.at(QS60StyleEnums::AD_Interval).toInt()); + + if (themeAnimationData.at(QS60StyleEnums::AD_NumberOfFrames).toInt() != 0) + animation->setFrameCount(themeAnimationData.at(QS60StyleEnums::AD_NumberOfFrames).toInt()); + + //todo: playmode is ignored for now, since it seems to return invalid data on some themes + //lets use the table values for play mode + + animation->setCurrentFrame(0); //always initialize + const int timerId = q->startTimer(animation->interval()); + animation->setTimerId(timerId); + } +} + +void QS60StylePrivate::stopAnimation(QS60StyleEnums::SkinParts animationPart) +{ + Q_Q(QS60Style); + + QS60StyleAnimation *animation = QS60StylePrivate::animationDefinition(animationPart); + if (animation) { + animation->setCurrentFrame(0); + if (animation->timerId() != 0) { + q->killTimer(animation->timerId()); + animation->setTimerId(0); + } + animation->resetToDefaults(); + } +} + +QVariant QS60StyleModeSpecifics::themeDefinition( + QS60StyleEnums::ThemeDefinitions definition, QS60StyleEnums::SkinParts part) +{ + MAknsSkinInstance* skinInstance = AknsUtils::SkinInstance(); + + Q_ASSERT(skinInstance); + + switch(definition) { + //Animation definitions + case QS60StyleEnums::TD_AnimationData: + { + CAknsBmpAnimItemData *animationData; + TAknsItemID animationSkinId = partSpecificThemeId(part); + QList list; + + TRAPD( error, QT_TRYCATCH_LEAVING( + animationData = static_cast(skinInstance->CreateUncachedItemDataL( + animationSkinId, EAknsITBmpAnim)))); + if (error) + return list; + + if (animationData) { + list.append((int)animationData->FrameInterval()); + list.append((int)animationData->NumberOfImages()); + + QS60StyleEnums::AnimationMode playMode; + switch(animationData->PlayMode()) { + case CBitmapAnimClientData::EPlay: + playMode = QS60StyleEnums::AM_PlayOnce; + break; + case CBitmapAnimClientData::ECycle: + playMode = QS60StyleEnums::AM_Looping; + break; + case CBitmapAnimClientData::EBounce: + playMode = QS60StyleEnums::AM_Bounce; + break; + default: + break; + } + list.append(QVariant((int)playMode)); + delete animationData; + } else { + list.append(0); + list.append(0); + } + return list; + } + break; + default: + break; + } + return QVariant(); +} + #endif // Q_WS_S60 QT_END_NAMESPACE diff --git a/src/gui/styles/qs60style_simulated.cpp b/src/gui/styles/qs60style_simulated.cpp index bd43eb7..557243c 100644 --- a/src/gui/styles/qs60style_simulated.cpp +++ b/src/gui/styles/qs60style_simulated.cpp @@ -364,6 +364,11 @@ QFont QS60StylePrivate::s60Font_specific( return result; } +int QS60StylePrivate::currentAnimationFrame(QS60StyleEnums::SkinParts part) +{ + return 0; +} + /*! Constructs a QS60Style object. */ diff --git a/src/gui/styles/styles.pri b/src/gui/styles/styles.pri index 7e5c55a..f30f5af 100644 --- a/src/gui/styles/styles.pri +++ b/src/gui/styles/styles.pri @@ -168,7 +168,7 @@ contains( styles, s60 ):contains(QT_CONFIG, s60) { SOURCES += styles/qs60style.cpp symbian { SOURCES += styles/qs60style_s60.cpp - LIBS += -laknicon -laknskins -laknskinsrv -lfontutils -legul + LIBS += -laknicon -laknskins -laknskinsrv -lfontutils -legul -lbmpanim } else { SOURCES += styles/qs60style_simulated.cpp RESOURCES += styles/qstyle_s60_simulated.qrc -- cgit v0.12 From 7b8b83d2ea788e73a4c0f7dee09ba3394787e6bb Mon Sep 17 00:00:00 2001 From: Sami Merila Date: Fri, 22 Jan 2010 17:18:48 +0200 Subject: S60Style: Add support for animations to style (part2) Missing #include added to avoid build break. Task-number: QTBUG-4050 Reviewed-by: Alessandro Portale --- src/gui/styles/qs60style.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/gui/styles/qs60style.cpp b/src/gui/styles/qs60style.cpp index 498c6ef..28e94a3 100644 --- a/src/gui/styles/qs60style.cpp +++ b/src/gui/styles/qs60style.cpp @@ -68,6 +68,7 @@ #include "qtoolbutton.h" #include "qfocusframe.h" #include "qformlayout.h" +#include "qprogressbar.h" #include "private/qtoolbarextension_p.h" #include "private/qcombobox_p.h" -- cgit v0.12 From 6b79cbf5cadbaebef8be895a47ca314724afb1a1 Mon Sep 17 00:00:00 2001 From: Iain Date: Fri, 22 Jan 2010 18:18:06 +0100 Subject: Improve DEF file enable/disable mechanism on Symbian Provide configure flag to enable/disable the use of DEF files on Symbian. A useful side-effect was that it cleaned up how we control DEF files from qbase.pri and in WebKit. -nokia-developer still disables DEF files, as it triggers the autotest exports, which are not frozen into the DEF files. Disabling DEF files means that there is no BC with previously released versions of Qt, so this should only be used for development purposes. .pro files can specify custom locations for DEF files by setting defFilePath. Task-number: QTBUG-6556 Reviewed-by: Jason Barron --- configure.exe | Bin 1176064 -> 1178624 bytes mkspecs/features/symbian/def_files.prf | 26 +++++++++++++++++ mkspecs/features/symbian/def_files_disabled.prf | 7 +++++ src/qbase.pri | 36 ++++++++---------------- tools/configure/configureapp.cpp | 26 +++++++++++++++-- 5 files changed, 68 insertions(+), 27 deletions(-) create mode 100644 mkspecs/features/symbian/def_files.prf create mode 100644 mkspecs/features/symbian/def_files_disabled.prf diff --git a/configure.exe b/configure.exe index d88da13..03cbf37 100644 Binary files a/configure.exe and b/configure.exe differ diff --git a/mkspecs/features/symbian/def_files.prf b/mkspecs/features/symbian/def_files.prf new file mode 100644 index 0000000..c29d4ec --- /dev/null +++ b/mkspecs/features/symbian/def_files.prf @@ -0,0 +1,26 @@ +# With DEF files enabled, removed exported symbols are treated as errors +# and there is binary compatibility between successive builds. + +CONFIG -= def_files_disabled + +!isEmpty(defFilePath) { + defBlock = \ + "$${LITERAL_HASH}ifdef WINSCW" \ + "DEFFILE $$defFilePath/bwins/$${TARGET}.def" \ + "$${LITERAL_HASH}elif defined EABI" \ + "DEFFILE $$defFilePath/eabi/$${TARGET}.def" \ + "$${LITERAL_HASH}endif" + + MMP_RULES += defBlock +} else { + # If defFilePath is not defined, then put the folders containing the DEF files at the + # same level as the .pro (and generated MMP) file(s) + defBlock = \ + "$${LITERAL_HASH}ifdef WINSCW" \ + "DEFFILE ./bwins/$${TARGET}.def" \ + "$${LITERAL_HASH}elif defined EABI" \ + "DEFFILE ./eabi/$${TARGET}.def" \ + "$${LITERAL_HASH}endif" + + MMP_RULES += defBlock +} diff --git a/mkspecs/features/symbian/def_files_disabled.prf b/mkspecs/features/symbian/def_files_disabled.prf new file mode 100644 index 0000000..d5c9505 --- /dev/null +++ b/mkspecs/features/symbian/def_files_disabled.prf @@ -0,0 +1,7 @@ +# With DEF files disabled, binary compatibility is broken every time you build + +CONFIG -= def_files + +# with EXPORTUNFROZEN enabled, new exports are included in the dll without +# needing to run abld/sbs freeze +MMP_RULES += EXPORTUNFROZEN diff --git a/src/qbase.pri b/src/qbase.pri index d9832a1..3307ae3 100644 --- a/src/qbase.pri +++ b/src/qbase.pri @@ -94,31 +94,17 @@ symbian { DEFINES+=QT_MAKEDLL TARGET.CAPABILITY = All -Tcb - defBlock = \ - "$${LITERAL_HASH}ifdef WINSCW" \ - "DEFFILE ../s60installs/bwins/$${TARGET}.def" \ - "$${LITERAL_HASH}elif defined EABI" \ - "DEFFILE ../s60installs/eabi/$${TARGET}.def" \ - "$${LITERAL_HASH}endif" - - contains(QT_CONFIG, private_tests) { - #When building autotest configuration, there are extra exports from - #the Qt DLLs, which we don't want in the frozen DEF files. - MMP_RULES += EXPORTUNFROZEN - } else { - #When building without autotests, DEF files are used by default. - #This is to maintain binary compatibility with previous releases. - - #with defBlock enabled, removed exported symbols are treated as errors - #and there is binary compatibility between successive builds. - #with defBlock disabled, binary compatibility is broken every time you build - MMP_RULES += defBlock - - #with EXPORTUNFROZEN enabled, new exports are included in the dll without - #needing to run abld freeze, however binary compatibility is only maintained - #for symbols that are frozen (and only if defBlock is also enabled) - #the downside of EXPORTUNFROZEN is that the linker gets run twice - #MMP_RULES += EXPORTUNFROZEN + # When building without autotests, DEF files are used by default. + # This is to maintain binary compatibility with previous releases. + # To explicitly disable DEF files usage, eg. when lots of code churn is + # going on, and functions may be added and removed before shipping, + # configure with -no-usedeffiles + # WARNING - disabling DEF files *will* break BC with previous released versions + # of Qt, and the only compatibility will be between this build of Qt and anything + # built in this exact environment. *Never* use this when building a version + # for release. + contains(QT_CONFIG, def_files) { + defFilePath=../s60installs } } load(armcc_warnings) diff --git a/tools/configure/configureapp.cpp b/tools/configure/configureapp.cpp index 919e68f..3f105b5 100644 --- a/tools/configure/configureapp.cpp +++ b/tools/configure/configureapp.cpp @@ -317,6 +317,7 @@ Configure::Configure( int& argc, char** argv ) dictionary[ "OPENSSL" ] = "auto"; dictionary[ "DBUS" ] = "auto"; dictionary[ "S60" ] = "yes"; + dictionary[ "SYMBIAN_DEFFILES" ] = "yes"; dictionary[ "STYLE_WINDOWS" ] = "yes"; dictionary[ "STYLE_WINDOWSXP" ] = "auto"; @@ -482,6 +483,7 @@ void Configure::parseCmdLine() dictionary[ "BUILDNOKIA" ] = "yes"; dictionary[ "BUILDDEV" ] = "yes"; dictionary["LICENSE_CONFIRMED"] = "yes"; + dictionary[ "SYMBIAN_DEFFILES" ] = "no"; } else if( configCmdLine.at(i) == "-opensource" ) { dictionary[ "BUILDTYPE" ] = "opensource"; @@ -803,7 +805,7 @@ void Configure::parseCmdLine() else if( configCmdLine.at(i) == "-no-native-gestures" ) dictionary[ "NATIVE_GESTURES" ] = "no"; #if !defined(EVAL) - // Others --------------------------------------------------- + // Symbian Support ------------------------------------------- else if (configCmdLine.at(i) == "-fpu" ) { ++i; @@ -812,12 +814,17 @@ void Configure::parseCmdLine() dictionary[ "ARM_FPU_TYPE" ] = configCmdLine.at(i); } - // S60 Support ------------------------------------------- else if( configCmdLine.at(i) == "-s60" ) dictionary[ "S60" ] = "yes"; else if( configCmdLine.at(i) == "-no-s60" ) dictionary[ "S60" ] = "no"; + else if( configCmdLine.at(i) == "-usedeffiles" ) + dictionary[ "SYMBIAN_DEFFILES" ] = "yes"; + else if( configCmdLine.at(i) == "-no-usedeffiles" ) + dictionary[ "SYMBIAN_DEFFILES" ] = "no"; + + // Others --------------------------------------------------- else if (configCmdLine.at(i) == "-fast" ) dictionary[ "FAST" ] = "yes"; else if (configCmdLine.at(i) == "-no-fast" ) @@ -1823,6 +1830,8 @@ bool Configure::displayHelp() desc( "-fpu ", "VFP type on ARM, supported options: softvfp(default) | vfpv2 | softvfp+vfpv2"); desc("S60", "no", "-no-s60", "Do not compile in S60 support."); desc("S60", "yes", "-s60", "Compile with support for the S60 UI Framework\n"); + desc("SYMBIAN_DEFFILES", "no", "no-usedeffiles", "Disable the usage of DEF files."); + desc("SYMBIAN_DEFFILES", "yes", "usedeffiles", "Enable the usage of DEF files.\n"); return true; } return false; @@ -2744,6 +2753,11 @@ void Configure::generateCachefile() if ( dictionary["PLUGIN_MANIFESTS"] == "no" ) configStream << " no_plugin_manifest"; + if ( dictionary["SYMBIAN_DEFFILES"] == "yes" ) { + configStream << " def_files"; + } else if ( dictionary["SYMBIAN_DEFFILES"] == "no" ) { + configStream << " def_files_disabled"; + } configStream << endl; configStream << "QT_ARCH = " << dictionary[ "ARCHITECTURE" ] << endl; if (dictionary["QT_EDITION"].contains("OPENSOURCE")) @@ -3268,6 +3282,14 @@ void Configure::displayConfig() cout << "Support for S60............." << dictionary[ "S60" ] << endl; } + if (dictionary.contains("SYMBIAN_DEFFILES")) { + cout << "Symbian DEF files enabled..." << dictionary[ "SYMBIAN_DEFFILES" ] << endl; + if(dictionary["SYMBIAN_DEFFILES"] == "no") { + cout << "WARNING: Disabling DEF files will mean that Qt is NOT binary compatible with previous versions." << endl; + cout << " This feature is only intended for use during development, NEVER for release builds." << endl; + } + } + if(dictionary["ASSISTANT_WEBKIT"] == "yes") cout << "Using WebKit as html rendering engine in Qt Assistant." << endl; -- cgit v0.12