2
* Copyright (C) 2012-2014 Canonical, Ltd.
4
* This program is free software; you can redistribute it and/or modify
5
* it under the terms of the GNU General Public License as published by
6
* the Free Software Foundation; version 3.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU General Public License for more details.
13
* You should have received a copy of the GNU General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19
import Ubuntu.Components 1.1
20
import Ubuntu.Components.ListItems 1.0 as ListItem
25
property list<Action> leftSideActions
26
property list<Action> rightSideActions
27
property double defaultHeight: units.gu(8)
28
property bool locked: false
29
property Action activeAction: null
30
property var activeItem: null
31
property bool triggerActionOnMouseRelease: false
32
property color color: Theme.palette.normal.background
33
property color selectedColor: "#E6E6E6"
34
property bool selected: false
35
property bool selectionMode: false
36
property alias pressed: mouseArea.pressed
37
property alias showDivider: divider.visible
38
property alias internalAnchors: mainContents.anchors
39
default property alias contents: mainContents.children
41
readonly property double actionWidth: units.gu(4)
42
readonly property double actionThreshold: actionWidth * 0.4
43
readonly property double threshold: actionWidth * 0.8
44
readonly property string swipeState: main.x == 0 ? "Normal" : main.x > 0 ? "LeftToRight" : "RightToLeft"
45
readonly property alias swipping: mainItemMoving.running
46
readonly property bool _showActions: mouseArea.pressed || swipeState != "Normal" || swipping
49
property var _visibleLeftSideActions: filterVisibleActions(leftSideActions)
50
property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
52
/* Remember to mouse.accepted = true when managing this component */
53
signal itemClicked(var mouse)
54
signal itemPressAndHold(var mouse)
56
function returnToBoundsRTL(direction) {
57
var actionFullWidth = actionWidth + units.gu(2)
59
// go back to normal state if swipping reverse
60
if (direction === "LTR") {
63
} else if (!triggerActionOnMouseRelease) {
64
updatePosition(-rightActionsView.width + units.gu(2))
68
var xOffset = Math.abs(main.x)
69
var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
71
if (index === _visibleRightSideActions.length) {
72
newX = -(rightActionsView.width - units.gu(2))
73
} else if (index >= 1) {
74
newX = -(actionFullWidth * index)
79
function returnToBoundsLTR(direction) {
80
var actionFullWidth = actionWidth + units.gu(2)
82
// go back to normal state if swipping reverse
83
if (direction === "RTL") {
86
} else if (!triggerActionOnMouseRelease) {
87
updatePosition(leftActionView.width - units.gu(2))
91
var xOffset = Math.abs(main.x)
92
var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleLeftSideActions.length)
94
if (index === _visibleLeftSideActions.length) {
95
newX = leftActionView.width - units.gu(2)
96
} else if (index >= 1) {
97
newX = actionFullWidth * index
102
function returnToBounds(direction) {
104
returnToBoundsRTL(direction)
105
} else if (main.x > 0) {
106
returnToBoundsLTR(direction)
112
function contains(item, point, marginX) {
113
var itemStartX = item.x - marginX
114
var itemEndX = item.x + item.width + marginX
115
return (point.x >= itemStartX) && (point.x <= itemEndX) &&
116
(point.y >= item.y) && (point.y <= (item.y + item.height));
119
function getActionAt(point) {
120
if (contains(leftActionView, point, 0)) {
121
var newPoint = root.mapToItem(leftActionView, point.x, point.y)
122
for (var i = 0; i < leftActionsRepeater.count; i++) {
123
var child = leftActionsRepeater.itemAt(i)
124
if (contains(child, newPoint, units.gu(3))) {
128
} else if (contains(rightActionsView, point, 0)) {
129
var newPoint = root.mapToItem(rightActionsView, point.x, point.y)
130
for (var i = 0; i < rightActionsRepeater.count; i++) {
131
var child = rightActionsRepeater.itemAt(i)
132
if (contains(child, newPoint, units.gu(3))) {
140
function updateActiveAction() {
141
if (triggerActionOnMouseRelease &&
142
(main.x <= -(root.actionWidth + units.gu(2))) &&
143
(main.x > -(rightActionsView.width - units.gu(2)))) {
144
var actionFullWidth = actionWidth + units.gu(2)
145
var xOffset = Math.abs(main.x)
146
var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
149
root.activeItem = rightActionsRepeater.itemAt(index)
150
root.activeAction = root._visibleRightSideActions[index]
153
root.activeAction = null
157
function resetSwipe() {
161
function filterVisibleActions(actions) {
162
var visibleActions = []
163
for(var i = 0; i < actions.length; i++) {
164
var action = actions[i]
165
if (action.visible) {
166
visibleActions.push(action)
169
return visibleActions
172
function updatePosition(pos) {
173
if (!root.triggerActionOnMouseRelease && (pos !== 0)) {
174
mouseArea.state = pos > 0 ? "RightToLeft" : "LeftToRight"
184
when: selectionMode || selected
186
target: selectionIcon
187
source: Qt.resolvedUrl("ListItemWithActionsCheckBox.qml")
188
anchors.leftMargin: units.gu(2)
201
height: defaultHeight
202
clip: height !== defaultHeight
218
bottom: parent.bottom
221
visible: _visibleLeftSideActions.length > 0
222
width: leftActionsRepeater.count > 0 ? leftActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(3) : 0
223
color: "#eef32c36" // UbuntuColors.red with alpha
228
bottom: parent.bottom
230
rightMargin: units.gu(1)
234
id: leftActionsRepeater
236
model: _showActions ? _visibleLeftSideActions : []
238
property alias image: img
240
height: leftActionView.height
241
width: root.actionWidth + units.gu(2)
246
anchors.centerIn: parent
249
name: modelData.iconName
265
visible: _visibleRightSideActions.length > 0
266
width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(3) : 0
271
bottom: parent.bottom
273
leftMargin: units.gu(1)
277
id: rightActionsRepeater
279
model: _showActions ? _visibleRightSideActions : []
281
property alias image: img
283
height: rightActionsView.height
284
width: root.actionWidth + units.gu(2)
289
anchors.centerIn: parent
292
name: modelData.iconName
293
color: root.activeAction === modelData ? UbuntuColors.lightAubergine : UbuntuColors.lightGrey
302
objectName: "mainItem"
306
bottom: parent.bottom
310
color: root.selected ? root.selectedColor : root.color
317
verticalCenter: main.verticalCenter
319
width: (status === Loader.Ready) ? item.implicitWidth : 0
320
visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
323
duration: UbuntuAnimation.SnapDuration
332
left: selectionIcon.right
334
topMargin: units.gu(1)
336
bottom: parent.bottom
337
bottomMargin: units.gu(1)
342
UbuntuNumberAnimation {
345
easing.type: Easing.OutElastic
346
duration: UbuntuAnimation.SlowDuration
355
SequentialAnimation {
358
property var currentItem: root.activeItem ? root.activeItem.image : null
362
UbuntuNumberAnimation {
363
target: triggerAction.currentItem
367
duration: UbuntuAnimation.SlowDuration
368
easing { type: Easing.InOutBack; }
370
UbuntuNumberAnimation {
371
target: triggerAction.currentItem
372
properties: "width, height"
375
duration: UbuntuAnimation.SlowDuration
376
easing { type: Easing.InOutBack; }
380
target: triggerAction.currentItem
381
properties: "width, height"
385
target: triggerAction.currentItem
386
properties: "opacity"
391
root.activeAction.triggered(root)
398
UbuntuNumberAnimation {
408
propagateComposedEvents: true
409
property bool locked: root.locked || ((root._visibleLeftSideActions === null) && (root._visibleRightSideActions.count === 0))
410
property bool manual: false
411
property string direction: "None"
412
property real lastX: -1
418
target: locked ? null : main
420
minimumX: rightActionsView.visible ? -(rightActionsView.width) : 0
421
maximumX: leftActionView.visible ? leftActionView.width : 0
422
threshold: root.threshold
443
var offset = (lastX - mouseX)
444
if (Math.abs(offset) <= root.threshold) {
448
direction = offset > 0 ? "RTL" : "LTR";
456
if (root.triggerActionOnMouseRelease && root.activeAction) {
457
triggerAction.start()
459
root.returnToBounds(direction)
460
root.activeAction = null
467
mouse.accepted = false
470
root.itemClicked(mouse)
471
} else if (main.x > 0) {
472
var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
473
if (actionIndex !== -1) {
474
root.activeItem = leftActionsRepeater.itemAt(actionIndex)
475
root.activeAction = root._visibleLeftSideActions[actionIndex]
476
triggerAction.start()
480
var actionIndex = getActionAt(Qt.point(mouse.x, mouse.y))
481
if (actionIndex !== -1) {
482
root.activeItem = rightActionsRepeater.itemAt(actionIndex)
483
root.activeAction = root._visibleRightSideActions[actionIndex]
484
triggerAction.start()
492
if (mouseArea.pressed) {
499
root.itemPressAndHold(mouse)
504
ListItem.ThinDivider {
507
width: parent.width + units.gu(4)
511
bottom: parent.bottom
512
bottomMargin: units.gu(-0.1)