~mzanetti/telegram-app/reply

« back to all changes in this revision

Viewing changes to telegram/app/qml/components/listitems/ListItemWithActions.qml

  • Committer: Michael Zanetti
  • Date: 2016-02-29 22:46:12 UTC
  • Revision ID: michael.zanetti@canonical.com-20160229224612-a8vm6nvk4yl9mhp9
update MessageListItem to uitk ListItem

This replaces the last occurance of the custom ListItemWithAction
so that one can be dropped. This seems to help a little with
scrolling performance and also fixes some listview contentY placemtn
issues.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2012-2014 Canonical, Ltd.
3
 
 *
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.
7
 
 *
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.
12
 
 *
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/>.
15
 
 */
16
 
 
17
 
import QtQuick 2.2
18
 
import QtFeedback 5.0
19
 
import Ubuntu.Components 1.1
20
 
import Ubuntu.Components.ListItems 1.0 as ListItem
21
 
 
22
 
Item {
23
 
    id: root
24
 
 
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
40
 
 
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
47
 
 
48
 
    /* internal */
49
 
    property var _visibleLeftSideActions: filterVisibleActions(leftSideActions)
50
 
    property var _visibleRightSideActions: filterVisibleActions(rightSideActions)
51
 
 
52
 
    /* Remember to mouse.accepted = true when managing this component */
53
 
    signal itemClicked(var mouse)
54
 
    signal itemPressAndHold(var mouse)
55
 
 
56
 
    function returnToBoundsRTL(direction) {
57
 
        var actionFullWidth = actionWidth + units.gu(2)
58
 
 
59
 
        // go back to normal state if swipping reverse
60
 
        if (direction === "LTR") {
61
 
            updatePosition(0)
62
 
            return
63
 
        } else if (!triggerActionOnMouseRelease) {
64
 
            updatePosition(-rightActionsView.width + units.gu(2))
65
 
            return
66
 
        }
67
 
 
68
 
        var xOffset = Math.abs(main.x)
69
 
        var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleRightSideActions.length)
70
 
        var newX = 0
71
 
        if (index === _visibleRightSideActions.length) {
72
 
            newX = -(rightActionsView.width - units.gu(2))
73
 
        } else if (index >= 1) {
74
 
            newX = -(actionFullWidth * index)
75
 
        }
76
 
        updatePosition(newX)
77
 
    }
78
 
 
79
 
    function returnToBoundsLTR(direction) {
80
 
        var actionFullWidth = actionWidth + units.gu(2)
81
 
 
82
 
        // go back to normal state if swipping reverse
83
 
        if (direction === "RTL") {
84
 
            updatePosition(0)
85
 
            return
86
 
        } else if (!triggerActionOnMouseRelease) {
87
 
            updatePosition(leftActionView.width - units.gu(2))
88
 
            return
89
 
        }
90
 
 
91
 
        var xOffset = Math.abs(main.x)
92
 
        var index = Math.min(Math.floor(xOffset / actionFullWidth), _visibleLeftSideActions.length)
93
 
        var newX = 0
94
 
        if (index === _visibleLeftSideActions.length) {
95
 
            newX = leftActionView.width - units.gu(2)
96
 
        } else if (index >= 1) {
97
 
            newX = actionFullWidth * index
98
 
        }
99
 
        updatePosition(newX)
100
 
    }
101
 
 
102
 
    function returnToBounds(direction) {
103
 
        if (main.x < 0) {
104
 
            returnToBoundsRTL(direction)
105
 
        } else if (main.x > 0) {
106
 
            returnToBoundsLTR(direction)
107
 
        } else {
108
 
            updatePosition(0)
109
 
        }
110
 
    }
111
 
 
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));
117
 
    }
118
 
 
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))) {
125
 
                    return i
126
 
                }
127
 
            }
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))) {
133
 
                    return i
134
 
                }
135
 
            }
136
 
        }
137
 
        return -1
138
 
    }
139
 
 
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)
147
 
            index = index - 1
148
 
            if (index > -1) {
149
 
                root.activeItem = rightActionsRepeater.itemAt(index)
150
 
                root.activeAction = root._visibleRightSideActions[index]
151
 
            }
152
 
        } else {
153
 
            root.activeAction = null
154
 
        }
155
 
    }
156
 
 
157
 
    function resetSwipe() {
158
 
        main.x = 0
159
 
    }
160
 
 
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)
167
 
            }
168
 
        }
169
 
        return visibleActions
170
 
    }
171
 
 
172
 
    function updatePosition(pos) {
173
 
        if (!root.triggerActionOnMouseRelease && (pos !== 0)) {
174
 
            mouseArea.state = pos > 0 ? "RightToLeft" : "LeftToRight"
175
 
        } else {
176
 
            mouseArea.state = ""
177
 
        }
178
 
        main.x = pos
179
 
    }
