~zeller-benjamin/ubuntu-ui-toolkit/qrcify3

« back to all changes in this revision

Viewing changes to src/Ubuntu/Components/plugin/uclistitem.cpp

  • Committer: Benjamin Zeller
  • Date: 2015-09-10 08:43:12 UTC
  • mfrom: (1585.1.2 staging)
  • Revision ID: benjamin.zeller@canonical.com-20150910084312-6eh8n2729jpslgmb
Merge staging

Show diffs side-by-side

added added

removed removed

Lines of Context:
200
200
    , leadingActions(0)
201
201
    , trailingActions(0)
202
202
    , mainAction(0)
 
203
    , expansion(Q_NULLPTR)
203
204
{
204
205
}
205
206
UCListItemPrivate::~UCListItemPrivate()
325
326
bool UCListItemPrivate::loadStyleItem(bool animated)
326
327
{
327
328
    // the style should be loaded only if one of the condition is satisfied
328
 
    if (!swiped && !selectMode() && !dragMode()) {
 
329
    if (!swiped && !selectMode() && !dragMode() && !(expansion && expansion->expanded())) {
329
330
        return false;
330
331
    }
331
332
 
339
340
        preStyleChanged();
340
341
        return false;
341
342
    }
 
343
    myStyle->updateFlickable(flickable);
342
344
    // bring the panels foreground
343
345
    styleItem->setZ(0);
344
346
    listItemStyle()->setAnimatePanels(true);
372
374
                (parentItem ? QQuickItemPrivate::get(parentItem)->childItems.indexOf(q) : -1);
373
375
}
374
376
 
375
 
// returns true if the highlight is possible
376
 
bool UCListItemPrivate::canHighlight(QMouseEvent *event)
 
377
// returns true if the highlight is possible; the highlight is possible if the
 
378
// list item has at least one action, leading/trailing actions set, onClicked
 
379
// or onPressAndHold signal handlers set
 
380
bool UCListItemPrivate::canHighlight()
377
381
{
378
 
    // if automatic, the highlight should not happen if we clicked on an active component;
379
 
    // localPos is a position relative to ListItem which will give us a child from
380
 
    // from the original coordinates; therefore we must map the position to the contentItem
381
 
   Q_Q(UCListItem);
382
 
   QPointF myPos = q->mapToItem(contentItem, event->localPos());
383
 
   QQuickItem *child = contentItem->childAt(myPos.x(), myPos.y());
384
 
   bool activeComponent = child && (child->acceptedMouseButtons() & event->button()) &&
385
 
           child->isEnabled() && !qobject_cast<QQuickText*>(child);
386
 
   // do highlight if not pressed above the active component, and the component has either
387
 
   // action, leading or trailing actions list or at least an active child component declared
388
 
   QQuickMouseArea *ma = q->findChild<QQuickMouseArea*>();
389
 
   bool activeMouseArea = ma && ma->isEnabled();
390
 
   return !activeComponent && (isClickedConnected() || isPressAndHoldConnected() ||
391
 
                               mainAction || leadingActions || trailingActions || activeMouseArea);
 
382
   return (isClickedConnected() || isPressAndHoldConnected() || mainAction || leadingActions || trailingActions);
392
383
}
393
384
 
394
385
// set highlighted flag and update contentItem
876
867
 *
877
868
 * \sa ViewItems::dragMode, ViewItems::dragUpdated
878
869
 *
 
870
 * \section2 Expansion
 
871
 * Since Ubuntu.Components 1.3, ListItem supports expansion. ListItems declared
 
872
 * in a view can expand exclusively, having leading and trailing panes locked
 
873
 * when expanded and to be collapsed when tapping outside of the expanded area.
 
874
 * The expansion is driven by the \l expansion group property, and the behavior
 
875
 * by the \l ViewItems::expansionFlags and \l ViewItems::expandedIndices
 
876
 * attached properties. Each ListItem which is required to expand should set a
 
877
 * proper height in the \l expansion.height property, which should be bigger
 
878
 * than the collapsed height of the ListItem is. The expansion itself is driven
 
879
 * by the \l expansion.expanded property, which can be set freely depending on
 
880
 * the use case, on click, on long press, etc.
 
881
 *
 
882
 * The default expansion behavior is set to be exclusive and locked, meaning
 
883
 * there can be only one ListItem expanded within a view and neither leading
 
