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