~unity-team/unity8/dash-only

« back to all changes in this revision

Viewing changes to qml/Panel/IndicatorsMenu.qml

  • Committer: Kevin Gunn
  • Date: 2016-10-24 19:51:33 UTC
  • Revision ID: kevin.gunn@canonical.com-20161024195133-61lwdzzdwsnue1mn
shave some more

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2014-2016 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.4
18
 
import Ubuntu.Components 1.3
19
 
import Ubuntu.Gestures 0.1
20
 
import "../Components"
21
 
import "Indicators"
22
 
 
23
 
Showable {
24
 
    id: root
25
 
    property alias indicatorsModel: bar.indicatorsModel
26
 
    property alias showDragHandle: __showDragHandle
27
 
    property alias hideDragHandle: __hideDragHandle
28
 
    property alias overFlowWidth: bar.overFlowWidth
29
 
    property alias verticalVelocityThreshold: yVelocityCalculator.velocityThreshold
30
 
    property alias currentIndicator: bar.currentIndicator
31
 
    property int minimizedPanelHeight: units.gu(3)
32
 
    property int expandedPanelHeight: units.gu(7)
33
 
    property real openedHeight: units.gu(71)
34
 
    readonly property real unitProgress: Math.max(0, (height - minimizedPanelHeight) / (openedHeight - minimizedPanelHeight))
35
 
    readonly property bool fullyOpened: unitProgress >= 1
36
 
    readonly property bool partiallyOpened: unitProgress > 0 && unitProgress < 1.0
37
 
    readonly property bool fullyClosed: unitProgress == 0
38
 
    property bool enableHint: true
39
 
    property bool showOnClick: true
40
 
    property color panelColor: theme.palette.normal.background
41
 
 
42
 
    signal showTapped()
43
 
 
44
 
    // TODO: Perhaps we need a animation standard for showing/hiding? Each showable seems to
45
 
    // use its own values. Need to ask design about this.
46
 
    showAnimation: SequentialAnimation {
47
 
        StandardAnimation {
48
 
            target: root
49
 
            property: "height"
50
 
            to: openedHeight
51
 
            duration: UbuntuAnimation.BriskDuration
52
 
            easing.type: Easing.OutCubic
53
 
        }
54
 
        // set binding in case units.gu changes while menu open, so height correctly adjusted to fit
55
 
        ScriptAction { script: root.height = Qt.binding( function(){ return root.openedHeight; } ) }
56
 
    }
57
 
 
58
 
    hideAnimation: SequentialAnimation {
59
 
        StandardAnimation {
60
 
            target: root
61
 
            property: "height"
62
 
            to: minimizedPanelHeight
63
 
            duration: UbuntuAnimation.BriskDuration
64
 
            easing.type: Easing.OutCubic
65
 
        }
66
 
        // set binding in case units.gu changes while menu closed, so menu adjusts to fit
67
 
        ScriptAction { script: root.height = Qt.binding( function(){ return root.minimizedPanelHeight; } ) }
68
 
    }
69
 
 
70
 
    height: minimizedPanelHeight
71
 
 
72
 
    onUnitProgressChanged: d.updateState()
73
 
    clip: root.partiallyOpened
74
 
 
75
 
    IndicatorsLight {
76
 
        id: indicatorLights
77
 
    }
78
 
 
79
 
    // eater
80
 
    MouseArea {
81
 
        anchors.fill: parent
82
 
        hoverEnabled: true
83
 
        acceptedButtons: Qt.AllButtons
84
 
        onWheel: wheel.accepted = true;
85
 
    }
86
 
 
87
 
    MenuContent {
88
 
        id: content
89
 
        objectName: "menuContent"
90
 
 
91
 
        anchors {
92
 
            left: parent.left
93
 
            right: parent.right
94
 
            top: bar.bottom
95
 
        }
96
 
        height: openedHeight - bar.height - handle.height
97
 
        indicatorsModel: root.indicatorsModel
98
 
        visible: root.unitProgress > 0
99
 
        currentMenuIndex: bar.currentItemIndex
100
 
    }
101
 
 
102
 
    Handle {
103
 
        id: handle
104
 
        objectName: "handle"
105
 
        anchors {
106
 
            left: parent.left
107
 
            right: parent.right
108
 
            bottom: parent.bottom
109
 
        }
110
 
        height: units.gu(2)
111
 
        active: d.activeDragHandle ? true : false
112
 
 
113
 
        //small shadow gradient at bottom of menu
114
 
        Rectangle {
115
 
            anchors {
116
 
                left: parent.left
117
 
                right: parent.right
118
 
                bottom: parent.top
119
 
            }
120
 
            height: units.gu(0.5)
121
 
            gradient: Gradient {
122
 
                GradientStop { position: 0.0; color: "transparent" }
123
 
                GradientStop { position: 1.0; color: theme.palette.normal.background }
124
 
            }
125
 
            opacity: 0.3
126
 
        }
127
 
    }
128
 
 
129
 
    Rectangle {
130
 
        anchors.fill: bar
131
 
        color: panelColor
132
 
    }
133
 
 
134
 
    IndicatorsBar {
135
 
        id: bar
136
 
        objectName: "indicatorsBar"
137
 
 
138
 
        anchors {
139
 
            left: parent.left
140
 
            right: parent.right
141
 
        }
142
 
        expanded: false
143
 
        enableLateralChanges: false
144
 
        lateralPosition: -1
145
 
        unitProgress: root.unitProgress
146
 
 
147
 
        height: expanded ? expandedPanelHeight : minimizedPanelHeight
148
 
        Behavior on height { NumberAnimation { duration: UbuntuAnimation.SnapDuration; easing: UbuntuAnimation.StandardEasing } }
149
 
    }
150
 
 
151
 
    ScrollCalculator {
152
 
        id: leftScroller
153
 
        width: units.gu(5)
154
 
        anchors.left: bar.left
155
 
        height: bar.height
156
 
 
157
 
        forceScrollingPercentage: 0.33
158
 
        stopScrollThreshold: units.gu(0.75)
159
 
        direction: Qt.RightToLeft
160
 
        lateralPosition: -1
161
 
 
162
 
        onScroll: bar.addScrollOffset(-scrollAmount);
163
 
    }
164
 
 
165
 
    ScrollCalculator {
166
 
        id: rightScroller
167
 
        width: units.gu(5)
168
 
        anchors.right: bar.right
169
 
        height: bar.height
170
 
 
171
 
        forceScrollingPercentage: 0.33
172
 
        stopScrollThreshold: units.gu(0.75)
173
 
        direction: Qt.LeftToRight
174
 
        lateralPosition: -1
175
 
 
176
 
        onScroll: bar.addScrollOffset(scrollAmount);
177
 
    }
178
 
 
179
 
    MouseArea {
180
 
        anchors.bottom: parent.bottom
181
 
        anchors.left: parent.left
182
 
        anchors.right: parent.right
183
 
        height: minimizedPanelHeight
184
 
        enabled: __showDragHandle.enabled && showOnClick
185
 
        onClicked: {
186
 
            bar.selectItemAt(mouseX)
187
 
            root.show()
188
 
        }
189
 
    }
190
 
 
191
 
    DragHandle {
192
 
        id: __showDragHandle
193
 
        objectName: "showDragHandle"
194
 
        anchors.bottom: parent.bottom
195
 
        anchors.left: parent.left
196
 
        anchors.right: parent.right
197
 
        height: minimizedPanelHeight
198
 
        direction: Direction.Downwards
199
 
        enabled: !root.shown && root.available
200
 
        autoCompleteDragThreshold: maxTotalDragDistance / 2
201
 
        stretch: true
202
 
 
203
 
        onPressedChanged: {
204
 
            if (pressed) {
205
 
                touchPressTime = new Date().getTime();
206
 
            } else {
207
 
                var touchReleaseTime = new Date().getTime();
208
 
                if (touchReleaseTime - touchPressTime <= 300) {
209
 
                    root.showTapped();
210
 
                }
211
 
            }
212
 
        }
213
 
        property var touchPressTime
214
 
 
215
 
        // using hint regulates minimum to hint displacement, but in fullscreen mode, we need to do it manually.
216
 
        overrideStartValue: enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height
217
 
        maxTotalDragDistance: openedHeight - (enableHint ? minimizedPanelHeight : expandedPanelHeight + handle.height)
218
 
        hintDisplacement: enableHint ? expandedPanelHeight - minimizedPanelHeight + handle.height : 0
219
 
    }
220
 
 
221
 
    MouseArea {
222
 
        anchors.fill: __hideDragHandle
223
 
        enabled: __hideDragHandle.enabled
224
 
        onClicked: root.hide()
225
 
    }
226
 
 
227
 
    DragHandle {
228
 
        id: __hideDragHandle
229
 
        objectName: "hideDragHandle"
230
 
        anchors.fill: handle
231
 
        direction: Direction.Upwards
232
 
        enabled: root.shown && root.available
233
 
        hintDisplacement: units.gu(3)
234
 
        autoCompleteDragThreshold: maxTotalDragDistance / 6
235
 
        stretch: true
236
 
        maxTotalDragDistance: openedHeight - expandedPanelHeight - handle.height
237
 
 
238
 
        onTouchPositionChanged: {
239
 
            if (root.state === "locked") {
240
 
                d.xDisplacementSinceLock += (touchPosition.x - d.lastHideTouchX)
241
 
                d.lastHideTouchX = touchPosition.x;
242
 
            }
243
 
        }
244
 
    }
245
 
 
246
 
    PanelVelocityCalculator {
247
 
        id: yVelocityCalculator
248
 
        velocityThreshold: d.hasCommitted ? 0.1 : 0.3
249
 
        trackedValue: d.activeDragHandle ?
250
 
                            (Direction.isPositive(d.activeDragHandle.direction) ?
251
 
                                    d.activeDragHandle.distance :
252
 
                                    -d.activeDragHandle.distance)
253
 
                            : 0
254
 
 
255
 
        onVelocityAboveThresholdChanged: d.updateState()
256
 
    }
257
 
 
258
 
    Connections {
259
 
        target: showAnimation
260
 
        onRunningChanged: {
261
 
            if (showAnimation.running) {
262
 
                root.state = "commit";
263
 
            }
264
 
        }
265
 
    }
266
 
 
267
 
    Connections {
268
 
        target: hideAnimation
269
 
        onRunningChanged: {
270
 
            if (hideAnimation.running) {
271
 
                root.state = "initial";
272
 
            }
273
 
        }
274
 
    }
275
 
 
276
 
    QtObject {
277
 
        id: d
278
 
        property var activeDragHandle: showDragHandle.dragging ? showDragHandle : hideDragHandle.dragging ? hideDragHandle : null
279
 
        property bool hasCommitted: false
280
 
        property real lastHideTouchX: 0
281
 
        property real xDisplacementSinceLock: 0
282
 
        onXDisplacementSinceLockChanged: d.updateState()
283
 
 
284
 
        property real rowMappedLateralPosition: {
285
 
            if (!d.activeDragHandle) return -1;
286
 
            return d.activeDragHandle.mapToItem(bar, d.activeDragHandle.touchPosition.x, 0).x;
287
 
        }
288
 
 
289
 
        function updateState() {
290
 
            if (!showAnimation.running && !hideAnimation.running && d.activeDragHandle) {
291
 
                if (unitProgress <= 0) {
292
 
                    root.state = "initial";
293
 
                // lock indicator if we've been committed and aren't moving too much laterally or too fast up.
294
 
                } else if (d.hasCommitted && (Math.abs(d.xDisplacementSinceLock) < units.gu(2) || yVelocityCalculator.velocityAboveThreshold)) {
295
 
                    root.state = "locked";
296
 
                } else {
297
 
                    root.state = "reveal";
298
 
                }
299
 
            }
300
 
        }
301
 
    }
302
 
 
303
 
    states: [
304
 
        State {
305
 
            name: "initial"
306
 
            PropertyChanges { target: d; hasCommitted: false; restoreEntryValues: false }
307
 
        },
308
 
        State {
309
 
            name: "reveal"
310
 
            StateChangeScript {
311
 
                script: {
312
 
                    yVelocityCalculator.reset();
313
 
                    // initial item selection
314
 
                    if (!d.hasCommitted) bar.selectItemAt(d.activeDragHandle ? d.activeDragHandle.touchPosition.x : -1);
315
 
                    d.hasCommitted = false;
316
 
                }
317
 
            }
318
 
            PropertyChanges {
319
 
                target: bar
320
 
                expanded: true
321
 
                // changes to lateral touch position effect which indicator is selected
322
 
                lateralPosition: d.rowMappedLateralPosition
323
 
                // vertical velocity determines if changes in lateral position has an effect
324
 
                enableLateralChanges: d.activeDragHandle &&
325
 
                                      !yVelocityCalculator.velocityAboveThreshold
326
 
            }
327
 
            // left scroll bar handling
328
 
            PropertyChanges {
329
 
                target: leftScroller
330
 
                lateralPosition: {
331
 
                    if (!d.activeDragHandle) return -1;
332
 
                    var mapped = d.activeDragHandle.mapToItem(leftScroller, d.activeDragHandle.touchPosition.x, 0);
333
 
                    return mapped.x;
334
 
                }
335
 
            }
336
 
            // right scroll bar handling
337
 
            PropertyChanges {
338
 
                target: rightScroller
339
 
                lateralPosition: {
340
 
                    if (!d.activeDragHandle) return -1;
341
 
                    var mapped = d.activeDragHandle.mapToItem(rightScroller, d.activeDragHandle.touchPosition.x, 0);
342
 
                    return mapped.x;
343
 
                }
344
 
            }
345
 
        },
346
 
        State {
347
 
            name: "locked"
348
 
            StateChangeScript {
349
 
                script: {
350
 
                    d.xDisplacementSinceLock = 0;
351
 
                    d.lastHideTouchX = hideDragHandle.touchPosition.x;
352
 
                }
353
 
            }
354
 
            PropertyChanges { target: bar; expanded: true }
355
 
        },
356
 
        State {
357
 
            name: "commit"
358
 
            extend: "locked"
359
 
            PropertyChanges { target: bar; interactive: true }
360
 
            PropertyChanges {
361
 
                target: d;
362
 
                hasCommitted: true
363
 
                lastHideTouchX: 0
364
 
                xDisplacementSinceLock: 0
365
 
                restoreEntryValues: false
366
 
            }
367
 
        }
368
 
    ]
369
 
    state: "initial"
370
 
}