summaryrefslogtreecommitdiffstats
path: root/src/declarative/graphicsitems/qdeclarativepathview.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/declarative/graphicsitems/qdeclarativepathview.cpp')
-rw-r--r--src/declarative/graphicsitems/qdeclarativepathview.cpp286
1 files changed, 239 insertions, 47 deletions
diff --git a/src/declarative/graphicsitems/qdeclarativepathview.cpp b/src/declarative/graphicsitems/qdeclarativepathview.cpp
index 783387c..9b548d4 100644
--- a/src/declarative/graphicsitems/qdeclarativepathview.cpp
+++ b/src/declarative/graphicsitems/qdeclarativepathview.cpp
@@ -44,6 +44,7 @@
#include <qdeclarativestate_p.h>
#include <qdeclarativeopenmetaobject_p.h>
+#include <qdeclarativeeasefollow_p.h>
#include <QDebug>
#include <QEvent>
#include <qlistmodelinterface_p.h>
@@ -146,20 +147,23 @@ void QDeclarativePathViewPrivate::updateMappedRange()
mappedRange = 1.0;
}
-qreal QDeclarativePathViewPrivate::positionOfIndex(int index) const
+qreal QDeclarativePathViewPrivate::positionOfIndex(qreal index) const
{
qreal pos = -1.0;
if (model && index >= 0 && index < model->count()) {
- qreal globalPos = qreal(index) + offset;
+ qreal start = 0.0;
+ if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange)
+ start = highlightRangeStart;
+ qreal globalPos = index + offset;
globalPos = qmlMod(globalPos, qreal(model->count())) / model->count();
if (pathItems != -1 && pathItems < model->count()) {
- globalPos += snapPos * mappedRange;
+ globalPos += start * mappedRange;
globalPos = qmlMod(globalPos, 1.0);
if (globalPos < mappedRange)
pos = globalPos / mappedRange;
} else {
- pos = qmlMod(globalPos + snapPos, 1.0);
+ pos = qmlMod(globalPos + start, 1.0);
}
}
@@ -169,6 +173,9 @@ qreal QDeclarativePathViewPrivate::positionOfIndex(int index) const
void QDeclarativePathViewPrivate::createHighlight()
{
Q_Q(QDeclarativePathView);
+ if (!q->isComponentComplete())
+ return;
+
bool changed = false;
if (highlightItem) {
delete highlightItem;
@@ -203,10 +210,67 @@ void QDeclarativePathViewPrivate::createHighlight()
void QDeclarativePathViewPrivate::updateHighlight()
{
Q_Q(QDeclarativePathView);
- if (!q->isComponentComplete())
+ if (!q->isComponentComplete() || !isValid())
return;
- if (highlightItem)
- updateItem(highlightItem, snapPos);
+ if (highlightItem) {
+ if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
+ updateItem(highlightItem, highlightRangeStart);
+ } else {
+ qreal target = currentIndex;
+
+ tl.reset(moveHighlight);
+ moveHighlight.setValue(highlightPosition);
+
+ const int duration = 300;
+
+ if (target - highlightPosition > model->count()/2) {
+ highlightUp = false;
+ qreal distance = model->count() - target + highlightPosition;
+ tl.move(moveHighlight, 0.0, QEasingCurve(QEasingCurve::InQuad), int(duration * highlightPosition / distance));
+ tl.set(moveHighlight, model->count()-0.01);
+ tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * (model->count()-target) / distance));
+ } else if (target - highlightPosition <= -model->count()/2) {
+ highlightUp = true;
+ qreal distance = model->count() - highlightPosition + target;
+ tl.move(moveHighlight, model->count()-0.01, QEasingCurve(QEasingCurve::InQuad), int(duration * (model->count()-highlightPosition) / distance));
+ tl.set(moveHighlight, 0.0);
+ tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::OutQuad), int(duration * target / distance));
+ } else {
+ highlightUp = highlightPosition - target < 0;
+ tl.move(moveHighlight, target, QEasingCurve(QEasingCurve::InOutQuad), duration);
+ }
+ }
+ }
+}
+
+void QDeclarativePathViewPrivate::setHighlightPosition(qreal pos)
+{
+ if (pos != highlightPosition) {
+ qreal start = 0.0;
+ qreal end = 1.0;
+ if (haveHighlightRange && highlightRangeMode != QDeclarativePathView::NoHighlightRange) {
+ start = highlightRangeStart;
+ end = highlightRangeEnd;
+ }
+
+ qreal range = qreal(model->count());
+ // calc normalized position of highlight relative to offset
+ qreal relativeHighlight = qmlMod(pos + offset, range) / range;
+
+ if (!highlightUp && relativeHighlight > end * mappedRange) {
+ qreal diff = 1.0 - relativeHighlight;
+ setOffset(offset + diff * range);
+ } else if (highlightUp && relativeHighlight >= (end - start) * mappedRange) {
+ qreal diff = relativeHighlight - (end - start) * mappedRange;
+ setOffset(offset - diff * range - 0.00001);
+ }
+
+ highlightPosition = pos;
+ qreal pathPos = positionOfIndex(pos);
+ updateItem(highlightItem, pathPos);
+ if (QDeclarativePathViewAttached *att = attached(highlightItem))
+ att->setOnPath(pathPos != -1.0);
+ }
}
void QDeclarativePathViewPrivate::updateItem(QDeclarativeItem *item, qreal percent)
@@ -437,9 +501,11 @@ void QDeclarativePathView::setCurrentIndex(int idx)
}
}
}
+ d->moveReason = QDeclarativePathViewPrivate::SetIndex;
d->currentIndex = idx;
if (d->model->count()) {
- d->snapToCurrent();
+ if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange)
+ d->snapToCurrent();
int itemIndex = (idx - d->firstIndex + d->model->count()) % d->model->count();
if (itemIndex < d->items.count()) {
QDeclarativeItem *item = d->items.at(itemIndex);
@@ -447,6 +513,8 @@ void QDeclarativePathView::setCurrentIndex(int idx)
if (QDeclarativePathViewAttached *att = d->attached(item))
att->setIsCurrentItem(true);
}
+ d->currentItemOffset = d->positionOfIndex(d->currentIndex);
+ d->updateHighlight();
}
emit currentIndexChanged();
}
@@ -478,7 +546,7 @@ void QDeclarativePathViewPrivate::setOffset(qreal o)
if (isValid() && q->isComponentComplete()) {
offset = qmlMod(o, qreal(model->count()));
if (offset < 0)
- offset = model->count() + offset;
+ offset += qreal(model->count());
q->refill();
} else {
offset = o;
@@ -488,29 +556,28 @@ void QDeclarativePathViewPrivate::setOffset(qreal o)
}
/*!
- \qmlproperty real PathView::snapPosition
+ \qmlproperty component PathView::highlight
+ This property holds the component to use as the highlight.
- This property determines the position on the path (0.0-1.0) the nearest item will snap to.
- The item nearest this position will set currentIndex, for example when offset is 0.0 the
- first item will be placed at this position and currentIndex will be 0.
-*/
-qreal QDeclarativePathView::snapPosition() const
-{
- Q_D(const QDeclarativePathView);
- return d->snapPos;
-}
+ An instance of the highlight component will be created for each view.
+ The geometry of the resultant component instance will be managed by the view
+ so as to stay with the current item.
-void QDeclarativePathView::setSnapPosition(qreal pos)
-{
- Q_D(QDeclarativePathView);
- qreal normalizedPos = pos - int(pos);
- if (qFuzzyCompare(normalizedPos, d->snapPos))
- return;
- d->snapPos = normalizedPos;
- d->updateHighlight();
- d->fixOffset();
- emit snapPositionChanged();
-}
+ The below example demonstrates how to make a simple highlight. Note the use
+ of the PathView.onPath property to ensure that the highlight is hidden
+ when flicked off of the path.
+
+ \code
+ Component {
+ Rectangle {
+ visible: PathView.onPath
+ ...
+ }
+ }
+ \endcode
+
+ \sa highlightItem, highlightRangeMode
+*/
QDeclarativeComponent *QDeclarativePathView::highlight() const
{
@@ -529,11 +596,99 @@ void QDeclarativePathView::setHighlight(QDeclarativeComponent *highlight)
}
}
+/*!
+ \qmlproperty Item PathView::highlightItem
+
+ \c highlightItem holds the highlight item, which was created
+ from the \l highlight component.
+
+ \sa highlight
+*/
QDeclarativeItem *QDeclarativePathView::highlightItem()
{
Q_D(const QDeclarativePathView);
return d->highlightItem;
}
+/*!
+ \qmlproperty real PathView::preferredHighlightBegin
+ \qmlproperty real PathView::preferredHighlightEnd
+ \qmlproperty enumeration PathView::highlightRangeMode
+
+ These properties set the preferred range of the highlight (current item)
+ within the view. The preferred values must be in the range 0.0-1.0.
+
+ If highlightRangeMode is set to \e ApplyRange the view will
+ attempt to maintain the highlight within the range, however
+ the highlight can move outside of the range at the ends of the path
+ or due to a mouse interaction.
+
+ If highlightRangeMode is set to \e StrictlyEnforceRange the highlight will never
+ move outside of the range. This means that the current item will change
+ if a keyboard or mouse action would cause the highlight to move
+ outside of the range.
+
+ Note that this is the correct way to influence where the
+ current item ends up when the view moves. For example, if you want the
+ currently selected item to be in the middle of the path, then set the
+ highlight range to be 0.5,0.5 and highlightRangeMode to StrictlyEnforceRange.
+ Then, when the path scrolls,
+ the currently selected item will be the item at that position. This also applies to
+ when the currently selected item changes - it will scroll to within the preferred
+ highlight range. Furthermore, the behaviour of the current item index will occur
+ whether or not a highlight exists.
+
+ The default value is \e StrictlyEnforceRange.
+
+ Note that a valid range requires preferredHighlightEnd to be greater
+ than or equal to preferredHighlightBegin.
+*/
+qreal QDeclarativePathView::preferredHighlightBegin() const
+{
+ Q_D(const QDeclarativePathView);
+ return d->highlightRangeStart;
+}
+
+void QDeclarativePathView::setPreferredHighlightBegin(qreal start)
+{
+ Q_D(QDeclarativePathView);
+ if (d->highlightRangeStart == start || start < 0 || start > 1.0)
+ return;
+ d->highlightRangeStart = start;
+ d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit preferredHighlightBeginChanged();
+}
+
+qreal QDeclarativePathView::preferredHighlightEnd() const
+{
+ Q_D(const QDeclarativePathView);
+ return d->highlightRangeEnd;
+}
+
+void QDeclarativePathView::setPreferredHighlightEnd(qreal end)
+{
+ Q_D(QDeclarativePathView);
+ if (d->highlightRangeEnd == end || end < 0 || end > 1.0)
+ return;
+ d->highlightRangeEnd = end;
+ d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit preferredHighlightEndChanged();
+}
+
+QDeclarativePathView::HighlightRangeMode QDeclarativePathView::highlightRangeMode() const
+{
+ Q_D(const QDeclarativePathView);
+ return d->highlightRangeMode;
+}
+
+void QDeclarativePathView::setHighlightRangeMode(HighlightRangeMode mode)
+{
+ Q_D(QDeclarativePathView);
+ if (d->highlightRangeMode == mode)
+ return;
+ d->highlightRangeMode = mode;
+ d->haveHighlightRange = d->highlightRangeMode != NoHighlightRange && d->highlightRangeStart <= d->highlightRangeEnd;
+ emit highlightRangeModeChanged();
+}
/*!
\qmlproperty real PathView::dragMargin
@@ -770,16 +925,18 @@ void QDeclarativePathView::mouseReleaseEvent(QGraphicsSceneMouseEvent *)
velocity = (velocity > 0 ? count : -count) * 2;
// Calculate the distance to be travelled
qreal v2 = velocity*velocity;
- qreal accel = d->deceleration;
+ qreal accel = d->deceleration/10;
// + 0.25 to encourage moving at least one item in the flick direction
- qreal dist = qMin(qreal(d->model->count()-1), qreal(qreal(d->model->count()) * v2 / (accel * 2.0) + 0.25));
- // round to nearest item.
- if (velocity > 0.)
- dist = qRound(dist + d->offset) - d->offset;
- else
- dist = qRound(dist - d->offset) + d->offset;
- // Calculate accel required to stop on item boundary
- accel = v2 / (2.0f * qAbs(dist));
+ qreal dist = qMin(qreal(d->model->count()-1), qreal(v2 / (accel * 2.0) + 0.25));
+ if (d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
+ // round to nearest item.
+ if (velocity > 0.)
+ dist = qRound(dist + d->offset) - d->offset;
+ else
+ dist = qRound(dist - d->offset) + d->offset;
+ // Calculate accel required to stop on item boundary
+ accel = v2 / (2.0f * qAbs(dist));
+ }
d->moveOffset.setValue(d->offset);
d->tl.accel(d->moveOffset, velocity, accel, dist);
d->tl.callback(QDeclarativeTimeLineCallback(&d->moveOffset, d->fixOffsetCallback, d));
@@ -862,6 +1019,7 @@ void QDeclarativePathView::componentComplete()
{
Q_D(QDeclarativePathView);
QDeclarativeItem::componentComplete();
+ d->createHighlight();
d->regenerate();
d->updateHighlight();
}
@@ -872,7 +1030,7 @@ void QDeclarativePathView::refill()
if (!d->isValid() || !isComponentComplete())
return;
-// qDebug() << "offset" << d->_offset;
+ bool currentVisible = false;
// first move existing items and remove items off path
int idx = d->firstIndex;
@@ -882,6 +1040,10 @@ void QDeclarativePathView::refill()
QDeclarativeItem *item = *it;
if (pos >= 0.0) {
d->updateItem(item, pos);
+ if (idx == d->currentIndex) {
+ currentVisible = true;
+ d->currentItemOffset = pos;
+ }
++it;
} else {
// qDebug() << "release";
@@ -902,7 +1064,9 @@ void QDeclarativePathView::refill()
int count = d->pathItems == -1 ? d->model->count() : qMin(d->pathItems, d->model->count());
if (d->items.count() < count) {
int idx = qRound(d->model->count() - d->offset) % d->model->count();
- qreal startPos = d->snapPos;
+ qreal startPos = 0.0;
+ if (d->haveHighlightRange && d->highlightRangeMode != QDeclarativePathView::NoHighlightRange)
+ startPos = d->highlightRangeStart;
if (d->firstIndex >= 0) {
startPos = d->positionOfIndex(d->firstIndex);
idx = (d->firstIndex + d->items.count()) % d->model->count();
@@ -917,6 +1081,8 @@ void QDeclarativePathView::refill()
item->setFocus(true);
if (QDeclarativePathViewAttached *att = d->attached(item))
att->setIsCurrentItem(true);
+ currentVisible = true;
+ d->currentItemOffset = pos;
}
if (d->items.count() == 0)
d->firstIndex = idx;
@@ -941,6 +1107,8 @@ void QDeclarativePathView::refill()
item->setFocus(true);
if (QDeclarativePathViewAttached *att = d->attached(item))
att->setIsCurrentItem(true);
+ currentVisible = true;
+ d->currentItemOffset = pos;
}
d->items.prepend(item);
d->updateItem(item, pos);
@@ -951,6 +1119,19 @@ void QDeclarativePathView::refill()
pos = d->positionOfIndex(idx);
}
}
+
+ if (!currentVisible)
+ d->currentItemOffset = 1.0;
+
+ if (d->highlightItem && d->haveHighlightRange && d->highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
+ d->updateItem(d->highlightItem, d->highlightRangeStart);
+ if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem))
+ att->setOnPath(true);
+ } else if (d->moveReason != QDeclarativePathViewPrivate::SetIndex) {
+ d->updateItem(d->highlightItem, d->currentItemOffset);
+ if (QDeclarativePathViewAttached *att = d->attached(d->highlightItem))
+ att->setOnPath(currentVisible);
+ }
}
void QDeclarativePathView::itemsInserted(int modelIndex, int count)
@@ -962,6 +1143,10 @@ void QDeclarativePathView::itemsInserted(int modelIndex, int count)
QList<QDeclarativeItem *> removedItems = d->items;
d->items.clear();
+ if (modelIndex <= d->currentIndex) {
+ d->currentIndex += count;
+ emit currentIndexChanged();
+ }
d->regenerate();
while (removedItems.count())
d->releaseItem(removedItems.takeLast());
@@ -980,6 +1165,7 @@ void QDeclarativePathView::itemsRemoved(int modelIndex, int count)
d->items.clear();
if (d->offset >= d->model->count())
d->offset = d->model->count() - 1;
+ //XXX update currentIndex
d->regenerate();
while (removedItems.count())
d->releaseItem(removedItems.takeLast());
@@ -995,6 +1181,7 @@ void QDeclarativePathView::itemsMoved(int from, int to, int count)
QList<QDeclarativeItem *> removedItems = d->items;
d->items.clear();
+ //XXX update currentIndex
d->regenerate();
while (removedItems.count())
d->releaseItem(removedItems.takeLast());
@@ -1047,6 +1234,9 @@ void QDeclarativePathViewPrivate::updateCurrent()
Q_Q(QDeclarativePathView);
if (moveReason != Mouse)
return;
+ if (!haveHighlightRange || highlightRangeMode != QDeclarativePathView::StrictlyEnforceRange)
+ return;
+
int idx = calcCurrentIndex();
if (model && idx != currentIndex) {
int itemIndex = (currentIndex - firstIndex + model->count()) % model->count();
@@ -1077,11 +1267,13 @@ void QDeclarativePathViewPrivate::fixOffset()
{
Q_Q(QDeclarativePathView);
if (model && items.count()) {
- int curr = calcCurrentIndex();
- if (curr != currentIndex)
- q->setCurrentIndex(curr);
- else
- snapToCurrent();
+ if (haveHighlightRange && highlightRangeMode == QDeclarativePathView::StrictlyEnforceRange) {
+ int curr = calcCurrentIndex();
+ if (curr != currentIndex)
+ q->setCurrentIndex(curr);
+ else
+ snapToCurrent();
+ }
}
}
@@ -1093,7 +1285,7 @@ void QDeclarativePathViewPrivate::snapToCurrent()
qreal targetOffset = model->count() - currentIndex;
moveReason = Other;
- tl.clear();
+ tl.reset(moveOffset);
moveOffset.setValue(offset);
const int duration = 300;