180
 
 
181
 
    states: [
182
 
        State {
183
 
            name: "select"
184
 
            when: selectionMode || selected
185
 
            PropertyChanges {
186
 
                target: selectionIcon
187
 
                source: Qt.resolvedUrl("ListItemWithActionsCheckBox.qml")
188
 
                anchors.leftMargin: units.gu(2)
189
 
            }
190
 
            PropertyChanges {
191
 
                target: root
192
 
                locked: true
193
 
            }
194
 
            PropertyChanges {
195
 
                target: main
196
 
                x: 0
197
 
            }
198
 
        }
199
 
    ]
200
 
 
201
 
    height: defaultHeight
202
 
    clip: height !== defaultHeight
203
 
    HapticsEffect {
204
 
        id: clickEffect
205
 
        attackIntensity: 0.0
206
 
        attackTime: 50
207
 
        intensity: 1.0
208
 
        duration: 10
209
 
        fadeTime: 50
210
 
        fadeIntensity: 0.0
211
 
    }
212
 
 
213
 
    Rectangle {
214
 
        id: leftActionView
215
 
 
216
 
        anchors {
217
 
            top: parent.top
218
 
            bottom: parent.bottom
219
 
            right: main.left
220
 
        }
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
224
 
 
225
 
        Row {
226
 
            anchors{
227
 
                top: parent.top
228
 
                bottom: parent.bottom
229
 
                right: parent.right
230
 
                rightMargin: units.gu(1)
231
 
            }
232
 
            spacing: 0
233
 
            Repeater {
234
 
                id: leftActionsRepeater
235
 
 
236
 
                model: _showActions ? _visibleLeftSideActions : []
237
 
                Item {
238
 
                    property alias image: img
239
 
 
240
 
                    height: leftActionView.height
241
 
                    width: root.actionWidth + units.gu(2)
242
 
 
243
 
                    Icon {
244
 
                        id: img
245
 
 
246
 
                        anchors.centerIn: parent
247
 
                        width: units.gu(3)
248
 
                        height: units.gu(3)
249
 
                        name: modelData.iconName
250
 
                        color: "white"
251
 
                    }
252
 
                }
253
 
            }
254
 
        }
255
 
    }
256
 
 
257
 
    Rectangle {
258
 
        id: rightActionsView
259
 
 
260
 
        anchors {
261
 
            top: main.top
262
 
            left: main.right
263
 
            bottom: main.bottom
264
 
        }
265
 
        visible: _visibleRightSideActions.length > 0
266
 
        width: rightActionsRepeater.count > 0 ? rightActionsRepeater.count * (root.actionWidth + units.gu(2)) + root.actionThreshold + units.gu(3) : 0
267
 
        color: "white"
268
 
        Row {
269
 
            anchors{
270
 
                top: parent.top
271
 
                bottom: parent.bottom
272
 
                left: parent.left
273
 
                leftMargin: units.gu(1)
274
 
            }
275
 
            spacing: 0
276
 
            Repeater {
277
 
                id: rightActionsRepeater
278
 
 
279
 
                model: _showActions ? _visibleRightSideActions : []
280
 
                Item {
281
 
                    property alias image: img
282
 
 
283
 
                    height: rightActionsView.height
284
 
                    width: root.actionWidth + units.gu(2)
285
 
 
286
 
                    Icon {
287
 
                        id: img
288
 
 
289
 
                        anchors.centerIn: parent
290
 
                        width: units.gu(3)
291
 
                        height: units.gu(3)
292
 
                        name: modelData.iconName
293
 
                        color: root.activeAction === modelData ? UbuntuColors.lightAubergine : UbuntuColors.lightGrey
294
 
                    }
295
 
                }
296
 
            }
297
 
        }
298
 
    }
299
 
 
300
 
    Rectangle {
301
 
        id: main
302
 
        objectName: "mainItem"
303
 
 
304
 
        anchors {
305
 
            top: parent.top
306
 
            bottom: parent.bottom
307
 
        }
308
 
 
309
 
        width: parent.width
310
 
        color: root.selected ? root.selectedColor : root.color
311
 
 
312
 
        Loader {
313
 
            id: selectionIcon
314
 
 
315
 
            anchors {
316
 
                left: main.left
317
 
                verticalCenter: main.verticalCenter
318
 
            }
319
 
            width: (status === Loader.Ready) ? item.implicitWidth : 0
320
 
            visible: (status === Loader.Ready) && (item.width === item.implicitWidth)
321
 
            Behavior on width {
322
 
                NumberAnimation {
323
 
                    duration: UbuntuAnimation.SnapDuration
324
 
                }
325
 
            }
326
 
        }
327
 
 
328
 
        Item {
329
 
            id: mainContents
330
 
 
331
 
            anchors {
332
 
                left: selectionIcon.right
333
 
                top: parent.top
334
 
                topMargin: units.gu(1)
335
 
                right: parent.right
336
 
                bottom: parent.bottom
337
 
                bottomMargin: units.gu(1)
338
 
            }
339
 
        }
340
 
 
341
 
        Behavior on x {
342
 
            UbuntuNumberAnimation {
343
 
                id: mainItemMoving
344
 
 
345
 
                easing.type: Easing.OutElastic
346
 
                duration: UbuntuAnimation.SlowDuration
347
 
            }
348
 
        }
349
 
 
350
 
        Behavior on color {
351
 
            ColorAnimation {}
352
 
        }
353
 
    }
