src/hbcore/gui/hbscrollbar.cpp
changeset 0 16d8024aca5e
child 1 f7ac710697a9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hbcore/gui/hbscrollbar.cpp	Mon Apr 19 14:02:13 2010 +0300
@@ -0,0 +1,684 @@
+/****************************************************************************
+**
+** Copyright (C) 2008-2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (developer.feedback@nokia.com)
+**
+** This file is part of the HbCore module of the UI Extensions for Mobile.
+**
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this file.
+** Please review the following information to ensure the GNU Lesser General
+** Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights.  These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** If you have questions regarding the use of this file, please contact
+** Nokia at developer.feedback@nokia.com.
+**
+****************************************************************************/
+
+#include <hbscrollbar.h>
+#include <hbscrollbar_p.h>
+#include <hbwidget_p.h>
+#include <hbstyleoptionscrollbar.h>
+#include <hbwidgetfeedback.h>
+#include <hbframeitem.h>
+
+#include <QGraphicsSceneMouseEvent>
+#include <QGesture>
+#include <QDebug>
+
+#ifdef HB_EFFECTS
+#include "hbeffect.h"
+#include "hbeffectinternal_p.h"
+bool HbScrollBarPrivate::effectsLoaded = false;
+#endif
+
+static const int REPEATION_TIME = 500;
+static const qreal THRESHOLD_VALUE = 2.0;
+/*!
+    @stable
+    @hbcore
+    \class HbScrollBar
+    \brief HbScrollBar represents a scrollbar that can be used in different kinds of lists, options
+    menus and editors.
+
+    A vertical scrollbar is created in this example to listview-parent and also shown:
+
+    \code
+    //HbListView *listView is defined already
+    HbScrollBar *scrollbar = new HbScrollBar(Qt::Vertical, listView);
+    scrollbar->show();
+    \endcode
+
+    By default scrollbar is hidden.
+*/
+
+/*!
+    \reimp
+    \fn int HbScrollBar::type() const
+ */
+
+/*!
+    \fn void HbScrollBar::valueChanged( qreal value, Qt::Orientation orientation )
+
+    This signal is emitted when thumb position is changed from user interaction.
+*/
+
+HbScrollBarPrivate::HbScrollBarPrivate():
+        mOrientation(Qt::Vertical),
+        mCurrentPosition(0.5),
+        mPageSize(1.0),
+        mActivated(false),
+        mThumbPressed(false),
+        mGroovePressed(false),
+        mInteractive(false),
+        mPressedTargetValue(0.0),
+        grooveItem(0),
+        handleItem(0),
+        mTouchArea(0),
+        mBoundingRect(QRectF(0,0,0,0)),
+        lastEmittedPos(QPointF()),
+        emittedPos(false)
+{
+}
+
+HbScrollBarPrivate::~HbScrollBarPrivate()
+{
+}
+
+void HbScrollBarPrivate::init()
+{
+}
+
+void HbScrollBarPrivate::createPrimitives()
+{
+    Q_Q(HbScrollBar);
+
+    if ( !grooveItem ) {
+        grooveItem = q->style()->createPrimitive( HbStyle::P_ScrollBar_groove, q );
+        grooveItem->setZValue(2);
+        HbStyle::setItemName( grooveItem, "groove" );
+    }
+
+    if ( !handleItem ) {
+        handleItem = q->style()->createPrimitive( HbStyle::P_ScrollBar_handle, q );
+        handleItem->setZValue(3);
+        HbStyle::setItemName( handleItem, "handle" );
+    }
+    if( !mTouchArea ) {
+        mTouchArea = q->style()->createPrimitive(HbStyle::P_ScrollBar_toucharea, q);
+    }
+    q->updatePrimitives();
+}
+
+void HbScrollBarPrivate::updatePosition()
+{
+    if(!mHandleGeometry.isValid() || !handleItem)
+        return;
+    if (handleItem){
+        if (mOrientation == Qt::Vertical) {
+            handleItem->setPos(mHandleGeometry.topLeft().x(), mCurrentPosition * (mBoundingRect.height() - mHandleGeometry.height()));
+        } else {
+            handleItem->setPos(mCurrentPosition * (mBoundingRect.width() - mHandleGeometry.width()), mHandleGeometry.topLeft().y());
+        }
+    }
+}
+
+void HbScrollBarPrivate::sizeHelper()
+{
+    if(!polished)
+        return;
+    Q_Q(HbScrollBar);
+    if(handleItem){
+        HbFrameItem *item = qgraphicsitem_cast<HbFrameItem*>(handleItem);
+        mBoundingRect = q->boundingRect();
+        if(item){
+            if (mOrientation == Qt::Vertical) {
+                qreal height(mPageSize * mBoundingRect.height());
+                if(!qFuzzyCompare(item->preferredHeight(),height)){
+                    item->setPreferredHeight(height);
+                    item->resize(item->size().width(), height);
+                }
+            } else {
+                qreal width(mPageSize * mBoundingRect.width());
+                if(!qFuzzyCompare(item->preferredWidth(),width)){
+                    item->setPreferredWidth(width);
+                    item->resize(width, item->size().height());
+                }
+            }
+            mHandleGeometry = item->geometry();
+            updatePosition();
+        }
+    }
+}
+
+void HbScrollBarPrivate::loadEffects()
+{
+#if defined(HB_EFFECTS)
+    if (effectsLoaded)
+        return;
+    hasEffects = HbEffectInternal::add( "HB_scrollbar",
+                                        "scrollbar_activate",
+                                        "activate" );
+    if ( hasEffects ) {
+        hasEffects = HbEffectInternal::add( "HB_scrollbar",
+                                            "scrollbar_deactivate",
+                                            "deactivate" );
+
+    }
+    effectsLoaded = true;
+#endif
+}
+
+void HbScrollBarPrivate::startShowEffect() 
+{
+#if defined(HB_EFFECTS)
+    if (!hasEffects)
+        loadEffects();
+    Q_Q(HbScrollBar);
+    if (hasEffects) {
+        HbEffect::start(q, "HB_scrollbar", "activate");
+    }
+#endif
+}
+
+void HbScrollBarPrivate::startHideEffect() 
+{
+#if defined(HB_EFFECTS)
+    Q_Q(HbScrollBar);
+    if (hasEffects) {
+        HbEffect::start(q, "HB_scrollbar", "deactivate");
+        //        q->resetTransform();
+
+    }
+#endif
+}
+
+/*!
+    Constructs a scrollbar with \a parent.
+*/
+HbScrollBar::HbScrollBar( QGraphicsItem *parent ) :
+        HbWidget(*new HbScrollBarPrivate, parent)
+{
+    Q_D(HbScrollBar);
+    d->q_ptr = this;
+    d->init();
+    grabGesture(Qt::TapGesture);
+    grabGesture(Qt::PanGesture);
+}
+
+/*!
+    Constructs a scrollbar with \a orientation and \a parent.
+*/
+HbScrollBar::HbScrollBar( Qt::Orientation orientation, QGraphicsItem *parent ) :
+        HbWidget( *new HbScrollBarPrivate, parent )
+{
+    Q_D(HbScrollBar);
+    d->q_ptr = this;
+    d->mOrientation = orientation;
+    d->init();
+}
+
+/*!
+    Destructor
+ */
+HbScrollBar::~HbScrollBar()
+{
+}
+
+/*!
+    Returns the value of the scrollbar. The value is in range of 0.0 to 1.0.
+    The value corresponds to the position of the thumb.
+
+    \sa HbScrollBar::setValue()
+*/
+qreal HbScrollBar::value() const
+{
+    Q_D( const HbScrollBar );
+    return d->mCurrentPosition;
+}
+
+/*!
+   Returns relative size of the visible area the scrollbar is attached to.
+   For example if the pageSize is 0.2 it means that the overall size of the content
+   is five times larger than what is shown currently.
+
+   The size is in range of 0.0 to 1.0.
+   \sa HbScrollBar::setPageSize()
+*/
+qreal HbScrollBar::pageSize() const
+{
+    Q_D( const HbScrollBar );
+    return d->mPageSize;
+}
+
+/*!
+    Returns the orientation of scrollbar.
+
+    \sa HbScrollBar::setOrientation()
+*/
+Qt::Orientation HbScrollBar::orientation() const
+{
+    Q_D( const HbScrollBar );
+    return d->mOrientation;
+}
+
+/*!
+    Returns whether scrollbar is in interactive mode.
+
+    \sa HbScrollBar::setInteractive()
+*/
+bool HbScrollBar::isInteractive() const
+{
+    const Q_D(HbScrollBar);
+    return d->mInteractive;
+}
+
+/*!
+    Returns whether scrollbar is in interactive mode.
+
+    \deprecated HbScrollBar::interactive() const
+        is deprecated. Please use HbScrollBar::isInteractive() const instead.
+
+    \sa HbScrollBar::isInteractive()
+*/
+bool HbScrollBar::interactive() const
+{
+    qWarning() << "HbScrollBar::interactive() is DEPRECATED! Use HbScrollBar::isInteractive";
+    return isInteractive();
+}
+
+/*!
+    Sets the value of interactive property. If this value is set
+    to true scrollbar is interactive, the user can change the scroll position by
+    dragging the thumb or pressing the groove. Dragging the thumb emits valueChanged
+    signal and pressing the groove emits valueChangeRequested which means
+    that the widget using scrollbars should handle the value change (for example
+    by animating to the given target value).
+
+    The default behavior is non interactive,
+    which means that the scrollbar is just indicative.
+
+    \sa HbScrollBar::isInteractive()
+*/
+void HbScrollBar::setInteractive( bool enabled )
+{
+    Q_D( HbScrollBar );
+    if( d->mInteractive != enabled){
+        d->mInteractive = enabled;
+        if(d->handleItem) {
+            repolish();
+            updatePrimitives();
+        }
+    }
+}
+
+/*!
+    Sets the value of the scrollbar. The given \avalue should be from 0.0 to 1.0.
+    The value is used to position the thumb.
+
+    \sa HbScrollBar::value()
+*/
+void HbScrollBar::setValue( qreal value )
+{
+    Q_D(HbScrollBar);
+    if( value < 0.0 || value > 1.0 ) {
+        qWarning( "HbScrollBar::setValue(%f) out of range 0.0-1.0", value );
+        return;
+    }
+
+    if( !qFuzzyCompare(d->mCurrentPosition,value )) {
+        d->mCurrentPosition = value;
+        d->updatePosition();
+    }
+}
+
+/*!
+    Sets the page size for the scrollbar. The page size is relative size of the visible area
+    the scrollbar is attached to. For example if the pageSize is 0.2 it means that the
+    overall size of the content is five times larger than what is shown currently.
+
+    \asize should be in the range of 0.0 to 1.0.
+    \sa HbScrollBar::pageSize()
+*/
+void HbScrollBar::setPageSize( qreal size )
+{
+    Q_D(HbScrollBar);
+    if( size < 0.0 || size > 1.0 ) {
+        qWarning( "HbScrollBar::setPageSize(%f) out of range 0.0-1.0", size );
+        return;
+    }
+
+    if(!qFuzzyCompare(d->mPageSize,size)) {
+        d->mPageSize = size;
+        d->sizeHelper();
+    }
+}
+
+/*!
+    Sets the orientation of scrollbar.
+
+    \sa HbScrollBar::orientation()
+*/
+void HbScrollBar::setOrientation( Qt::Orientation orientation )
+{
+    Q_D(HbScrollBar);
+    if(d->mOrientation != orientation) {
+        d->mOrientation = orientation;
+        repolish();
+    }
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::updatePrimitives()
+{
+    Q_D(HbScrollBar);
+
+    HbStyleOptionScrollBar option;
+    initStyleOption(&option);
+    if (d->grooveItem) {
+        style()->updatePrimitive(d->grooveItem, HbStyle::P_ScrollBar_groove, &option);
+    }
+    if (d->handleItem) {
+        style()->updatePrimitive(d->handleItem, HbStyle::P_ScrollBar_handle, &option);
+    }
+    if( d->mTouchArea ) {
+        style()->updatePrimitive(d->mTouchArea, HbStyle::P_ScrollBar_toucharea, &option);
+    }
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::initStyleOption( HbStyleOptionScrollBar *option ) const
+{
+    Q_D(const HbScrollBar);
+    HbWidget::initStyleOption(option);
+
+    Q_ASSERT(option);
+    option->orientation = d->mOrientation;
+    option->thumbPressed = d->mThumbPressed;
+    option->groovePressed = d->mGroovePressed;
+    option->interactive = d->mInteractive;
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::mousePressEvent( QGraphicsSceneMouseEvent *event )
+{
+    Q_D(HbScrollBar);
+    QGraphicsWidget::mousePressEvent(event);
+
+    if ( !d->mInteractive ) {
+        return;
+    }
+
+    HbStyleOptionScrollBar opt;
+
+    QRectF handleBounds = d->handleItem->boundingRect();
+
+    d->mPressPosition = mapToItem(d->handleItem, event->pos());
+
+    switch (orientation()) {
+    case Qt::Horizontal:
+        d->mThumbPressed = (event->pos().x() >= d->handleItem->pos().x() &&
+                            event->pos().x() <= d->handleItem->pos().x() + handleBounds.width());
+        if (!d->mThumbPressed) {
+            d->mGroovePressed = true;
+            HbWidgetFeedback::triggered(this, Hb::InstantPressed);
+
+            if (d->handleItem->pos().x() < event->pos().x()) {
+                emit valueChangeRequested(qMin(value() + pageSize(), qreal(1.0)), orientation());
+            } else {
+                emit valueChangeRequested(qMax(value() - pageSize(), qreal(0.0)), orientation());
+            }
+            d->mPressedTargetValue = qBound(qreal(0.0),
+                                            qreal((event->pos().x() - (handleBounds.width() / 2.0)) / (boundingRect().width() - handleBounds.width())),
+                                            qreal(1.0));
+
+            d->repeatActionTimer.start(REPEATION_TIME, this);
+        } else {
+            HbWidgetFeedback::triggered(this, Hb::InstantPressed, Hb::ModifierSliderHandle);
+            initStyleOption(&opt);
+            style()->updatePrimitive(d->handleItem, HbStyle::P_ScrollBar_handle, &opt );
+        }
+
+        break;
+    case Qt::Vertical:
+        d->mThumbPressed = (event->pos().y() >= d->handleItem->pos().y() &&
+                            event->pos().y() <= d->handleItem->pos().y() + handleBounds.height());
+        if (!d->mThumbPressed) {
+            d->mGroovePressed = true;
+            HbWidgetFeedback::triggered(this, Hb::InstantPressed);
+
+            if (d->handleItem->pos().y() < event->pos().y()) {
+                emit valueChangeRequested(qMin(value() + pageSize(), qreal(1.0)), orientation());
+            } else {
+                emit valueChangeRequested(qMax(value() - pageSize(), qreal(0.0)), orientation());
+            }
+
+            d->mPressedTargetValue = qBound(qreal(0.0),
+                                            qreal((event->pos().y() - (handleBounds.height() / 2.0)) / (boundingRect().height() - handleBounds.height())),
+                                            qreal(1.0));
+
+            d->repeatActionTimer.start(REPEATION_TIME, this);
+        } else {
+            HbWidgetFeedback::triggered(this, Hb::InstantPressed, Hb::ModifierSliderHandle);
+            initStyleOption(&opt);
+            style()->updatePrimitive(d->handleItem, HbStyle::P_ScrollBar_handle, &opt );
+        }
+
+        break;
+    }
+    event->accept();
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::mouseReleaseEvent( QGraphicsSceneMouseEvent *event )
+{
+    Q_D(HbScrollBar);
+    QGraphicsWidget::mouseReleaseEvent(event);
+
+    if ( !d->mInteractive ) {
+        return;
+    }
+    HbStyleOptionScrollBar opt;
+    if (d->mThumbPressed) {
+        d->mThumbPressed = false;
+        HbWidgetFeedback::triggered(this, Hb::InstantReleased, Hb::ModifierSliderHandle);
+        emit valueChanged(value(), orientation());
+        initStyleOption(&opt);
+        style()->updatePrimitive(d->handleItem, HbStyle::P_ScrollBar_handle, &opt );
+    } else if (d->mGroovePressed){
+        HbWidgetFeedback::triggered(this, Hb::InstantReleased);
+        d->repeatActionTimer.stop();
+        d->mGroovePressed = false;
+        initStyleOption(&opt);
+        style()->updatePrimitive(d->grooveItem, HbStyle::P_ScrollBar_groove, &opt );
+    }
+    d->emittedPos = false;
+    event->accept();            
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::mouseMoveEvent( QGraphicsSceneMouseEvent *event )
+{
+    Q_D(HbScrollBar);
+    QGraphicsWidget::mouseMoveEvent(event);
+
+    if ( !d->mInteractive ) {
+        return;
+    }
+
+    if (d->mThumbPressed) {
+        qreal newPosition(0);
+        QPointF movePosition(0, 0);
+        switch (orientation()) {
+        case Qt::Horizontal:
+            newPosition = (event->pos().x() - d->mPressPosition.x()) /
+                          (boundingRect().width() - d->handleItem->boundingRect().width());
+            movePosition.setX(event->pos().x());
+            break;
+        case Qt::Vertical:
+            newPosition = (event->pos().y() - d->mPressPosition.y()) / 
+                          (boundingRect().height() - d->handleItem->boundingRect().height());
+            movePosition.setY(event->pos().y());
+            break;
+        }
+        HbWidgetFeedback::continuousTriggered(this, Hb::ContinuousDragged);
+        qreal newValue = qBound(qreal(0.0), newPosition,  qreal(1.0));
+        if (!d->emittedPos ||
+            (qAbs(d->lastEmittedPos.x() - movePosition.x()) >= qreal(THRESHOLD_VALUE)) ||
+            (qAbs(d->lastEmittedPos.y() - movePosition.y()) >= qreal(THRESHOLD_VALUE))) {
+            setValue(newValue);
+            d->lastEmittedPos = movePosition;
+            d->emittedPos = true;
+            emit valueChanged(newValue, orientation());
+        }        
+    }
+}
+
+/*!
+    \reimp
+ */
+QRectF HbScrollBar::boundingRect() const
+{
+    Q_D(const HbScrollBar);
+    QRectF newBoundingRect = HbWidget::boundingRect();    
+    /* Workaround for touch area and event filter issue */
+    if (d->mInteractive) {
+        if (d->mOrientation == Qt::Vertical && d->mTouchArea) {
+            QRectF toucharea = d->mTouchArea->boundingRect();
+            qreal newWidth = (toucharea.width() - newBoundingRect.width());
+            newBoundingRect.setRight(toucharea.width());
+            newBoundingRect.setLeft(-newWidth);
+        } else if (d->mTouchArea) {
+            QRectF toucharea = d->mTouchArea->boundingRect();
+            qreal newHeight = (toucharea.height() - newBoundingRect.height());
+            newBoundingRect.setBottom(toucharea.height());
+            newBoundingRect.setTop(-newHeight);
+        }
+    }
+    /* Workaround ends */
+    return newBoundingRect;
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::timerEvent( QTimerEvent *event )
+{
+    Q_D(HbScrollBar);
+    if (event->timerId() == d->repeatActionTimer.timerId()) {
+        if (value() > d->mPressedTargetValue) {
+            HbWidgetFeedback::triggered(this, Hb::InstantKeyRepeated, Hb::ModifierSliderHandle);
+            emit valueChangeRequested(value() - qMin(pageSize(), value() - d->mPressedTargetValue), orientation());
+        } else if (value() < d->mPressedTargetValue){
+            HbWidgetFeedback::triggered(this, Hb::InstantKeyRepeated, Hb::ModifierSliderHandle);
+            emit valueChangeRequested(value() + qMin(pageSize(), d->mPressedTargetValue - value()), orientation());
+        }
+        if (value() != d->mPressedTargetValue) {
+            d->repeatActionTimer.start(REPEATION_TIME, this);
+        } else {
+            d->repeatActionTimer.stop();
+        }
+    }
+}
+
+/*!
+    \reimp
+*/
+bool HbScrollBar::event(QEvent *event)
+{
+    if (event && event->type() == QEvent::GraphicsSceneResize) {
+        Q_D(HbScrollBar);
+        d->sizeHelper();
+    }
+    return HbWidget::event(event);
+}
+
+void HbScrollBar::resizeEvent(QGraphicsSceneResizeEvent *event)
+{
+    HbWidget::resizeEvent(event);
+    Q_D(HbScrollBar);
+    if (d->handleItem) {
+        HbFrameItem* item = (qgraphicsitem_cast<HbFrameItem*>(d->handleItem));
+        d->mHandleGeometry = item->geometry();
+    }
+}
+
+/*!
+    \reimp
+*/
+void HbScrollBar::updateGeometry()
+{
+    Q_D(HbScrollBar);
+    d->sizeHelper();
+    HbWidget::updateGeometry();
+}
+
+/*!
+    \reimp
+ */
+QVariant HbScrollBar::itemChange ( GraphicsItemChange change, const QVariant & value )
+{
+    if (change == QGraphicsItem::ItemVisibleChange) {
+        Q_D(HbScrollBar);
+        if (value.toBool()) {
+            if (!d->mActivated) {
+                d->startShowEffect();
+                d->mActivated = true;
+            }
+        } else {
+            if (d->mActivated) {
+                d->startHideEffect();
+                d->mActivated = false;
+            }
+        }
+    }
+    return HbWidget::itemChange(change, value);
+}
+
+/*!
+    \reimp
+ */
+void HbScrollBar::polish( HbStyleParameters& params )
+{
+    Q_D(HbScrollBar);
+    if (!d->handleItem && isVisible()){
+        d->createPrimitives();
+    }
+    HbWidget::polish(params);
+    d->sizeHelper();
+}
+
+/*!
+  \reimp
+*/
+void HbScrollBar::gestureEvent(QGestureEvent* event) 
+{
+    Q_D(HbScrollBar);
+    if ( !d->mInteractive ) {
+        return;
+    }
+    // as gestures don't provide low enough details, just eat them to
+    // prevent propagation to other components.
+    event->accept(Qt::TapGesture);
+    event->accept(Qt::PanGesture);
+    event->accept();
+}
+#include "moc_hbscrollbar.cpp"