Home · All Classes · Main Classes · Deprecated

mbuttonview.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002 **
00003 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
00004 ** All rights reserved.
00005 ** Contact: Nokia Corporation (directui@nokia.com)
00006 **
00007 ** This file is part of libmeegotouch.
00008 **
00009 ** If you have questions regarding the use of this file, please contact
00010 ** Nokia at directui@nokia.com.
00011 **
00012 ** This library is free software; you can redistribute it and/or
00013 ** modify it under the terms of the GNU Lesser General Public
00014 ** License version 2.1 as published by the Free Software Foundation
00015 ** and appearing in the file LICENSE.LGPL included in the packaging
00016 ** of this file.
00017 **
00018 ****************************************************************************/
00019 
00020 #include "mbuttonview.h"
00021 #include "mbuttonview_p.h"
00022 
00023 #include <QPainter>
00024 #include <QGraphicsSceneMouseEvent>
00025 #include <QFontMetricsF>
00026 #include <QPixmap>
00027 #include <QIcon>
00028 #include <QTimer>
00029 
00030 #include "mbutton.h"
00031 #include "mfeedback.h"
00032 #include "mtheme.h"
00033 #include "mscalableimage.h"
00034 #include "mviewcreator.h"
00035 #include "mscenemanager.h"
00036 #include "mlabel.h"
00037 #include "mdebug.h"
00038 #include "mtimestamp.h"
00039 #include "mviewconstants.h"
00040 
00041 MButtonViewPrivate::MButtonViewPrivate()
00042     : icon(0), toggledIcon(0), label(NULL), styleModeChangeTimer(NULL),
00043     iconOrigin(IconOriginUndefined), toggledIconOrigin(IconOriginUndefined),
00044     queuedStyleModeChange(false)
00045 {
00046 }
00047 
00048 MButtonViewPrivate::Icon::Icon()
00049 {
00050     pixmap = 0;
00051     origin = IconOriginUndefined;
00052 }
00053 
00054 MButtonViewPrivate::Icon::~Icon()
00055 {
00056     switch(origin) {
00057         case IconOriginFromModelIconId:
00058         case IconOriginFromStyleIconId:
00059             MTheme::releasePixmap(pixmap);
00060             break;
00061 
00062         case IconOriginFromModelQIcon:
00063             delete pixmap;
00064             break;
00065 
00066         case IconOriginUndefined:
00067         default:
00068             break;
00069     }
00070 
00071     pixmap = 0;
00072     origin = IconOriginUndefined;
00073     id.clear();
00074     theme.clear();
00075 }
00076 
00077 int MButtonViewPrivate::pressTimeout() const
00078 {
00079     Q_Q(const MButtonView);
00080 
00081     return q->style()->pressTimeout();
00082 }
00083 
00084 void MButtonViewPrivate::freeIcons()
00085 {
00086     if (icon) {
00087         delete icon;
00088         icon = 0;
00089     }
00090 
00091     if (toggledIcon) {
00092         delete toggledIcon;
00093         toggledIcon = 0;
00094     }
00095 }
00096 
00097 MButtonViewPrivate::~MButtonViewPrivate()
00098 {
00099     freeIcons();
00100 }
00101 
00102 // As the condition of text color and background change for button
00103 bool MButtonViewPrivate::toggleState() const
00104 {
00105     Q_Q(const MButtonView);
00106     if (q->model()->checkable()) {
00107         bool state = (!q->model()->checked() && q->model()->down())
00108                      || (q->model()->checked() && !q->model()->down());
00109         return state;
00110     } else
00111         return q->model()->down();
00112 }
00113 
00114 void MButtonViewPrivate::_q_applyQueuedStyleModeChange()
00115 {
00116     Q_Q(MButtonView);
00117 
00118     if (queuedStyleModeChange) {
00119         queuedStyleModeChange = false;
00120         q->applyStyle();
00121     }
00122 }
00123 
00124 void MButtonViewPrivate::refreshStyleMode()
00125 {
00126     Q_Q(MButtonView);
00127 
00128     if (controller->isEnabled()) {
00129         if (q->model()->down()) {
00130             if (styleModeChangeTimer->isActive()) {
00131                 styleModeChangeTimer->start(pressTimeout());
00132                 return;
00133             }
00134             styleModeChangeTimer->start(pressTimeout());
00135             q->style().setModePressed();
00136         } else if (q->model()->checked()) {
00137             q->style().setModeSelected();
00138         } else {
00139             if (styleModeChangeTimer->isActive()) {
00140                 queuedStyleModeChange = true;
00141                 return;
00142             }
00143             q->style().setModeDefault();
00144         }
00145     } else {
00146         styleModeChangeTimer->stop();
00147         q->style().setModeDisabled();
00148     }
00149 
00150     label->setAlignment(q->style()->horizontalTextAlign() | q->style()->verticalTextAlign());
00151     label->setFont(q->style()->font());
00152     label->setColor(q->style()->textColor());
00153 
00154     updateIcon();
00155     updateToggledIcon();
00156 
00157     calcIconTextRects();
00158 }
00159 
00160 QSizeF MButtonViewPrivate::maxTextSize() const
00161 {
00162     Q_Q(const MButtonView);
00163 
00164     const QFontMetrics fm(q->style()->font());
00165 
00166     QString text = q->model()->text();
00167     const QChar multiLengthSeparator(0x9c, 0);
00168     const int index = text.indexOf(multiLengthSeparator);
00169     if (index >= 0) {
00170         text = text.left(index);
00171     }
00172 
00173     return QSizeF(fm.width(text), fm.height());
00174 }
00175 
00176 void MButtonViewPrivate::calcIconTextRects()
00177 {
00178     Q_Q(const MButtonView);
00179 
00180     //total horizontal and vertical text margins
00181     int hTextMargin = q->style()->textMarginLeft() + q->style()->textMarginRight();
00182     int vTextMargin = q->style()->textMarginTop() + q->style()->textMarginBottom();
00183 
00184     //total horizontal and vertical padding
00185     int hPadding = q->style()->paddingLeft() + q->style()->paddingRight();
00186     int vPadding = q->style()->paddingTop() + q->style()->paddingBottom();
00187 
00188     //area for the content (icon and text)
00189     QRectF contentRect(q->style()->paddingLeft(), q->style()->paddingTop(),
00190                       q->size().width() - hPadding,
00191                       q->size().height() - vPadding);
00192 
00193     //text rect when there is no icon
00194     QRectF textRect(contentRect.left() + q->style()->textMarginLeft(),
00195                     contentRect.top() + q->style()->textMarginTop(),
00196                     contentRect.width() - hTextMargin,
00197                     contentRect.height() - vTextMargin);
00198 
00199     QSizeF textSize = maxTextSize();
00200 
00201     //icon visible and valid?
00202     if (q->model()->iconVisible() && (icon || toggledIcon)) {
00203 
00204         int iconWidth = q->style()->iconSize().width();
00205         int iconHeight = q->style()->iconSize().height();
00206 
00207         QSizeF iconSize(iconWidth, iconHeight);
00208         QPointF iconPosition(0, 0);
00209 
00210         //text visible and valid?
00211         if (q->model()->textVisible() && !q->model()->text().isEmpty()) {
00212             textSize.setWidth(qMin(textSize.width(), contentRect.width() - iconWidth - hTextMargin));
00213             switch (q->style()->iconAlign()) {
00214                 //icon on left and text on right
00215             case Qt::AlignLeft: {
00216                 if (q->style()->horizontalTextAlign() == Qt::AlignHCenter)
00217                     iconPosition.setX(contentRect.center().x() - iconWidth - (textSize.width() / 2));
00218                 else if (q->style()->horizontalTextAlign() == Qt::AlignRight)
00219                     iconPosition.setX(contentRect.right() - textSize.width() - iconWidth - hTextMargin);
00220                 else if (q->style()->horizontalTextAlign() == Qt::AlignLeft)
00221                     iconPosition.setX(contentRect.left());
00222 
00223                 iconPosition.setY(contentRect.center().y() - (iconHeight / 2));
00224                 textRect.setX(contentRect.left() + q->style()->textMarginLeft() + iconWidth);
00225                 textRect.setWidth(contentRect.width() - iconWidth - hTextMargin);
00226                 break;
00227             }
00228 
00229             //icon on right and text on left
00230             case Qt::AlignRight: {
00231                 if (q->style()->horizontalTextAlign() == Qt::AlignHCenter)
00232                     iconPosition.setX(contentRect.center().x() + (textSize.width() / 2));
00233                 else if (q->style()->horizontalTextAlign() == Qt::AlignRight)
00234                     iconPosition.setX(contentRect.right() - iconWidth);
00235                 else if (q->style()->horizontalTextAlign() == Qt::AlignLeft)
00236                     iconPosition.setX(contentRect.left() + textSize.width() + hTextMargin);
00237 
00238                 iconPosition.setY(contentRect.center().y() - (iconHeight / 2));
00239                 textRect.setWidth(contentRect.width() - iconWidth - hTextMargin);
00240                 break;
00241             }
00242 
00243             //icon on bottom and text on top
00244             case Qt::AlignBottom: {
00245                 iconPosition = QPointF(contentRect.center().x() - (iconWidth / 2), contentRect.bottom() - iconHeight);
00246                 textRect.setHeight(contentRect.height() - iconHeight - vTextMargin);
00247                 break;
00248             }
00249 
00250             //icon on top and text on bottom
00251             default: {
00252                 iconPosition = QPointF(contentRect.center().x() - (iconWidth / 2), contentRect.top());
00253                 textRect.setY(iconRect.bottom() + q->style()->textMarginTop());
00254                 textRect.setHeight(contentRect.height() - iconHeight - vTextMargin);
00255                 break;
00256             }
00257             }
00258         }
00259         // no text
00260         else {
00261             //icon on center
00262             iconPosition = QPointF(contentRect.center().x() - (iconWidth / 2), contentRect.center().y() - (iconHeight / 2));
00263         }
00264 
00265         iconRect = QRectF(iconPosition, iconSize);
00266     }
00267 
00268     //adjust label with button margins
00269     if (controller->layoutDirection() == Qt::LeftToRight)
00270         label->setGeometry(textRect.translated(q->marginLeft(), q->marginTop()));
00271     else
00272         label->setGeometry(textRect.translated(q->marginRight(), q->marginTop()));
00273 }
00274 
00275 bool MButtonViewPrivate::isCurrentIconObsolete(const QString &newIconId) const
00276 {
00277     Q_Q(const MButtonView);
00278     bool result = false;
00279 
00280     if (icon) {
00281         result = icon->id != newIconId // it's a different icon
00282                  || icon->theme != MTheme::currentTheme() // current icon is from a previous theme.
00283                                                           // In the new theme the id might point
00284                                                           // to a different graphical asset.
00285                  || icon->pixmap->size() != q->style()->iconSize(); // icon has changed its size
00286     } else {
00287         // We don't even have an icon to compare with.
00288         result = true;
00289     }
00290 
00291     return result;
00292 }
00293 
00294 void MButtonViewPrivate::updateIcon(Icon **iconPtr,
00295         QIcon::Mode mode, const QString &iconIdFromModel,
00296         const QString &iconIdFromStyle)
00297 {
00298     Q_Q(MButtonView);
00299     Icon *icon = *iconPtr;
00300 
00301     /*
00302      Fallback order for chosing where to take the icon from:
00303      1 - MButtonModel::icon(), a QIcon
00304      2 - MButtonModel::[toggled]iconID(), a QString
00305      3 - MButtonStyle::[toggled]iconId(),  a QString
00306      4 - No icon at all
00307      */
00308 
00309     /*
00310         We only reload an icon if it has really changed.
00311      */
00312 
00313     if (!q->model()->icon().isNull()) {
00314 
00315         if (icon)
00316             delete icon;
00317 
00318         icon = new Icon;
00319         *iconPtr = icon;
00320 
00321         icon->pixmap = new QPixmap(q->model()->icon().pixmap(q->style()->iconSize(), mode));
00322         icon->origin = IconOriginFromModelQIcon;
00323 
00324     } else if (!iconIdFromModel.isEmpty() && isCurrentIconObsolete(iconIdFromModel)) {
00325 
00326         if (icon)
00327             delete icon;
00328 
00329         icon = new Icon;
00330         *iconPtr = icon;
00331 
00332         icon->pixmap = MTheme::pixmap(iconIdFromModel, q->style()->iconSize());
00333         icon->origin = IconOriginFromModelIconId;
00334         icon->id = iconIdFromModel;
00335         icon->theme = MTheme::currentTheme();
00336 
00337     } else if (!iconIdFromStyle.isEmpty() && isCurrentIconObsolete(iconIdFromStyle)) {
00338 
00339         if (icon)
00340             delete icon;
00341 
00342         icon = new Icon;
00343         *iconPtr = icon;
00344 
00345         icon->pixmap = MTheme::pixmap(iconIdFromStyle, q->style()->iconSize());
00346         icon->origin = IconOriginFromStyleIconId;
00347         icon->id = iconIdFromStyle;
00348         icon->theme = MTheme::currentTheme();
00349 
00350     } else if (iconIdFromModel.isEmpty() && iconIdFromStyle.isEmpty()) {
00351         delete icon;
00352         icon = NULL;
00353         *iconPtr = icon;
00354     }
00355 }
00356 
00357 void MButtonViewPrivate::updateIcon()
00358 {
00359     Q_Q(MButtonView);
00360     updateIcon(&icon, QIcon::Normal, q->model()->iconID(), q->style()->iconId());
00361 }
00362 
00363 void MButtonViewPrivate::updateToggledIcon()
00364 {
00365     Q_Q(MButtonView);
00366     updateIcon(&toggledIcon, QIcon::Selected, q->model()->toggledIconID(), q->style()->toggledIconId());
00367 }
00368 
00369 MButtonView::MButtonView(MButton *controller) :
00370     MWidgetView(* new MButtonViewPrivate, controller)
00371 {
00372     Q_D(MButtonView);
00373     d->label = new MLabel(controller);
00374     d->label->setParentItem(controller);
00375     d->label->setTextElide(true);
00376     d->label->setObjectName("ButtonLabel");
00377     d->label->setMinimumSize(0,0);
00378 
00379     d->styleModeChangeTimer = new QTimer(this);
00380     d->styleModeChangeTimer->setSingleShot(true);
00381     connect(d->styleModeChangeTimer, SIGNAL(timeout()), SLOT(_q_applyQueuedStyleModeChange()));
00382 }
00383 
00384 MButtonView::MButtonView(MButtonViewPrivate &dd, MButton *controller) :
00385     MWidgetView(dd, controller)
00386 {
00387     Q_D(MButtonView);
00388     d->label = new MLabel(controller);
00389     d->label->setParentItem(controller);
00390     d->label->setTextElide(true);
00391     d->label->setObjectName("ButtonLabel");
00392     d->label->setMinimumSize(0,0);
00393 
00394     d->styleModeChangeTimer = new QTimer(this);
00395     d->styleModeChangeTimer->setSingleShot(true);
00396     connect(d->styleModeChangeTimer, SIGNAL(timeout()), SLOT(_q_applyQueuedStyleModeChange()));
00397 }
00398 
00399 MButtonView::~MButtonView()
00400 {
00401     Q_D(MButtonView);
00402     if (d->label) {
00403         delete d->label;
00404         d->label = 0;
00405     }
00406 }
00407 
00408 void MButtonView::resizeEvent(QGraphicsSceneResizeEvent *event)
00409 {
00410     Q_D(MButtonView);
00411 
00412     MWidgetView::resizeEvent(event);
00413 
00414     d->calcIconTextRects();
00415 }
00416 
00417 void MButtonView::drawContents(QPainter *painter, const QStyleOptionGraphicsItem *option) const
00418 {
00419     Q_UNUSED(option);
00420 
00421     Q_D(const MButtonView);
00422     mTimestamp("MButtonView", QString("start text=%1").arg(model()->text()));
00423     drawIcon(painter, d->iconRect);
00424     mTimestamp("MButtonView", QString("end text=%1").arg(model()->text()));
00425 }
00426 
00427 void MButtonView::drawIcon(QPainter *painter, const QRectF &iconRect) const
00428 {
00429     if (model()->iconVisible()) {
00430         Q_D(const MButtonView);
00431 
00432         bool toggleState = d->toggleState();
00433 
00434         const QPixmap *pixmap = NULL;
00435         if (toggleState && d->toggledIcon)
00436             pixmap = d->toggledIcon->pixmap;
00437         else if (d->icon)
00438             pixmap = d->icon->pixmap;
00439 
00440         if (pixmap)
00441             painter->drawPixmap(iconRect, *pixmap, QRectF(pixmap->rect()));
00442     }
00443 }
00444 
00445 /*MLabel* MButtonView::label()
00446 {
00447     Q_D(const MButtonView);
00448     return d->label;
00449 }*/
00450 
00451 void MButtonView::applyStyle()
00452 {
00453     Q_D(MButtonView);
00454 
00455     MWidgetView::applyStyle();
00456 
00457     d->refreshStyleMode();
00458 
00459     update();
00460 }
00461 
00462 void MButtonView::mousePressEvent(QGraphicsSceneMouseEvent *event)
00463 {
00464     Q_UNUSED(event);
00465 
00466     if (model()->down()) {
00467         return;
00468     }
00469     model()->setDown(true);
00470 
00471     style()->pressFeedback().play();
00472 }
00473 
00474 void MButtonView::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
00475 {
00476     Q_D(MButtonView);
00477 
00478     QPointF touch = event->scenePos();
00479     QRectF rect = d->controller->sceneBoundingRect();
00480     rect.adjust(-M_RELEASE_MISS_DELTA, -M_RELEASE_MISS_DELTA,
00481                 M_RELEASE_MISS_DELTA, M_RELEASE_MISS_DELTA);
00482 
00483     if (rect.contains(touch)) {
00484         if (!model()->down()) {
00485             model()->setDown(true);
00486             style()->pressFeedback().play();
00487         }
00488     } else {
00489         if (model()->down()) {
00490             model()->setDown(false);
00491             style()->cancelFeedback().play();
00492         }
00493     }
00494 
00495 }
00496 
00497 void MButtonView::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
00498 {
00499     Q_D(MButtonView);
00500 
00501     if (!model()->down()) {
00502         return;
00503     }
00504     model()->setDown(false);
00505 
00506     style()->releaseFeedback().play();
00507 
00508     QPointF touch = event->scenePos();
00509     QRectF rect = d->controller->sceneBoundingRect();
00510     rect.adjust(-M_RELEASE_MISS_DELTA, -M_RELEASE_MISS_DELTA,
00511                 M_RELEASE_MISS_DELTA, M_RELEASE_MISS_DELTA);
00512     if (rect.contains(touch))
00513         model()->click();
00514 }
00515 
00516 void MButtonView::cancelEvent(MCancelEvent *event)
00517 {
00518     Q_UNUSED(event);
00519 
00520     if (!model()->down()) {
00521         return;
00522     }
00523 
00524     style()->cancelFeedback().play();
00525 
00526     model()->setDown(false);
00527 }
00528 
00529 void MButtonView::updateData(const QList<const char *>& modifications)
00530 {
00531     Q_D(MButtonView);
00532 
00533     MWidgetView::updateData(modifications);
00534     const char *member;
00535 
00536     bool mustCalcIconTextRects = false;
00537     bool mustUpdateIcon = false;
00538     bool mustUpdateToggledIcon = false;
00539 
00540     foreach(member, modifications) {
00541         if (member == MButtonModel::Text) {
00542             d->label->setText(model()->text());
00543             mustCalcIconTextRects = true;
00544         } else if (member == MButtonModel::TextVisible) {
00545             d->label->setVisible(model()->textVisible());
00546             mustCalcIconTextRects = true;
00547         } else if (member == MButtonModel::IconID) {
00548             mustUpdateIcon = true;
00549             mustCalcIconTextRects = true;
00550         } else if (member == MButtonModel::ToggledIconID) {
00551             mustUpdateToggledIcon = true;
00552             mustCalcIconTextRects = true;
00553         } else if (member == MButtonModel::Icon) {
00554             mustUpdateIcon = true;
00555             mustUpdateToggledIcon = true;
00556             mustCalcIconTextRects = true;
00557         } else if (member == MButtonModel::IconVisible) {
00558             mustCalcIconTextRects = true;
00559         } else if (member == MButtonModel::Down || member == MButtonModel::Checked ||
00560                    member == MButtonModel::Checkable) {
00561             d->refreshStyleMode();
00562         }
00563     }
00564 
00565     if (mustUpdateIcon) {
00566         d->updateIcon();
00567     }
00568 
00569     if (mustUpdateToggledIcon) {
00570         d->updateToggledIcon();
00571     }
00572 
00573     if (mustCalcIconTextRects) {
00574         d->calcIconTextRects();
00575     }
00576 
00577     update();
00578 }
00579 
00580 void MButtonView::setupModel()
00581 {
00582     Q_D(MButtonView);
00583 
00584     MWidgetView::setupModel();
00585     QList<const char *> members;
00586     if (model()->icon().isNull())
00587         members << MButtonModel::IconID;
00588     else
00589         members << MButtonModel::Icon;
00590     if (!model()->toggledIconID().isEmpty())
00591         members << MButtonModel::ToggledIconID;
00592 
00593     updateData(members);
00594 
00595     d->label->setText(model()->text());
00596     d->label->setVisible(model()->textVisible());
00597 
00598     d->calcIconTextRects();
00599 
00600     update();
00601 }
00602 
00603 QSizeF MButtonView::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
00604 {
00605     Q_D(const MButtonView);
00606 
00607     if (which == Qt::MinimumSize || which == Qt::MaximumSize)
00608         return MWidgetView::sizeHint(which, constraint);
00609 
00610     QSizeF iconSize(0, 0);
00611     if (model()->iconVisible() && d->icon)
00612         iconSize = d->icon->pixmap->size();
00613 
00614     QSizeF textSize(0, 0);
00615     if (model()->textVisible() && !model()->text().isEmpty()) {
00616         textSize = d->maxTextSize();
00617         textSize += QSizeF(style()->textMarginLeft() + style()->textMarginRight(), style()->textMarginTop() + style()->textMarginBottom());
00618     }
00619 
00620     qreal width = 0, height = 0;
00621     if (style()->iconAlign() == Qt::AlignTop || style()->iconAlign() == Qt::AlignBottom) {
00622         width  = qMax(iconSize.width(), textSize.width());
00623         height = iconSize.height() + textSize.height();
00624     } else {
00625         width  = iconSize.width() + textSize.width();
00626         height = qMax(iconSize.height(), textSize.height());
00627     }
00628 
00629     return QSizeF(width + style()->paddingLeft() + style()->paddingRight(), height + style()->paddingTop() + style()->paddingBottom());
00630 }
00631 
00632 M_REGISTER_VIEW_NEW(MButtonView, MButton)
00633 
00634 #include "moc_mbuttonview.cpp"

Copyright © 2010 Nokia Corporation Generated on Thu Nov 4 2010 18:14:20 (PDT)
Doxygen 1.7.1
MeeGo Touch