884
 * nor trailing action panels cannot be swiped in. Expanding an other ListItem
 
885
 * will collapse the previosuly expanded one. There can be cases when tapping
 
886
 * outside of the expanded area of a ListItem we woudl need the expanded one
 
887
 * to collapse automatically. This can be achieved by setting \c ViewItems.CollapseOnOutsidePress
 
888
 * flag to \l ViewItems::expansionFlags. This flag will also turn on \c ViewItems.Exclusive
 
889
 * flag, as tapping outside practicly forbids more than one item to be expanded
 
890
 * at a time.
 
891
 * \qml
 
892
 * import QtQuick 2.4
 
893
 * import Ubuntu.Components 1.3
 
894
 *
 
895
 * ListView {
 
896
 *     width: units.gu(40)
 
897
 *     height: units.gu(71)
 
898
 *     model: ListModel {
 
899
 *         Component.onCompleted: {
 
900
 *             for (var i = 0; i < 50; i++) {
 
901
 *                 append({data: i});
 
902
 *             }
 
903
 *         }
 
904
 *     }
 
905
 *     ViewItems.expansionFlags: ViewItems.CollapseOnOutsidePress
 
906
 *     delegate: ListItem {
 
907
 *         Label {
 
908
 *             text: "Model item #" + modelData
 
909
 *         }
 
910
 *         trailingActions: ListItemActions {
 
911
 *             actions: [
 
912
 *                 Action {
 
913
 *                     icon: "search"
 
914
 *                 },
 
915
 *                 Action {
 
916
 *                     icon: "edit"
 
917
 *                 },
 
918
 *                 Action {
 
919
 *                     icon: "copy"
 
920
 *                 }
 
921
 *             ]
 
922
 *         }
 
923
 *         expansion.height: units.gu(15)
 
924
 *         onClicked: expansion.expanded = true
 
925
 *     }
 
926
 * }
 
927
 * \endqml
 
928
 * The example above collapses the expanded item whenever it is tapped or mouse
 
929
 * pressed outside of the expanded list item.
 
930
 * \note Set 0 to \l ViewItems::expansionFlags if no restrictions on expanded items
 
931
 * is required (i.e multiple expanded items are allowed, swiping leading/trailing
 
932
 * actions when expanded).
 
933
 * \note Do not bind \l expansion.height to the ListItem's height as is will cause
 
934
 * binding loops.
 
935
 *
879
936
 * \section2 Note on styling
880
 
 * ListItem's styling differs from the other component sstyling, as ListItem loads
 
937
 * ListItem's styling differs from the other components styling, as ListItem loads
881
938
 * the style only when either of the leadin/trailing panels are swiped, or when the
882
939
 * item enters in select- or drag mode. The component does not assume any visuals
883
940
 * to be present in the style.
914
971
{
915
972
}
916
973
 
 
974
QObject *UCListItem::attachedViewItems(QObject *object, bool create)
 
975
{
 
976
    return qmlAttachedPropertiesObject<UCViewItemsAttached>(object, create);
 
977
}
 
978
 
917
979
void UCListItem::classBegin()
918
980
{
919
981
    UCStyledItemBase::classBegin();
966
1028
                this, SLOT(_q_syncDragMode()));
967
1029
 
968
1030
        // if selection or drag mode is on, initialize style, with animations turned off
