Lomiri
Loading...
Searching...
No Matches
SwipeToAct.qml
1/*
2 * Copyright (C) 2014-2015 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
17import QtQuick 2.12
18import Lomiri.Components 1.3
19
20Item {
21 id: swipeToAct
22
23 height: clickToAct ? leftButton.height : row.height
24
25 signal leftTriggered()
26 signal rightTriggered()
27
28 property string leftIconName
29 property string rightIconName
30
31 property bool clickToAct
32
33 QtObject {
34 id: priv
35
36 property double opacityDelta
37 readonly property double sliderHeight: units.gu(8)
38 readonly property double gap: units.gu(1)
39 readonly property color sliderMainColor: theme.palette.normal.base
40 readonly property color sliderBGColor: theme.palette.normal.background
41 readonly property double halfWay: mouseArea.drag.maximumX / 2
42
43 LomiriNumberAnimation on opacityDelta {
44 from: 0
45 to: .5
46 loops: Animation.Infinite
47 duration: LomiriAnimation.SleepyDuration
48 easing.type: Easing.Linear
49 }
50
51 // linearly interpolate between start- and end-color
52 // with a normalized weight-factor
53 // 0.0 meaning just the start-color being taken into
54 // account and 1.0 only taking the end-color into
55 // account
56 function interpolate(start, end, factor) {
57 var rdiff = start.r > end.r ? end.r - start.r : end.r - start.r
58 var gdiff = start.g > end.g ? end.g - start.g : end.g - start.g
59 var bdiff = start.b > end.b ? end.b - start.b : end.b - start.b
60 var adiff = start.a > end.a ? end.a - start.a : end.a - start.a
61 var r = start.r + factor * rdiff
62 var g = start.g + factor * gdiff
63 var b = start.b + factor * bdiff
64 var a = start.a + factor * adiff
65 return Qt.rgba(r,g,b,a)
66 }
67 }
68
69 NotificationButton {
70 id: leftButton
71 objectName: "leftButton"
72 anchors.verticalCenter: parent.verticalCenter
73 anchors.left: parent.left
74 iconName: leftIconName
75 visible: clickToAct
76 outline: false
77 width: (parent.width / 2) - priv.gap / 2
78 color: theme.palette.normal.negative
79 onClicked: leftTriggered()
80 }
81
82 NotificationButton {
83 id: rightButton
84 objectName: "rightButton"
85 anchors.verticalCenter: parent.verticalCenter
86 anchors.right: parent.right
87 iconName: rightIconName
88 visible: clickToAct
89 outline: false
90 width: (parent.width / 2) - priv.gap / 2
91 color: theme.palette.normal.positive
92 onClicked: rightTriggered()
93 }
94
95 LomiriShape {
96 id: row
97 width: parent.width
98 height: priv.sliderHeight
99 backgroundColor: priv.sliderBGColor
100 aspect: LomiriShape.Flat
101 visible: !clickToAct
102
103 LomiriShape {
104 id: leftShape
105 objectName: "leftArea"
106 anchors.top: parent.top
107 anchors.left: parent.left
108 anchors.margins: priv.gap
109 backgroundColor: theme.palette.normal.negative
110 aspect: LomiriShape.Flat
111
112 height: units.gu(6)
113 width: units.gu(6)
114 radius: "medium"
115 opacity: slider.x <= priv.halfWay ? 1.0 : 1.0 - ((slider.x - priv.halfWay) / priv.halfWay)
116
117 Icon {
118 anchors.centerIn: parent
119 width: units.gu(3.5)
120 height: units.gu(3.5)
121 name: leftIconName
122 color: "white"
123 }
124 }
125
126 Row {
127 anchors.verticalCenter: parent.verticalCenter
128 anchors.right: slider.left
129 anchors.rightMargin: units.gu(1.5)
130 spacing: -units.gu(1)
131 visible: slider.x === priv.halfWay
132 Icon {
133 name: "back"
134 height: units.gu(2.5)
135 color: priv.sliderMainColor
136 opacity: .5 + priv.opacityDelta
137 }
138 Icon {
139 name: "back"
140 height: units.gu(2.5)
141 color: priv.sliderMainColor
142 opacity: 1 - priv.opacityDelta
143 }
144 }
145
146 LomiriShape {
147 id: slider
148 objectName: "slider"
149 anchors.top: parent.top
150 anchors.margins: priv.gap
151 x: priv.halfWay
152
153 // Used by the tests to determine when the slider is ready to move
154 readonly property bool atRest: ((x === priv.halfWay) && (!mouseArea.dragging) && (!sliderXAnimation.running))
155
156 Component.onCompleted: {
157 xBehavior.enabled = true
158 }
159
160 Behavior on x {
161 id: xBehavior
162 enabled: false
163 LomiriNumberAnimation {
164 id: sliderXAnimation
165 duration: LomiriAnimation.FastDuration
166 easing.type: Easing.OutBounce
167 }
168 }
169
170 Behavior on opacity {
171 LomiriNumberAnimation {
172 duration: LomiriAnimation.FastDuration
173 }
174 }
175
176 onXChanged: {
177 var factor
178 if (slider.x <= priv.gap + leftShape.width)
179 {
180 factor = (slider.x - priv.gap) / leftShape.width
181 slider.color = priv.interpolate(leftShape.color, priv.sliderMainColor, factor)
182 } else if (slider.x >= rightShape.x - slider.width) {
183 factor = (slider.x - rightShape.x + rightShape.width) / rightShape.width
184 slider.color = priv.interpolate(priv.sliderMainColor, rightShape.color, factor)
185 } else {
186 slider.color = priv.sliderMainColor
187 }
188 }
189
190 z: 1
191 backgroundColor: priv.sliderMainColor
192 height: units.gu(6)
193 width: units.gu(6)
194 aspect: LomiriShape.Flat
195 radius: "medium"
196 Icon {
197 anchors.fill: parent
198 anchors.margins: units.gu(1.5)
199 name: "grip-large"
200 color: "white"
201 }
202 }
203
204 Row {
205 anchors.verticalCenter: parent.verticalCenter
206 anchors.left: slider.right
207 anchors.leftMargin: units.gu(1.5)
208 spacing: -units.gu(1)
209 visible: slider.x === priv.halfWay
210 Icon {
211 name: "next"
212 height: units.gu(2.5)
213 color: priv.sliderMainColor
214 opacity: 1 - priv.opacityDelta
215 }
216 Icon {
217 name: "next"
218 height: units.gu(2.5)
219 color: priv.sliderMainColor
220 opacity: 0.5 + priv.opacityDelta
221 }
222 }
223
224 LomiriShape {
225 id: rightShape
226 objectName: "rightArea"
227 anchors.top: parent.top
228 anchors.right: parent.right
229 anchors.margins: priv.gap
230 backgroundColor: theme.palette.normal.positive
231 aspect: LomiriShape.Flat
232
233 height: units.gu(6)
234 width: units.gu(6)
235 radius: "medium"
236 opacity: slider.x >= priv.halfWay ? 1.0 : slider.x / priv.halfWay
237
238 Icon {
239 anchors.centerIn: parent
240 width: units.gu(3.5)
241 height: units.gu(3.5)
242 name: rightIconName
243 color: "white"
244 }
245 }
246 }
247
248 MouseArea {
249 id: mouseArea
250 objectName: "swipeMouseArea"
251 enabled: !clickToAct
252
253 anchors.fill: row
254 drag.target: slider
255 drag.axis: Drag.XAxis
256 drag.minimumX: priv.gap
257 drag.maximumX: row.width - slider.width - priv.gap
258
259 onReleased: {
260 if (slider.x !== drag.minimumX || slider.x !== drag.maximumX) {
261 slider.x = priv.halfWay
262 }
263 if (slider.x === drag.minimumX) {
264 slider.x = drag.minimumX
265 enabled = false
266 leftTriggered()
267 }
268 if (slider.x === drag.maximumX) {
269 slider.x = drag.maximumX
270 enabled = false
271 rightTriggered()
272 }
273 }
274 }
275}