354
 
 
355
 
    SequentialAnimation {
356
 
        id: triggerAction
357
 
 
358
 
        property var currentItem: root.activeItem ? root.activeItem.image : null
359
 
 
360
 
        running: false
361
 
        ParallelAnimation {
362
 
            UbuntuNumberAnimation {
363
 
                target: triggerAction.currentItem
364
 
                property: "opacity"
365
 
                from: 1.0
366
 
                to: 0.0
367
 
                duration: UbuntuAnimation.SlowDuration
368
 
                easing { type: Easing.InOutBack; }
369
 
            }
370
 
            UbuntuNumberAnimation {
371
 
                target: triggerAction.currentItem
372
 
                properties: "width, height"
373
 
                from: units.gu(3)
374
 
                to: root.actionWidth
375
 
                duration: UbuntuAnimation.SlowDuration
376
 
                easing { type: Easing.InOutBack; }
377
 
            }
378
 
        }
379
 
        PropertyAction {
380
 
            target: triggerAction.currentItem
381
 
            properties: "width, height"
382
 
            value: units.gu(3)
383
 
        }
384
 
        PropertyAction {
385
 
            target: triggerAction.currentItem
386
 
            properties: "opacity"
387
 
            value: 1.0
388
 
        }
389
 
        ScriptAction {
390
 
            script: {
391
 
                root.activeAction.triggered(root)
392
 
                mouseArea.state = ""
393
 
            }
394
 
        }
395
 
        PauseAnimation {
396
 
            duration: 500
397
 
        }
398
 
        UbuntuNumberAnimation {
399
 
            target: main
400
 
            property: "x"
401
 
            to: 0
402
 
        }
403
 
    }
404
 
 
405
 
    MouseArea {
406
 
        id: mouseArea
407
 
 
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
413
 
 
414
 
        anchors.fill: parent
415
 
        z: -100
416
 
 
417
 
        drag {
418
 
            target: locked ? null : main
419
 
            axis: Drag.XAxis
420
 
            minimumX: rightActionsView.visible ? -(rightActionsView.width) : 0
421
 
            maximumX: leftActionView.visible ? leftActionView.width : 0
422
 
            threshold: root.threshold
423
 
        }
424
 
 
425
 
        states: [
426
 
            State {
427
 
                name: "LeftToRight"
428
 
                PropertyChanges {
429
 
                    target: mouseArea
430
 
                    drag.maximumX: 0
431
 
                }
432
 
            },
433
 
            State {
434
 
                name: "RightToLeft"
435
 
                PropertyChanges {
436
 
                    target: mouseArea
437
 
                    drag.minimumX: 0
438
 
                }
439
 
            }
440
 
        ]
441
 
 
442
 
        onMouseXChanged: {
443
 
            var offset = (lastX - mouseX)
444
 
            if (Math.abs(offset) <= root.threshold) {
445
 
                return
446
 
            }
447
 
            lastX = mouseX
448
 
            direction = offset > 0 ? "RTL" : "LTR";
449
 
        }
450
 
 
451
 
        onPressed: {
452
 
            lastX = mouse.x
453
 
        }
454
 
 
455
 
        onReleased: {
456
 
            if (root.triggerActionOnMouseRelease && root.activeAction) {
457
 
                triggerAction.start()
458
 
            } else {
459
 
                root.returnToBounds(direction)
460
 
                root.activeAction = null
461
 
            }
462
 
            lastX = -1
463
 
            direction = "None"
464
 
        }
465
 
 
466
 
        onClicked: {
467
 
            mouse.accepted = false
468
 
            clickEffect.start()
469
 
            if (main.x === 0) {
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()
477
 
                    return
478
 
                }
479
 
            } else {
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()
485
 
                    return
486
 
                }
487
 
            }
488
 
            root.resetSwipe()
489
 
        }
490
 
 
491
 
        onPositionChanged: {
492
 
            if (mouseArea.pressed) {
493
 
                updateActiveAction()
494
 
            }
495
 
        }
496
 
 
497
 
        onPressAndHold: {
498
 
            if (main.x === 0) {
499
 
                root.itemPressAndHold(mouse)
500
 
            }
501
 
        }
502
 
    }
503
 
 
504
 
    ListItem.ThinDivider {
505
 
        id: divider
506
 
        visible: false
507
 
        width: parent.width + units.gu(4)
508
 
        anchors {
509
 
            left: parent.left
510
 
            right: parent.right
511
 
            bottom: parent.bottom
512
 
            bottomMargin: units.gu(-0.1)
513
 
        }
514
 
    }
515
 
}