372
374
(parentItem ? QQuickItemPrivate::get(parentItem)->childItems.indexOf(q) : -1);
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()
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
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);
394
385
// set highlighted flag and update contentItem
877
868
* \sa ViewItems::dragMode, ViewItems::dragUpdated
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.
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
893
* import Ubuntu.Components 1.3
896
* width: units.gu(40)
897
* height: units.gu(71)
899
* Component.onCompleted: {
900
* for (var i = 0; i < 50; i++) {
905
* ViewItems.expansionFlags: ViewItems.CollapseOnOutsidePress
906
* delegate: ListItem {
908
* text: "Model item #" + modelData
910
* trailingActions: ListItemActions {
923
* expansion.height: units.gu(15)
924
* onClicked: expansion.expanded = true
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
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.
966
1028
this, SLOT(_q_syncDragMode()));
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);
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));
1004
1066
// mark as not ready, so no action should be performed which depends on readyness
1005
1067
d->ready = false;
1053
1120
return oldNode;
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)
1128
button = event->button();
1129
// create style instance
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);
1138
// stop any ongoing animation!
1139
swipeEvent(event->localPos(), UCSwipeEvent::Started);
1056
1142
void UCListItem::mousePressEvent(QMouseEvent *event)
1058
1144
UCStyledItemBase::mousePressEvent(event);
1062
1148
// while moving, we cannot select any items
1065
if (d->canHighlight(event) && !d->highlighted && event->button() == Qt::LeftButton) {
1066
// create style instance
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);
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);
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);
1128
void UCListItem::mouseReleaseEvent(QMouseEvent *event)
1204
// ungrabs any previously grabbed left mouse button event
1205
void UCListItemPrivate::ungrabLeftButtonEvents(QMouseEvent *event)
1130
UCStyledItemBase::mouseReleaseEvent(event);
1132
d->button = Qt::NoButton;
1133
1208
// set released
1134
if (d->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);
1141
if (!d->suppressClick) {
1216
if (!suppressClick) {
1142
1217
// emit clicked only if not swiped
1145
if (d->mainAction) {
1146
Q_EMIT d->mainAction->trigger(d->index());
1219
Q_EMIT q->clicked();
1221
Q_EMIT mainAction->trigger(index());
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);
1232
button = Qt::NoButton;
1235
void UCListItem::mouseReleaseEvent(QMouseEvent *event)
1237
UCStyledItemBase::mouseReleaseEvent(event);
1239
d->ungrabLeftButtonEvents(event);
1240
// make sure we ungrab the mouse!
1159
1244
void UCListItem13::mouseReleaseEvent(QMouseEvent *event)
1162
1247
UCListItem::mouseReleaseEvent(event);
1250
// returns true if the mouse is swiped over the threshold value
1251
bool UCListItemPrivate::swipedOverThreshold(const QPointF &mousePos, const QPointF relativePos)
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)));
1165
1259
void UCListItem::mouseMoveEvent(QMouseEvent *event)
1167
1261
Q_D(UCListItem);
1168
1262
UCStyledItemBase::mouseMoveEvent(event);
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
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();
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);
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)
1210
1302
QEvent::Type type = event->type();
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)) {
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();
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();
1226
1336
return UCStyledItemBase::childMouseEventFilter(child, event);
1629
1739
d->defaultThemeVersion = BUILD_VERSION(1, 3);
1742
QObject *UCListItem13::attachedViewItems(QObject *object, bool create)
1744
return qmlAttachedPropertiesObject<UCViewItemsAttached13>(object, create);
1747
void UCListItem13::itemChange(ItemChange change, const ItemChangeData &data)
1749
UCListItem::itemChange(change, data);
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);
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.
1766
UCListItemExpansion *UCListItem13::expansion()
1769
if (!d->expansion) {
1770
d->expansion = new UCListItemExpansion(this);
1772
return d->expansion;
1775
void UCListItem13::_q_updateExpansion(const QList<int> &indices)
1779
Q_EMIT expansion()->expandedChanged();
1780
// make sure the style is loaded
1781
if (indices.contains(d->index())) {
1632
1786
#include "moc_uclistitem.cpp"