calendarui/views/dayview/src/calendaycontentscrollarea.cpp
branchRCL_3
changeset 65 12af337248b1
equal deleted inserted replaced
60:96907930389d 65:12af337248b1
       
     1 /*
       
     2  * Copyright (c) 2010 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:  CalenDayContentScrollArea implementation.
       
    15  *
       
    16  */
       
    17 
       
    18 // System includes
       
    19 #include <QGraphicsLinearLayout>
       
    20 #include <QGesture>
       
    21 
       
    22 #include <HbInstance>
       
    23 #include <HbSwipeGesture>
       
    24 
       
    25 // User includes
       
    26 #include "calendaycontentscrollarea.h"
       
    27 #include "calendayutils.h"
       
    28 
       
    29 // Constants
       
    30 /*!
       
    31  Default timeout for scrolling between days [ms]
       
    32  */
       
    33 const int KCalenScrollDaysTimeout = 600;
       
    34 
       
    35 /*!
       
    36  Value [%] defines how long (depending on content area width) should horizontal
       
    37  pan gesture be to change day to previous/next.
       
    38  If the gesture is shorter - current view is not changed.
       
    39  */
       
    40 const int KCalenHScrollMoveParam = 30;  //!< Percentage
       
    41 
       
    42 /*!
       
    43  Value [degree] defines the max. angle of swipe gesture which should change day.
       
    44  */
       
    45 const qreal KCalenSwipeAngle = 30;
       
    46 
       
    47 
       
    48 /*!
       
    49  \class CalenDayContentScrollArea
       
    50  \brief Scrollable container class for content widgets.
       
    51  
       
    52  It handles horizontal scrolling and swipe or pan gestures.
       
    53  */
       
    54 
       
    55 /*!
       
    56  \brief Constructor
       
    57  
       
    58  Configures scroll area settings and resets internal stares of widget.
       
    59  Gets the width of device.
       
    60  
       
    61  \param parent The parent of scroll area widget
       
    62  */
       
    63 CalenDayContentScrollArea::CalenDayContentScrollArea(QGraphicsItem *parent) :
       
    64     HbScrollArea(parent), mPanDayDirection(ECalenPanNotSet), mIsMoving(false),
       
    65     mMoveDirection(ECalenScrollNoDayChange)
       
    66 { 
       
    67 #ifdef CALENDAYVIEW_PANNING_ENABLED
       
    68     grabGesture(Qt::PanGesture);
       
    69     ungrabGesture(Qt::SwipeGesture);
       
    70 #else
       
    71     grabGesture(Qt::SwipeGesture);
       
    72     ungrabGesture(Qt::PanGesture);
       
    73 #endif
       
    74 
       
    75     // Get the width of content area and orientation of screen
       
    76     mContentWidth = CalenDayUtils::instance()->contentWidth();
       
    77     mOrientation = CalenDayUtils::instance()->orientation();
       
    78     
       
    79     // Fix the width of scroll area
       
    80     setMinimumWidth(mContentWidth);
       
    81     setMaximumWidth(mContentWidth);
       
    82 
       
    83     // Connect to main window's orientationChanged SIGNAL to handle orientation
       
    84     // switching
       
    85     connect(CalenDayUtils::instance()->mainWindow(), 
       
    86         SIGNAL(orientationChanged(Qt::Orientation)), this,
       
    87         SLOT(orientationChanged(Qt::Orientation)));
       
    88 }
       
    89 
       
    90 /*!
       
    91  \brief Destructor
       
    92  */
       
    93 CalenDayContentScrollArea::~CalenDayContentScrollArea()
       
    94 {
       
    95 }
       
    96 
       
    97 /*!
       
    98  \brief Scrolls to middle widget.
       
    99  
       
   100  Scrolling to middle widget is done if needed.
       
   101  Resets internal pan direction flag.
       
   102  */
       
   103 void CalenDayContentScrollArea::scrollToMiddleWidget()
       
   104 {
       
   105     QPointF currentPosition = contentWidget()->pos();
       
   106     QPointF destPosition = QPointF(mContentWidth, currentPosition.y());
       
   107 
       
   108     // Scroll only when x position is wrong
       
   109     if (currentPosition.x() != destPosition.x()) {
       
   110         scrollContentsTo(QPointF(mContentWidth, currentPosition.y()), 0);
       
   111     }
       
   112 
       
   113     // Reset pan direction flag and scrolling flag
       
   114     mPanDayDirection = ECalenPanNotSet;
       
   115 }
       
   116 
       
   117 /*!
       
   118  \brief Scrolls the view by the amount indicated by "delta".
       
   119  
       
   120  Checks the direction of pan gesture and promotes leading direction.
       
   121  
       
   122  \param delta Move offset
       
   123  \return Returns TRUE if the view was able to scroll, FALSE otherwise
       
   124  */
       
   125 bool CalenDayContentScrollArea::scrollByAmount(const QPointF &delta)
       
   126 {
       
   127     QPointF newDelta(delta);
       
   128     if (mPanDayDirection == ECalenPanVertical) {
       
   129         newDelta.setX(0);
       
   130     }
       
   131     else
       
   132         if (mPanDayDirection == ECalenPanHorizontal) {
       
   133             newDelta.setY(0);
       
   134         }
       
   135         else {
       
   136             // Pan direction not set
       
   137         }
       
   138 
       
   139     return HbScrollArea::scrollByAmount(newDelta);
       
   140 }
       
   141 
       
   142 /*!
       
   143  \brief Handles pan gesture event (horizontal) or swipe gesture. 
       
   144  
       
   145  Ignores vertical pan gestures.
       
   146  
       
   147  \param event Gesture event to be handled
       
   148  */
       
   149 void CalenDayContentScrollArea::gestureEvent(QGestureEvent *event)
       
   150 {
       
   151 #ifdef CALENDAYVIEW_PANNING_ENABLED
       
   152     // Process a pan gesture event
       
   153     if (QPanGesture *panGesture = qobject_cast<QPanGesture*> (event->gesture(
       
   154         Qt::PanGesture))) {
       
   155 
       
   156         // Checks pan gesture direction
       
   157         checkPanDirection(panGesture);
       
   158 
       
   159         // Put the gesture forward before working with finished gesture
       
   160         HbScrollArea::gestureEvent(event);
       
   161 
       
   162         // If gesture is finished move the scroll area to next or previous 
       
   163         // widget or resume to gesture start point
       
   164         if (panGesture->state() == Qt::GestureFinished) {
       
   165             // Pan direction should be reseted when scrolling ends
       
   166 
       
   167             // Gets the offset of pan gesture.
       
   168             QPointF offset = panGesture->offset();
       
   169 
       
   170             // Note: in horizontal orientation x should is treaten as Y, y as X.
       
   171             QPointF movement;
       
   172             if (mOrientation == Qt::Vertical) {
       
   173                 movement = offset;
       
   174             }
       
   175             else {
       
   176                 movement.setX(offset.y());
       
   177                 movement.setY(offset.x());
       
   178             }
       
   179 
       
   180             // Gesture was long enough for place movement
       
   181             if (qAbs(movement.x()) > (KCalenHScrollMoveParam * mContentWidth / 100)) {
       
   182                 if (movement.x() < 0) {
       
   183                     mMoveDirection = ECalenScrollToNext;
       
   184                     moveTo(QPointF((-mStartPosition.x() + mContentWidth),
       
   185                         -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   186                 }
       
   187                 else {
       
   188                     mMoveDirection = ECalenScrollToPrev;
       
   189                     moveTo(QPointF(-mStartPosition.x() - mContentWidth,
       
   190                         -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   191                 }
       
   192             }
       
   193             // Gesture was short one, reset to gesture start point
       
   194             else {
       
   195                 qreal startPos = mStartPosition.x();
       
   196                 bool isNegative = false;
       
   197                 if (startPos < 0) {
       
   198                     isNegative = true;
       
   199                 }
       
   200                 startPos = qAbs(startPos);
       
   201                 qreal normalizeValue = mContentWidth / 2;
       
   202 
       
   203                 while (startPos > normalizeValue) {
       
   204                     normalizeValue += mContentWidth;
       
   205                 }
       
   206 
       
   207                 if (isNegative) {
       
   208                     mStartPosition.setX(-(normalizeValue - (mContentWidth / 2)));
       
   209                 }
       
   210                 else {
       
   211                     mStartPosition.setX(normalizeValue - (mContentWidth / 2));
       
   212                 }
       
   213 
       
   214                 mMoveDirection = ECalenScrollNoDayChange;
       
   215                 moveTo(-mStartPosition, KCalenScrollDaysTimeout);
       
   216             }
       
   217         }
       
   218     }
       
   219     else {
       
   220         HbScrollArea::gestureEvent(event);
       
   221     }
       
   222 #else
       
   223     // Let the content scroll area ignore pan gestures
       
   224     if (QPanGesture *panGesture = qobject_cast<QPanGesture *> (event->gesture(
       
   225                 Qt::PanGesture))) {
       
   226         // do nothing with pan gesture
       
   227     }
       
   228 
       
   229     if (HbSwipeGesture *swipeGesture =
       
   230         qobject_cast<HbSwipeGesture *> (event->gesture(Qt::SwipeGesture))) {
       
   231         if (swipeGesture->state() == Qt::GestureStarted) {
       
   232             mStartPosition = contentWidget()->pos();
       
   233             
       
   234             qreal swipeAngle = swipeGesture->sceneSwipeAngle();
       
   235             if (isHorizontalSwipe(swipeAngle)) {
       
   236                 if (QSwipeGesture::Left == 
       
   237                     swipeGesture->sceneHorizontalDirection()) {
       
   238                     mMoveDirection = ECalenScrollToNext;
       
   239                     moveTo(QPointF((-mStartPosition.x() + mContentWidth),
       
   240                             -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   241                 }
       
   242                 else if (QSwipeGesture::Right == 
       
   243                     swipeGesture->sceneHorizontalDirection()) {
       
   244                     mMoveDirection = ECalenScrollToPrev;
       
   245                     moveTo(QPointF(-mStartPosition.x() - mContentWidth,
       
   246                             -mStartPosition.y()), KCalenScrollDaysTimeout);
       
   247                 }
       
   248             }
       
   249         }
       
   250     }
       
   251 #endif
       
   252 }
       
   253 
       
   254 /*!
       
   255  \brief Filters pan gesture events.
       
   256  
       
   257  Filters events if this object has been installed as an event filter for
       
   258  the watched object. Handles horizontal pan gestures (ignores vertical).
       
   259  When moving scroll area all gesture events are blocked.
       
   260  
       
   261  \param obj Watched object
       
   262  \param event Event to be filtered
       
   263  \return Returns TRUE if event was handled. FALSE otherwise.
       
   264  */
       
   265 bool CalenDayContentScrollArea::eventFilter(QObject *obj, QEvent *event)
       
   266 {
       
   267     Q_UNUSED(obj);
       
   268 
       
   269     bool handled = false;
       
   270 
       
   271     // Check if we get a gesture event
       
   272     if (event->type() == QEvent::Gesture) {
       
   273 
       
   274         // Blocks handling of gesture events if scrolling started by 
       
   275         // pan gesture is in progress
       
   276         if (mIsMoving) {
       
   277             handled = true;
       
   278         }
       
   279         else {
       
   280             QGestureEvent* gesture = static_cast<QGestureEvent*> (event);
       
   281 
       
   282             // Check if we get a pan gesture
       
   283             QPanGesture *panGesture = qobject_cast<QPanGesture*> (
       
   284                 gesture->gesture(Qt::PanGesture));
       
   285             if (panGesture) {
       
   286                 checkPanDirection(panGesture);
       
   287                 if (mPanDayDirection == ECalenPanHorizontal) {
       
   288                     gestureEvent(gesture);
       
   289                     handled = true;
       
   290                 }
       
   291             }
       
   292         }
       
   293     }
       
   294 
       
   295     return handled;
       
   296 }
       
   297 
       
   298 /*!
       
   299  \brief Overriden event handler.
       
   300  
       
   301  Handles events:
       
   302  - gesture/focus events blocked when horizontal scrolling is in progress
       
   303  - layout request event - scrolls to middle widget if current position is wrong
       
   304  
       
   305  \param e Event to be handled
       
   306  \return Returns TRUE if event was handled. FALSE otherwise.
       
   307  */
       
   308 bool CalenDayContentScrollArea::event(QEvent *e)
       
   309 {
       
   310     bool result = false;
       
   311 
       
   312     // Blocks base class handler for certain events if scrolling started
       
   313     // by pan gesture is in progress
       
   314     if (mIsMoving && (e->type() == QEvent::Gesture || e->type()
       
   315         == QEvent::GestureOverride || e->type() == QEvent::FocusOut
       
   316         || e->type() == QEvent::FocusIn)) {
       
   317         result = true;
       
   318     }
       
   319     if (!result) {
       
   320         // Call base class handler
       
   321         result = HbScrollArea::event(e);
       
   322         
       
   323         // Scroll to middle widget when layout request
       
   324         if (e->type() == QEvent::LayoutRequest || e->type() == QEvent::Show) {
       
   325             scrollToMiddleWidget();
       
   326         }  
       
   327     }
       
   328 
       
   329     return result;
       
   330 }
       
   331 
       
   332 /*!
       
   333  \brief Checks the direction of pan gesture.
       
   334  
       
   335  Changes the scrolling style according to movement direction, 
       
   336  stores the orientation of the pan gesture.
       
   337  Function is used when switching widgets by panning gesture is enabled.
       
   338  
       
   339  \param panGesture Pan gesture event
       
   340  */
       
   341 void CalenDayContentScrollArea::checkPanDirection(QPanGesture *panGesture)
       
   342 {
       
   343     // Gets the offset of pan gesture.
       
   344     QPointF offset = panGesture->offset();
       
   345 
       
   346     // Note: in horizontal orientation x should is treaten as Y, y as X.
       
   347     QPointF movement;
       
   348     if (mOrientation == Qt::Vertical) {
       
   349         movement = offset;
       
   350     }
       
   351     else {
       
   352         movement.setX(offset.y());
       
   353         movement.setY(offset.x());
       
   354     }
       
   355 
       
   356     // If gesture is started check leading movement direction
       
   357     if (panGesture->state() == Qt::GestureStarted) {
       
   358         if (qAbs(movement.x()) < qAbs(movement.y())) {
       
   359             mPanDayDirection = ECalenPanVertical;
       
   360         }
       
   361         else {
       
   362             mStartPosition = contentWidget()->pos();
       
   363             mPanDayDirection = ECalenPanHorizontal;
       
   364         }
       
   365     }
       
   366 }
       
   367 
       
   368 /*!
       
   369  \brief Scrolls the contents to the newPosition in a given time.
       
   370  
       
   371  Sets the flag to indicate that scrolling is in progress. Use this function
       
   372  for scrolling with timeout > 0 to block gesture and focus events during
       
   373  scroll area movement.
       
   374  
       
   375  \param newPosition Destination position
       
   376  \param time Time of scroll movement
       
   377  */
       
   378 void CalenDayContentScrollArea::moveTo(const QPointF &newPosition, int time)
       
   379 {
       
   380     // Connect to scrollingEnded SIGNAL to get feedback when scrolling ends
       
   381     connect(this, SIGNAL(scrollingEnded()), this, SLOT(moveFinished()));
       
   382     
       
   383     // Scroll the content to new position and set isMoving flag
       
   384     scrollContentsTo(newPosition, time);
       
   385     mIsMoving = true;
       
   386     
       
   387     // Emit signal that moving has just started
       
   388     if (mMoveDirection != ECalenScrollNoDayChange) {
       
   389         emit scrollAreaMoveStarted(mMoveDirection);
       
   390     }
       
   391 }
       
   392 
       
   393 /*!
       
   394  \brief isHorizontalSwipe
       
   395  
       
   396  \return TRUE if horizontal swipe was recognized (angle in specific range)
       
   397  */
       
   398 bool CalenDayContentScrollArea::isHorizontalSwipe(qreal angle) const
       
   399 {
       
   400     bool isHSwipe = false;
       
   401     if ((angle < KCalenSwipeAngle) || 
       
   402         ((angle > 180 - KCalenSwipeAngle) && (angle < 180 + KCalenSwipeAngle)) ||
       
   403         (angle > 360 - KCalenSwipeAngle)) {
       
   404         isHSwipe = true;
       
   405     }
       
   406     
       
   407     return isHSwipe;
       
   408 }
       
   409 
       
   410 /*!
       
   411  \brief Slot which is called when moving of scroll area is finished.
       
   412  
       
   413  Resets internal isMoving flag.
       
   414  */
       
   415 void CalenDayContentScrollArea::moveFinished()
       
   416 {
       
   417     // Disconnect from signal, move is finished now
       
   418     disconnect(this, SIGNAL(scrollingEnded()), this, SLOT(moveFinished()));
       
   419     mIsMoving = false;
       
   420     
       
   421     // Emit signal that moving has just finished and reset direction
       
   422     if (mMoveDirection != ECalenScrollNoDayChange) {
       
   423         emit scrollAreaMoveFinished(mMoveDirection);
       
   424         mMoveDirection = ECalenScrollNoDayChange;
       
   425     }
       
   426 }
       
   427 
       
   428 /*!
       
   429  \brief Slot which is called whenever the orientation of the device changes.
       
   430  
       
   431  Stores screen width and orientation in private members.
       
   432  
       
   433  \param orientation Current device orientation
       
   434  */
       
   435 void CalenDayContentScrollArea::orientationChanged(Qt::Orientation orientation)
       
   436 {
       
   437     // Update the width of content area
       
   438     mContentWidth = CalenDayUtils::instance()->contentWidth();
       
   439     mOrientation = orientation;
       
   440 
       
   441     // Fix the width of scroll area
       
   442     setMinimumWidth(mContentWidth);
       
   443     setMaximumWidth(mContentWidth);
       
   444 
       
   445     scrollToMiddleWidget();
       
   446 
       
   447     // Reset flag related to moving
       
   448     mPanDayDirection = ECalenPanNotSet;
       
   449     mMoveDirection = ECalenScrollNoDayChange;
       
   450     mIsMoving = false;
       
   451 }
       
   452 
       
   453 // End of File