src/hbwidgets/itemviews/hbindexfeedback_p.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
equal deleted inserted replaced
-1:000000000000 0:16d8024aca5e
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (developer.feedback@nokia.com)
       
     6 **
       
     7 ** This file is part of the HbWidgets module of the UI Extensions for Mobile.
       
     8 **
       
     9 ** GNU Lesser General Public License Usage
       
    10 ** This file may be used under the terms of the GNU Lesser General Public
       
    11 ** License version 2.1 as published by the Free Software Foundation and
       
    12 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
       
    13 ** Please review the following information to ensure the GNU Lesser General
       
    14 ** Public License version 2.1 requirements will be met:
       
    15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    16 **
       
    17 ** In addition, as a special exception, Nokia gives you certain additional
       
    18 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    20 **
       
    21 ** If you have questions regarding the use of this file, please contact
       
    22 ** Nokia at developer.feedback@nokia.com.
       
    23 **
       
    24 ****************************************************************************/
       
    25 
       
    26 #include "hbindexfeedback.h"
       
    27 #include "hbindexfeedback_p.h"
       
    28 
       
    29 #include <hbscrollbar.h>
       
    30 #include <hbabstractitemview.h>
       
    31 #include <hbabstractviewitem.h>
       
    32 #include <hbstyle.h>
       
    33 #include <hbabstractitemcontainer.h>
       
    34 #include <hbapplication.h>
       
    35 
       
    36 #include <hbeffect.h>
       
    37 #include <hbeffectinternal_p.h>
       
    38 
       
    39 #include <hbstyleoptionindexfeedback.h>
       
    40 
       
    41 #include <QTimer>
       
    42 #include <QSize>
       
    43 #include <QGraphicsScene>
       
    44 
       
    45 // rather than having magic numbers
       
    46 // defaults and constants are defined here.
       
    47 namespace {
       
    48 /* 
       
    49     Press timeout is the duration after the scrollbar is 
       
    50     pressed to when the index feedback will be dismissed.
       
    51 
       
    52     In the case that the press timeout has not yet passed
       
    53     and the timer should be fired with the release timeout,
       
    54     the timer with the press timeout will continue running.
       
    55 */
       
    56 static const int DEFAULT_INDEX_FEEDBACK_PRESS_TIMEOUT = 1000;
       
    57 
       
    58 /*
       
    59     Dwell timeout is the duration after which if there is not a change
       
    60     in the scrollbar's value the index feedback will be dismissed.
       
    61 */
       
    62 static const int DEFAULT_INDEX_FEEDBACK_DWELL_TIMEOUT = 2000;
       
    63 
       
    64 /*
       
    65     Release timeout is the duration after which the user has lifted
       
    66     their finger from the scrollbar to when the index feedback will be dismissed.
       
    67 */
       
    68 static const int DEFAULT_INDEX_FEEDBACK_RELEASE_TIMEOUT = 250;
       
    69 
       
    70 /*
       
    71     The default value for the index feedback policy.
       
    72 */
       
    73 static const HbIndexFeedback::IndexFeedbackPolicy DEFAULT_INDEX_FEEDBACK_POLICY = HbIndexFeedback::IndexFeedbackSingleCharacter;
       
    74 
       
    75 /*
       
    76     The value of the index feedback's disappear animation
       
    77 */
       
    78 static const int INDEX_FEEDBACK_DISAPPEAR_DURATION = 300;
       
    79 }
       
    80 
       
    81 /*
       
    82     constructor
       
    83 */
       
    84 HbIndexFeedbackPrivate::HbIndexFeedbackPrivate() :
       
    85     mIndexFeedbackPressTimeout(DEFAULT_INDEX_FEEDBACK_PRESS_TIMEOUT),
       
    86     mIndexFeedbackDwellTimeout(DEFAULT_INDEX_FEEDBACK_DWELL_TIMEOUT),
       
    87     mIndexFeedbackReleaseTimeout(DEFAULT_INDEX_FEEDBACK_RELEASE_TIMEOUT),
       
    88     mIndexFeedbackPolicy(DEFAULT_INDEX_FEEDBACK_POLICY),
       
    89     mVerticalScrollBarValue(-1.0),
       
    90     mHorizontalScrollBarValue(-1.0),
       
    91     mIndexFeedbackTimer(0),
       
    92     mDisappearTimer(0),
       
    93     mTextItem(0),
       
    94     mPopupItem(0),
       
    95     mItemView(0),
       
    96     mOneCharHeight(0.0),
       
    97     mThreeCharHeight(0.0),
       
    98     mThreeCharWidth(0.0),
       
    99     mStringOffset(0.0)
       
   100 {
       
   101 
       
   102 }
       
   103 
       
   104 /*
       
   105     Destructor
       
   106 */
       
   107 HbIndexFeedbackPrivate::~HbIndexFeedbackPrivate()
       
   108 {
       
   109 
       
   110 }
       
   111 
       
   112 /*
       
   113     2ndary construction, called from public's constructor.
       
   114 */
       
   115 void HbIndexFeedbackPrivate::init()
       
   116 {
       
   117     Q_Q( HbIndexFeedback );
       
   118 
       
   119     //mItemView = 0; // double check that this is safe.
       
   120 
       
   121     HbEffect::add(HB_INDEXFEEDBACK_TYPE, "indexfeedback_appear", EFFECT_IFAPPEAR);
       
   122     if (!HbEffect::add(HB_INDEXFEEDBACK_TYPE, "indexfeedback_disappear", EFFECT_IFDISAPPEAR)) {
       
   123         mDisappearTimer = new QTimer(q);
       
   124         mDisappearTimer->setSingleShot(true);
       
   125         mDisappearTimer->setInterval(INDEX_FEEDBACK_DISAPPEAR_DURATION);
       
   126         q->connect(mDisappearTimer, SIGNAL(timeout()),
       
   127             q, SLOT(_q_hideIndexFeedbackNow()));
       
   128     }
       
   129 
       
   130     mIndexFeedbackTimer = new QTimer(q);
       
   131     mIndexFeedbackTimer->setSingleShot(true);
       
   132     q->connect(mIndexFeedbackTimer, SIGNAL(timeout()),
       
   133         q, SLOT(_q_hideIndexFeedback()));
       
   134 
       
   135     createPrimitives();
       
   136 }
       
   137 
       
   138 /*
       
   139     Update the data for, and show the index feedback (if it's not already shown)
       
   140 */
       
   141 void HbIndexFeedbackPrivate::showIndexFeedback()
       
   142 {
       
   143     if (!mItemView 
       
   144         || mIndexFeedbackPolicy == HbIndexFeedback::IndexFeedbackNone) {
       
   145         return;
       
   146     }
       
   147 
       
   148     QList <HbAbstractViewItem *> visibleItems = mItemView->visibleItems();
       
   149 
       
   150     QModelIndex targetIndex = visibleItems.first()->modelIndex();   
       
   151     qreal top = visibleItems.first()->mapToItem(mItemView, 0, 0).y();
       
   152     if (top < 0 
       
   153         && visibleItems.count() > 1) {
       
   154         targetIndex = visibleItems.at(1)->modelIndex();
       
   155     }
       
   156 
       
   157     if (targetIndex.isValid()) {
       
   158         QVariant data = targetIndex.data(Hb::IndexFeedbackRole);
       
   159         if (data.canConvert<QString>()) {
       
   160 
       
   161             QString testString = displayText(data);
       
   162             if (testString != mPopupContent) {
       
   163                 mPopupContent = testString;
       
   164                 updatePrimitives();
       
   165             }
       
   166 
       
   167             if (mTextItem->opacity() == 0.0) {
       
   168                 HbEffect::start(mPopupItemList, HB_INDEXFEEDBACK_TYPE, EFFECT_IFAPPEAR);
       
   169             }
       
   170             if (mTextItem) {
       
   171                 mTextItem->show();
       
   172             }
       
   173 
       
   174             if (mPopupItem) {
       
   175                 mPopupItem->show();
       
   176             }
       
   177 
       
   178             if (mDisappearTimer) {
       
   179                 mDisappearTimer->stop();
       
   180             }
       
   181         } else {
       
   182             _q_hideIndexFeedback();
       
   183         }
       
   184     }
       
   185 }
       
   186 
       
   187 /*
       
   188     Returns the qstring which will be the text in the index feedback.
       
   189 */
       
   190 QString HbIndexFeedbackPrivate::displayText(const QVariant &data) const
       
   191 {
       
   192     QString retVal = QString();
       
   193 
       
   194     switch (mIndexFeedbackPolicy) {
       
   195         case HbIndexFeedback::IndexFeedbackSingleCharacter:
       
   196             retVal = data.toString().left(1);
       
   197             break;
       
   198 
       
   199         case HbIndexFeedback::IndexFeedbackThreeCharacter:
       
   200             retVal = data.toString().left(3);
       
   201             break;
       
   202 
       
   203         case HbIndexFeedback::IndexFeedbackString:
       
   204             retVal = data.toString();
       
   205             break;
       
   206 
       
   207         default:
       
   208             qt_noop();
       
   209             break;
       
   210     }
       
   211 
       
   212     return retVal;
       
   213 }
       
   214 
       
   215 /*
       
   216     Handle the case of the scrollbar being pressed.
       
   217 
       
   218     This is show the index feedback and start the dismissal timer with the
       
   219     press timeout.
       
   220 */
       
   221 void HbIndexFeedbackPrivate::scrollBarPressed()
       
   222 {
       
   223     showIndexFeedback();
       
   224 
       
   225     // need to record the scrollbar values    
       
   226 
       
   227     // TODO::The values are storred here is a work around for a bug in hbscrollbar.
       
   228     // the bug is that the value changed signal is emitted when the thumb
       
   229     // is pressed, and again when it is released, regaurdless of if there was a value change.
       
   230     // once that bug is fixed, this should be removed.
       
   231     mVerticalScrollBarValue = mItemView->verticalScrollBar()->value();
       
   232     mHorizontalScrollBarValue = mItemView->horizontalScrollBar()->value();
       
   233 
       
   234     // and start the press timer.
       
   235     mIndexFeedbackTimer->setInterval(mIndexFeedbackPressTimeout);
       
   236     mIndexFeedbackTimer->start();
       
   237 }
       
   238 
       
   239 /*
       
   240     Handle the case of the scrollbar being released.
       
   241 
       
   242     This is to start the dismissal timer with the release timeout.
       
   243     ...but not if the timer is still running with the press timeout...
       
   244 */
       
   245 void HbIndexFeedbackPrivate::scrollBarReleased()
       
   246 {
       
   247     // record the scrollbar values in case position changed is emitted after us w/o a real change
       
   248     
       
   249     // TODO::The values are storred here is a work around for a bug in hbscrollbar.
       
   250     // the bug is that the value changed signal is emitted when the thumb
       
   251     // is pressed, and again when it is released, regaurdless of if there was a value change.
       
   252     // once that bug is fixed, this should be removed.
       
   253     mVerticalScrollBarValue = mItemView->verticalScrollBar()->value();
       
   254     mHorizontalScrollBarValue = mItemView->horizontalScrollBar()->value();
       
   255 
       
   256     // start this timer.
       
   257     if (!(mIndexFeedbackTimer->isActive()
       
   258          && mIndexFeedbackTimer->interval() == mIndexFeedbackPressTimeout)) {
       
   259         mIndexFeedbackTimer->setInterval(mIndexFeedbackReleaseTimeout);
       
   260         mIndexFeedbackTimer->start();
       
   261     }
       
   262 }
       
   263 
       
   264 /*
       
   265     Handle the case of the scrollbar being moved.
       
   266 
       
   267     This is to stop any existing timers (only if the scrollbar actually moved),
       
   268     and start a timer with the dwell timeout.
       
   269 
       
   270     NOTE:: this should be much simpler once the valueChanged signal from hbscrollbar
       
   271     is emitted at the correct times.
       
   272 */
       
   273 void HbIndexFeedbackPrivate::_q_scrollPositionChanged(qreal value, Qt::Orientation orientation )
       
   274 {
       
   275     // using 3 timers.  If the press timer is active, stop it, assuming the value actually changed.
       
   276 
       
   277     // TODO::The value check here is a work around for a bug in hbscrollbar.
       
   278     // the bug is that the value changed signal is emitted when the thumb
       
   279     // is pressed, and again when it is released, regaurdless of if there was a value change.
       
   280     // once that bug is fixed, This should be just setting the dwell interval,
       
   281     // starting the timer, and showing the index feedback.
       
   282     if (value != (orientation == 
       
   283             Qt::Vertical ? mVerticalScrollBarValue : mHorizontalScrollBarValue) ) {
       
   284         mIndexFeedbackTimer->setInterval(mIndexFeedbackDwellTimeout);
       
   285         mIndexFeedbackTimer->start();
       
   286         showIndexFeedback();
       
   287     }
       
   288 }
       
   289 
       
   290 /*
       
   291     The private slot for hiding the index feedback.
       
   292 
       
   293     If effects are active, use the disappear effect to hide the index feedback's
       
   294     primitives.  Otherwise simply hide them.
       
   295 */
       
   296 void HbIndexFeedbackPrivate::_q_hideIndexFeedback()
       
   297 {
       
   298     if (qFuzzyCompare(mTextItem->opacity(), (qreal) 1.0)) {
       
   299         HbEffect::start(mPopupItemList, HB_INDEXFEEDBACK_TYPE, EFFECT_IFDISAPPEAR);
       
   300     }
       
   301 
       
   302     if (mDisappearTimer) {
       
   303         mDisappearTimer->start();
       
   304     }
       
   305 }
       
   306 
       
   307 /*
       
   308     A private slot for actually calling hide on the index feedback.
       
   309 
       
   310     This should happen after the index feedback's hide animation is completed
       
   311     regardless of if it went successfully.
       
   312 */
       
   313 void HbIndexFeedbackPrivate::_q_hideIndexFeedbackNow()
       
   314 {
       
   315     if (mTextItem) {
       
   316         mTextItem->hide();
       
   317     }
       
   318 
       
   319     if (mPopupItem) {
       
   320         mPopupItem->hide();
       
   321     }
       
   322 }
       
   323 
       
   324 /*
       
   325     Do the appropriate thing if the item view we're pointing at is destroyed
       
   326 */
       
   327 void HbIndexFeedbackPrivate::_q_itemViewDestroyed()
       
   328 {
       
   329     mItemView = 0;
       
   330 }
       
   331 
       
   332 /*
       
   333     Update the primitives for the index feedback.
       
   334 */
       
   335 void HbIndexFeedbackPrivate::updatePrimitives()
       
   336 {
       
   337     Q_Q( HbIndexFeedback );
       
   338 
       
   339     HbStyleOptionIndexFeedback option;
       
   340     q->initStyleOption(&option);
       
   341     if (mTextItem) {
       
   342         q->style()->updatePrimitive(mTextItem, HbStyle::P_IndexFeedback_popup_text, &option);
       
   343     }
       
   344     if (mPopupItem) {
       
   345         q->style()->updatePrimitive(mPopupItem, HbStyle::P_IndexFeedback_popup_background, &option);
       
   346     }
       
   347 }
       
   348 
       
   349 /*
       
   350     Create the primitives for the index feedback.
       
   351 */
       
   352 void HbIndexFeedbackPrivate::createPrimitives()
       
   353 {
       
   354     Q_Q( HbIndexFeedback );
       
   355 
       
   356     mPopupItemList.clear();
       
   357 
       
   358     if (!mTextItem) {
       
   359         mTextItem = q->style()->createPrimitive(HbStyle::P_IndexFeedback_popup_text, q);
       
   360         mTextItem->hide();
       
   361         mPopupItemList.append(mTextItem);
       
   362     }
       
   363 
       
   364     if (!mPopupItem) {
       
   365         mPopupItem = q->style()->createPrimitive(HbStyle::P_IndexFeedback_popup_background, q);
       
   366         mPopupItem->hide();
       
   367         mPopupItemList.append(mPopupItem);
       
   368     }
       
   369 }
       
   370 
       
   371 /*
       
   372     disconnects the current item view from hbindexfeedback's slots & event filters.
       
   373 */
       
   374 void HbIndexFeedbackPrivate::disconnectItemView()
       
   375 {
       
   376     Q_Q( HbIndexFeedback );
       
   377 
       
   378     if (mItemView) {
       
   379         // disconnect from the existing itemView's scrollbars
       
   380         mItemView->disconnect(q);
       
   381         // uninstall the event filters;
       
   382         mItemView->horizontalScrollBar()->removeEventFilter(q);
       
   383         mItemView->verticalScrollBar()->removeEventFilter(q);
       
   384 
       
   385         mItemView->removeSceneEventFilter(q);
       
   386         if (mItemView->scene()) {
       
   387             mItemView->scene()->removeItem(q);
       
   388         }
       
   389     }
       
   390 
       
   391     mItemView = 0;
       
   392 }
       
   393 
       
   394 /*
       
   395     Hooks up the private slots & event filter to a scrollbar.
       
   396 */
       
   397 void HbIndexFeedbackPrivate::connectScrollBarToIndexFeedback(HbScrollBar* scrollBar)
       
   398 {
       
   399     Q_Q( HbIndexFeedback );
       
   400 
       
   401     if (scrollBar) {
       
   402         q->connect(scrollBar, SIGNAL(valueChanged(qreal, Qt::Orientation)),
       
   403             q, SLOT(_q_scrollPositionChanged(qreal, Qt::Orientation)));
       
   404         scrollBar->installEventFilter(q);
       
   405     }
       
   406 }
       
   407 
       
   408 /*
       
   409     Calculates the rects for the popup text and background.
       
   410 
       
   411     Does nothing if the rect is the same as the last time it was called.
       
   412 */
       
   413 void HbIndexFeedbackPrivate::calculatePopupRects()
       
   414 {
       
   415     Q_Q( HbIndexFeedback );
       
   416 
       
   417     if (!mItemView) {
       
   418         return;
       
   419     }
       
   420 
       
   421     QRectF contentRect = mItemView->rect();
       
   422     
       
   423     HbScrollBar *verticalScrollBar = mItemView->verticalScrollBar();
       
   424     HbScrollBar *horizontalScrollBar = mItemView->horizontalScrollBar();
       
   425     if (verticalScrollBar->isInteractive()) {
       
   426         contentRect.setWidth( contentRect.width() - verticalScrollBar->rect().width() );
       
   427         if (HbApplication::layoutDirection() == Qt::RightToLeft) {
       
   428             contentRect.setLeft( contentRect.left() + verticalScrollBar->rect().width() );
       
   429         }
       
   430     }
       
   431     
       
   432     if (horizontalScrollBar->isInteractive()) {
       
   433         contentRect.setHeight( contentRect.height() - horizontalScrollBar->rect().height() );
       
   434     }
       
   435 
       
   436     if (contentRect == mItemViewContentsRect) {
       
   437         return;
       
   438     }
       
   439 
       
   440     qreal margin = 0;
       
   441     q->style()->parameter(QLatin1String("hb-param-margin-gene-popup"), margin);
       
   442 
       
   443     QSizeF backgroundSize;
       
   444     QSizeF textSize;
       
   445 
       
   446     switch (mIndexFeedbackPolicy) {
       
   447         case HbIndexFeedback::IndexFeedbackSingleCharacter:
       
   448         case HbIndexFeedback::IndexFeedbackThreeCharacter:
       
   449             {
       
   450                 textSize.setHeight( textHeight() );
       
   451                 textSize.setWidth ( textWidth() );
       
   452 
       
   453                 backgroundSize.setHeight( textSize.height() + 2 * margin );
       
   454                 backgroundSize.setWidth ( textSize.width() + 2 * margin );
       
   455 
       
   456                 mPopupBackgroundRect.setLeft( contentRect.left() + (contentRect.width() -
       
   457                     backgroundSize.width()) / 2.0 );
       
   458                 mPopupBackgroundRect.setTop ( contentRect.top() + (contentRect.height() - 
       
   459                     backgroundSize.height()) / 2.0 );
       
   460 
       
   461                 mPopupTextRect.setLeft( mPopupBackgroundRect.left() + margin );
       
   462                 mPopupTextRect.setTop ( mPopupBackgroundRect.top()  + margin );
       
   463 
       
   464                 mPopupBackgroundRect.setSize(backgroundSize);
       
   465                 mPopupTextRect.setSize(textSize);
       
   466             }
       
   467             break;
       
   468 
       
   469         case HbIndexFeedback::IndexFeedbackString:
       
   470             {
       
   471                 textSize.setHeight( textHeight() );
       
   472                 backgroundSize.setHeight( textSize.height() + 2 * margin );
       
   473 
       
   474                 backgroundSize.setWidth( contentRect.width() - 2 * mStringOffset );
       
   475                 textSize.setWidth( backgroundSize.width() - 2 * margin );
       
   476 
       
   477                 mPopupBackgroundRect.setLeft( contentRect.left() + mStringOffset );
       
   478                 mPopupBackgroundRect.setTop( contentRect.top() + (contentRect.height() - 
       
   479                     backgroundSize.height()) / 2.0 );
       
   480 
       
   481                 mPopupTextRect.setLeft( mPopupBackgroundRect.left() + margin );
       
   482                 mPopupTextRect.setTop ( mPopupBackgroundRect.top() + margin );
       
   483 
       
   484                 mPopupBackgroundRect.setSize(backgroundSize);
       
   485                 mPopupTextRect.setSize(textSize);
       
   486             }
       
   487             break;
       
   488 
       
   489         case HbIndexFeedback::IndexFeedbackNone:
       
   490             {
       
   491                 mPopupTextRect = QRectF();
       
   492                 mPopupBackgroundRect = QRectF();
       
   493             }
       
   494             break;
       
   495     }
       
   496 }
       
   497 
       
   498 /*
       
   499     Returns the text height in pixels.
       
   500 */
       
   501 qreal HbIndexFeedbackPrivate::textHeight() const
       
   502 {
       
   503     Q_Q( const HbIndexFeedback );
       
   504 
       
   505     qreal retVal;
       
   506 
       
   507     switch (mIndexFeedbackPolicy) {
       
   508         case HbIndexFeedback::IndexFeedbackNone:
       
   509             retVal = 0.0;
       
   510             break;
       
   511 
       
   512         case HbIndexFeedback::IndexFeedbackSingleCharacter:
       
   513             retVal = mOneCharHeight;
       
   514             break;
       
   515 
       
   516         case HbIndexFeedback::IndexFeedbackThreeCharacter:
       
   517             retVal = mThreeCharHeight;
       
   518             break;
       
   519 
       
   520         case HbIndexFeedback::IndexFeedbackString:
       
   521             q->style()->parameter(QLatin1String("hb-param-text-height-primary"), retVal);
       
   522             break;
       
   523     }
       
   524 
       
   525     return retVal;
       
   526 }
       
   527 
       
   528 /*
       
   529     Returns the text width in units.
       
   530 */
       
   531 qreal HbIndexFeedbackPrivate::textWidth() const
       
   532 {
       
   533     qreal retVal = 0.0;
       
   534 
       
   535     switch (mIndexFeedbackPolicy) {
       
   536         case HbIndexFeedback::IndexFeedbackString:
       
   537         case HbIndexFeedback::IndexFeedbackNone:
       
   538             retVal = 0.0;
       
   539             break;
       
   540 
       
   541         case HbIndexFeedback::IndexFeedbackSingleCharacter:
       
   542             retVal = mOneCharHeight;
       
   543             break;
       
   544 
       
   545         case HbIndexFeedback::IndexFeedbackThreeCharacter:
       
   546             retVal = mThreeCharWidth;
       
   547             break;
       
   548     }
       
   549 
       
   550     return retVal;
       
   551 }