969
 
        if (d->parentAttached->selectMode() || d->parentAttached->dragMode()) {
 
1031
        if (d->parentAttached->selectMode() || d->parentAttached->dragMode() || (d->expansion && d->expansion->expanded())) {
970
1032
            d->loadStyleItem(false);
971
1033
        }
972
1034
        // set the object name for testing purposes
996
1058
        QQuickItem *parentAttachee = data.item;
997
1059
        if (d->flickable && d->flickable->inherits("QQuickListView")) {
998
1060
            // attach to ListView
999
 
            d->parentAttached = static_cast<UCViewItemsAttached*>(qmlAttachedPropertiesObject<UCViewItemsAttached>(d->flickable));
 
1061
            d->parentAttached = static_cast<UCViewItemsAttached*>(attachedViewItems(d->flickable, true));
1000
1062
            parentAttachee = d->flickable;
1001
1063
        } else if (data.item) {
1002
 
            d->parentAttached = static_cast<UCViewItemsAttached*>(qmlAttachedPropertiesObject<UCViewItemsAttached>(data.item));
 
1064
            d->parentAttached = static_cast<UCViewItemsAttached*>(attachedViewItems(data.item, true));
1003
1065
        } else {
1004
1066
            // mark as not ready, so no action should be performed which depends on readyness
1005
1067
            d->ready = false;
1007
1069
            d->parentAttached = 0;
1008
1070
        }
1009
1071
 
 
1072
        if (d->styleItem) {
 
1073
            UCListItemStyle * myStyle = static_cast<UCListItemStyle*>(d->styleItem);
 
1074
            myStyle->updateFlickable(d->flickable);
 
1075
        }
 
1076
 
1010
1077
        if (parentAttachee) {
1011
1078
            QObject::connect(parentAttachee, SIGNAL(widthChanged()), this, SLOT(_q_updateSize()), Qt::DirectConnection);
1012
1079
            // update size
1053
1120
    return oldNode;
1054
1121
}
1055
1122
 
 
1123
// grabs the left mouse button event by turning highlight on, and triggering
 
1124
// swipe events; child items should no longer get mouse events
 
1125
void UCListItemPrivate::grabLeftButtonEvents(QMouseEvent *event)
 
1126
{
 
1127
    Q_Q(UCListItem);
 
1128
    button = event->button();
 
1129
    // create style instance
 
1130
    loadStyleItem();
 
1131
    setHighlighted(true);
 
1132
    lastPos = pressedPos = event->localPos();
 
1133
    // connect the Flickable to know when to rebound
 
1134
    listenToRebind(true);
 
1135
    if (swiped && parentAttached) {
 
1136
        parentAttached->disableInteractive(q, true);
 
1137
    }
 
1138
    // stop any ongoing animation!
 
1139
    swipeEvent(event->localPos(), UCSwipeEvent::Started);
 
1140
}
 
1141
 
1056
1142
void UCListItem::mousePressEvent(QMouseEvent *event)
1057
1143
{
1058
1144
    UCStyledItemBase::mousePressEvent(event);
1062
1148
        // while moving, we cannot select any items
1063
1149
        return;
1064
1150
    }
1065
 
    if (d->canHighlight(event) && !d->highlighted && event->button() == Qt::LeftButton) {
1066
 
        // create style instance
1067
 
        d->loadStyleItem();
1068
 
        d->setHighlighted(true);
1069
 
        d->lastPos = d->pressedPos = event->localPos();
1070
 
        // connect the Flickable to know when to rebound
1071
 
        d->listenToRebind(true);
1072
 
        if (d->swiped && d->parentAttached) {
1073
 
            d->parentAttached->disableInteractive(this, true);
1074
 
        }
1075
 
        // stop any ongoing animation!
1076
 
        d->swipeEvent(event->localPos(), UCSwipeEvent::Started);
 
1151
    if (d->canHighlight() && !d->highlighted && event->button() == Qt::LeftButton) {
 
1152
        d->grabLeftButtonEvents(event);
1077
1153
    }
1078
1154
    // accept the event so we get the rest of the events as well
1079
1155
    event->setAccepted(true);
1125
1201
    d->setHighlighted(false);
1126
1202
}
1127
1203
 
1128
 
void UCListItem::mouseReleaseEvent(QMouseEvent *event)
 
1204
// ungrabs any previously grabbed left mouse button event
 
1205
void UCListItemPrivate::ungrabLeftButtonEvents(QMouseEvent *event)
1129
1206
{
1130
 
    UCStyledItemBase::mouseReleaseEvent(event);
1131
 
    Q_D(UCListItem);
1132
 
    d->button = Qt::NoButton;
 
1207
    Q_Q(UCListItem);
1133
1208
    // set released
1134
 
    if (d->highlighted) {
 
1209
    if (highlighted) {
1135
1210
        // unblock ascending Flickables
1136
 
        d->listenToRebind(false);
1137
 
        if (d->parentAttached) {
1138
 
            d->parentAttached->disableInteractive(this, false);
 
1211
        listenToRebind(false);
 
1212
        if (parentAttached) {
 
1213
            parentAttached->disableInteractive(q, false);
1139
1214
        }
1140
1215
 
1141
 
        if (!d->suppressClick) {
 
1216
        if (!suppressClick) {
1142
1217
            // emit clicked only if not swiped
1143
 
            if (!d->swiped) {
1144
 
                Q_EMIT clicked();
1145
 
                if (d->mainAction) {
1146
 
                    Q_EMIT d->mainAction->trigger(d->index());
 
1218
            if (!swiped) {
 
1219
                Q_EMIT q->clicked();
 
1220
                if (mainAction) {
 
1221
                    Q_EMIT mainAction->trigger(index());
1147
1222
                }
1148
1223
            }
1149
 
            d->snapOut();
 
1224
            snapOut();
1150
1225
        } else {
1151
1226
            // inform style about mouse/touch release
1152
 
            d->swipeEvent(event->localPos(), UCSwipeEvent::Finished);
1153
 
            d->suppressClick = false;
1154
 
            d->setHighlighted(false);
 
1227
            swipeEvent(event->localPos(), UCSwipeEvent::Finished);
 
1228
            suppressClick = false;
 
1229
            setHighlighted(false);
1155
1230
        }
1156
1231
    }
 
1232
    button = Qt::NoButton;
 
1233
}
 
1234
 
 
1235
void UCListItem::mouseReleaseEvent(QMouseEvent *event)
 
1236
{
 
1237
    UCStyledItemBase::mouseReleaseEvent(event);
 
1238
    Q_D(UCListItem);
 
1239
    d->ungrabLeftButtonEvents(event);
 
1240
    // make sure we ungrab the mouse!
 
1241
    ungrabMouse();
1157
1242
}
1158
1243
 
1159
1244
void UCListItem13::mouseReleaseEvent(QMouseEvent *event)
1162
1247
        UCListItem::mouseReleaseEvent(event);
1163
1248
}
1164
1249
 
 
1250
// returns true if the mouse is swiped over the threshold value
 
1251
bool UCListItemPrivate::swipedOverThreshold(const QPointF &mousePos, const QPointF relativePos)
 
1252
{
 
1253
    qreal threshold = UCUnits::instance().gu(xAxisMoveThresholdGU);
 
1254
    qreal mouseX = mousePos.x();
 
1255
    qreal pressedX = relativePos.x();
 
1256
    return ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold)));
 
1257
}
 
1258
 
1165
1259
void UCListItem::mouseMoveEvent(QMouseEvent *event)
1166
1260
{
1167
1261
    Q_D(UCListItem);
1168
1262
    UCStyledItemBase::mouseMoveEvent(event);
1169
1263
 
1170
 
    if (d->selectMode() || d->dragMode()) {
 
1264
    if (d->selectMode() || d->dragMode() || (d->expansion && d->expansion->expandedLocked())) {
1171
1265
        // no move is allowed while selectable mode is on
1172
1266
        return;
1173
1267
    }
1177
1271
    if (d->button == Qt::LeftButton && d->highlighted && !d->swiped && (d->leadingActions || d->trailingActions)) {
1178
1272
        // check if we can initiate the drag at all
1179
1273
        // only X direction matters, if Y-direction leaves the threshold, but X not, the tug is not valid
1180
 
        qreal threshold = UCUnits::instance().gu(d->xAxisMoveThresholdGU);
1181
 
        qreal mouseX = event->localPos().x();
1182
 
        qreal pressedX = d->pressedPos.x();
1183
 
 
1184
 
        if ((mouseX < (pressedX - threshold)) || (mouseX > (pressedX + threshold))) {
 
1274
        if (d->swipedOverThreshold(event->localPos(), d->pressedPos)) {
1185
1275
            // the press went out of the threshold area, enable move, if the direction allows it
1186
1276
            d->lastPos = event->localPos();
1187
1277
            if (d->parentAttached) {
1188
1278
                d->parentAttached->disableInteractive(this, true);
1189
1279
            }
 
1280
            qreal mouseX = event->localPos().x();
 
1281
            qreal pressedX = d->pressedPos.x();
1190
1282
            bool doSwipe = (d->leadingActions && (mouseX > pressedX)) ||
1191
1283
                           (d->trailingActions && (mouseX < pressedX));
1192
1284
            d->setSwiped(doSwipe);
1208
1300
bool UCListItem::childMouseEventFilter(QQuickItem *child, QEvent *event)
1209
1301
{
1210
1302
    QEvent::Type type = event->type();
 
1303
    Q_D(UCListItem);
1211
1304
    if (type == QEvent::MouseButtonPress) {
1212
1305
        // suppress click event if pressed over an active area, except Text, which can also handle
1213
1306
        // mouse clicks when content is an URL
1214
1307
        QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
1215
1308
        if (child->isEnabled() && (child->acceptedMouseButtons() & mouse->button()) && !qobject_cast<QQuickText*>(child)) {
1216
 
            Q_D(UCListItem);
1217
1309
            // suppress click
1218
1310
            d->suppressClick = true;
1219
1311
            // listen for flickable to be able to rebind if movement started there!
1220
1312
            d->listenToRebind(true);
 
1313
            // if left button pressed, remember the position
 
1314
            if (mouse->button() == Qt::LeftButton) {
 
1315
                d->pressedPos = mapFromItem(child, mouse->localPos());
 
1316
                d->button = mouse->button();
 
1317
            }
1221
1318
        }
1222
1319
    } else if (type == QEvent::MouseButtonRelease) {
1223
1320
        Q_D(UCListItem);
1224
1321
        d->suppressClick = false;
 
1322
    } else if (type == QEvent::MouseMove) {
 
1323
        QMouseEvent *mouse = static_cast<QMouseEvent*>(event);
 
1324
        const QPointF localPos = mapFromItem(child, mouse->localPos());
 
1325
        if ((mouse->buttons() & Qt::LeftButton) && d->swipedOverThreshold(localPos, d->pressedPos) && !d->highlighted) {
 
1326
            // grab the event from the child, so the click doesn't happen anymore, and initiate swiping
 
1327
            QMouseEvent pressed(QEvent::MouseButtonPress, localPos, mouse->windowPos(), mouse->screenPos(),
 
1328
                                    Qt::LeftButton, mouse->buttons(), mouse->modifiers());
 
1329
            d->grabLeftButtonEvents(&pressed);
 
1330
            // stop click and pressAndHold, then grab the mouse so children do not get the mouse events anymore
 
1331
            d->suppressClick = true;
 
1332
            d->pressAndHoldTimer.stop();
 
1333
            grabMouse();
 
1334
        }
1225
1335
    }
1226
1336
    return UCStyledItemBase::childMouseEventFilter(child, event);
1227
1337
}
1629
1739
    d->defaultThemeVersion = BUILD_VERSION(1, 3);
1630
1740
}
1631
1741
 
 
1742
QObject *UCListItem13::attachedViewItems(QObject *object, bool create)
 
1743
{
 
1744
    return qmlAttachedPropertiesObject<UCViewItemsAttached13>(object, create);
 
1745
}
 
1746
 
 
1747
void UCListItem13::itemChange(ItemChange change, const ItemChangeData &data)
 
1748
{
 
1749
    UCListItem::itemChange(change, data);
 
1750
 
 
1751
    Q_D(UCListItem);
 
1752
    // ViewItems drives expansion
 
1753
    if (d->parentAttached) {
 
1754
        connect(d->parentAttached.data(), SIGNAL(expandedIndicesChanged(QList<int>)),
 
1755
                this, SLOT(_q_updateExpansion(QList<int>)), Qt::UniqueConnection);
 
1756
    }
 
1757
}
 
1758
 
 
1759
/*!
 
1760
 * \qmlpropertygroup ::ListItem::expansion
 
1761
 * \qmlproperty bool ListItem::expansion.expanded
 
1762
 * \qmlproperty real ListItem::expansion.height
 
1763
 * \since Ubuntu.Components 1.3
 
1764
 * The group drefines the expansion state of the ListItem.
 
1765
 */
 
1766
UCListItemExpansion *UCListItem13::expansion()
 
1767
{
 
1768
    Q_D(UCListItem);
 
1769
    if (!d->expansion) {
 
1770
        d->expansion = new UCListItemExpansion(this);
 
1771
    }
 
1772
    return d->expansion;
 
1773
}
 
1774
 
 
1775
void UCListItem13::_q_updateExpansion(const QList<int> &indices)
 
1776
{
 
1777
    Q_UNUSED(indices);
 
1778
    Q_D(UCListItem);
 
1779
    Q_EMIT expansion()->expandedChanged();
 
1780
    // make sure the style is loaded
 
1781
    if (indices.contains(d->index())) {
 
1782
        d->loadStyleItem();
 
1783
    }
 
1784
}
 
1785
 
1632
1786
#include "moc_uclistitem.cpp"