Merge lp:~zsombi/ubuntu-ui-toolkit/cppBottomEdgeHint into lp:ubuntu-ui-toolkit/staging
- cppBottomEdgeHint
- Merge into staging
Status: | Merged |
---|---|
Approved by: | Cris Dywan |
Approved revision: | 1724 |
Merged at revision: | 1710 |
Proposed branch: | lp:~zsombi/ubuntu-ui-toolkit/cppBottomEdgeHint |
Merge into: | lp:ubuntu-ui-toolkit/staging |
Diff against target: |
1351 lines (+873/-186) 18 files modified
components.api (+7/-0) examples/ubuntu-ui-toolkit-gallery/BottomEdgePage.qml (+1/-1) examples/ubuntu-ui-toolkit-gallery/MainPage.qml (+19/-0) src/Ubuntu/Components/1.3/BottomEdgeHint.qml (+0/-110) src/Ubuntu/Components/ComponentModule.pro (+0/-1) src/Ubuntu/Components/Themes/Ambiance/1.3/BottomEdgeHintStyle.qml (+12/-47) src/Ubuntu/Components/plugin/plugin.cpp (+2/-0) src/Ubuntu/Components/plugin/plugin.pri (+6/-2) src/Ubuntu/Components/plugin/privates/gesturedetector.cpp (+132/-0) src/Ubuntu/Components/plugin/privates/gesturedetector.h (+73/-0) src/Ubuntu/Components/plugin/quickutils.cpp (+2/-1) src/Ubuntu/Components/plugin/quickutils.h (+8/-0) src/Ubuntu/Components/plugin/ucbottomedgehint.cpp (+347/-0) src/Ubuntu/Components/plugin/ucbottomedgehint.h (+92/-0) src/Ubuntu/Components/qmldir (+0/-1) src/Ubuntu/Test/plugin/uctestextras.cpp (+5/-0) src/Ubuntu/Test/plugin/uctestextras.h (+5/-1) tests/unit_x11/tst_components/tst_bottomedgehint.qml (+162/-22) |
To merge this branch: | bzr merge lp:~zsombi/ubuntu-ui-toolkit/cppBottomEdgeHint |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Cris Dywan | Approve | ||
PS Jenkins bot | continuous-integration | Approve | |
Review via email: mp+276627@code.launchpad.net |
Commit message
BottomEdgeHint API changes, deprecating state property, introducing locked property to drive visuals lock and click handling.
Description of the change
PS Jenkins bot (ps-jenkins) wrote : | # |
- 1711. By Zsombor Egri
-
Enter and Return key handling
- 1712. By Zsombor Egri
-
segfault fixed
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1711
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1712
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1713. By Zsombor Egri
-
touch functionality and test added
- 1714. By Zsombor Egri
-
redo removed state handling for Hidden and Visible
- 1715. By Zsombor Egri
-
roll back unneeded change
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1714
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1715
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1716. By Zsombor Egri
-
adding Status enum to replace locked property, the property is writable
- 1717. By Zsombor Egri
-
fixing API, adjusting style and tests
- 1718. By Zsombor Egri
-
introduce activateByGesture and deactivateTimeout properties with handling
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:1718
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
Click here to trigger a rebuild:
http://
- 1719. By Zsombor Egri
-
separate gesture detection and do a proper detection state handling
- 1720. By Zsombor Egri
-
test code removed
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1719
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1721. By Zsombor Egri
-
remove activateByGesture and make it sole part of the component
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1720
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1722. By Zsombor Egri
-
add possibility to turn mouse on/off from menu
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1721
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
- 1723. By Zsombor Egri
-
move click support from the style into the component
- 1724. By Zsombor Egri
-
final touch
PS Jenkins bot (ps-jenkins) wrote : | # |
PASSED: Continuous integration, rev:1724
http://
Executed test runs:
SUCCESS: http://
SUCCESS: http://
deb: http://
SUCCESS: http://
Click here to trigger a rebuild:
http://
Cris Dywan (kalikiana) wrote : | # |
I like.
Perhaps a follow-up bug report ought to be filed for implementing mouse discovery?
Preview Diff
1 | === modified file 'components.api' |
2 | --- components.api 2015-11-04 14:58:18 +0000 |
3 | +++ components.api 2015-11-05 14:29:06 +0000 |
4 | @@ -176,11 +176,18 @@ |
5 | property bool iconFrame |
6 | property bool progression |
7 | Ubuntu.Components.BottomEdgeHint 1.3: StyledItem |
8 | + property int deactivateTimeout |
9 | property Flickable flickable |
10 | property string iconName |
11 | property url iconSource |
12 | signal clicked() |
13 | + property Status status |
14 | property string text |
15 | +Ubuntu.Components.BottomEdgeHint.Status: Enum |
16 | + Active |
17 | + Hidden |
18 | + Inactive |
19 | + Locked |
20 | Ubuntu.Components.Button 1.0 0.1: AbstractButton |
21 | property color color |
22 | property QFont font |
23 | |
24 | === modified file 'examples/ubuntu-ui-toolkit-gallery/BottomEdgePage.qml' |
25 | --- examples/ubuntu-ui-toolkit-gallery/BottomEdgePage.qml 2015-10-23 06:50:14 +0000 |
26 | +++ examples/ubuntu-ui-toolkit-gallery/BottomEdgePage.qml 2015-11-05 14:29:06 +0000 |
27 | @@ -33,6 +33,6 @@ |
28 | BottomEdgeHint { |
29 | iconName: "stock_message" |
30 | text: "Compose a new message" |
31 | - onClicked: state = "Idle" |
32 | + onClicked: state = "Hidden" |
33 | } |
34 | } |
35 | |
36 | === modified file 'examples/ubuntu-ui-toolkit-gallery/MainPage.qml' |
37 | --- examples/ubuntu-ui-toolkit-gallery/MainPage.qml 2015-10-23 14:46:16 +0000 |
38 | +++ examples/ubuntu-ui-toolkit-gallery/MainPage.qml 2015-11-05 14:29:06 +0000 |
39 | @@ -53,6 +53,18 @@ |
40 | text: i18n.tr('About') |
41 | iconName: "info" |
42 | onTriggered: mainPage.pageStack.addPageToCurrentColumn(mainPage, Qt.resolvedUrl("About.qml")) |
43 | + }, |
44 | + Action { |
45 | + text: i18n.tr("Deactivate mouse") |
46 | + iconName: "non-starred" |
47 | + visible: QuickUtils.mouseAttached |
48 | + onTriggered: QuickUtils.mouseAttached = false |
49 | + }, |
50 | + Action { |
51 | + text: i18n.tr("Activate mouse") |
52 | + iconName: "starred" |
53 | + visible: !QuickUtils.mouseAttached |
54 | + onTriggered: QuickUtils.mouseAttached = true |
55 | } |
56 | ] |
57 | } |
58 | @@ -120,4 +132,11 @@ |
59 | } |
60 | highlightMoveDuration: 0 |
61 | } |
62 | + |
63 | + BottomEdgeHint { |
64 | + flickable: widgetList |
65 | + text: i18n.tr('About') |
66 | + iconName: "info" |
67 | + onClicked: mainPage.pageStack.addPageToCurrentColumn(mainPage, Qt.resolvedUrl("About.qml")) |
68 | + } |
69 | } |
70 | |
71 | === removed file 'src/Ubuntu/Components/1.3/BottomEdgeHint.qml' |
72 | --- src/Ubuntu/Components/1.3/BottomEdgeHint.qml 2015-10-23 16:37:49 +0000 |
73 | +++ src/Ubuntu/Components/1.3/BottomEdgeHint.qml 1970-01-01 00:00:00 +0000 |
74 | @@ -1,110 +0,0 @@ |
75 | -/* |
76 | - * Copyright 2015 Canonical Ltd. |
77 | - * |
78 | - * This program is free software; you can redistribute it and/or modify |
79 | - * it under the terms of the GNU Lesser General Public License as published by |
80 | - * the Free Software Foundation; version 3. |
81 | - * |
82 | - * This program is distributed in the hope that it will be useful, |
83 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
84 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
85 | - * GNU Lesser General Public License for more details. |
86 | - * |
87 | - * You should have received a copy of the GNU Lesser General Public License |
88 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
89 | - */ |
90 | - |
91 | -import QtQuick 2.4 |
92 | -import Ubuntu.Components 1.3 |
93 | - |
94 | -/*! |
95 | - \qmltype BottomEdgeHint |
96 | - \inqmlmodule Ubuntu.Components 1.3 |
97 | - \ingroup ubuntu |
98 | - \inherits StyledItem |
99 | - \brief The BottomEdgeHint shows the availability of extra features |
100 | - available from the bottom edge of the application. |
101 | - |
102 | - It displays either a label or an icon at the bottom of the application. |
103 | - |
104 | - It has 4 states: Hidden, Idle, Active and Locked. When Idle, part of it is |
105 | - still visible hinting the existence of the bottom edge. |
106 | - |
107 | - When used with a mouse it acts like a button. The typical action associated |
108 | - with clicking on it should be revealing the extra features provided by the |
109 | - bottom edge. However, the click can only happen if the hint is in \e Locked |
110 | - state. |
111 | - |
112 | - Example: |
113 | - \qml |
114 | - BottomEdgeHint { |
115 | - id: bottomEdgeHint |
116 | - text: i18n.tr("Favorites") |
117 | - onClicked: revealBottomEdge() |
118 | - } |
119 | - \endqml |
120 | - |
121 | - The component is styled through \b BottomEdgeHintStyle. |
122 | -*/ |
123 | -StyledItem { |
124 | - id: bottomEdgeHint |
125 | - |
126 | - anchors.bottom: parent.bottom |
127 | - |
128 | - /*! |
129 | - This handler is called when there is a mouse click on the BottomEdgeHint |
130 | - and the BottomEdgeHint is not disabled. |
131 | - */ |
132 | - signal clicked() |
133 | - |
134 | - Keys.onEnterPressed: clicked() |
135 | - Keys.onReturnPressed: clicked() |
136 | - |
137 | - /*! |
138 | - \qmlproperty string text |
139 | - The label displayed by the BottomEdgeHint. |
140 | - */ |
141 | - property string text |
142 | - |
143 | - /*! |
144 | - \qmlproperty url iconSource |
145 | - The icon displayed by the BottomEdgeHint. |
146 | - |
147 | - This is the URL of any image file. |
148 | - If both iconSource and iconName are defined, iconName will be ignored. |
149 | - */ |
150 | - property url iconSource |
151 | - |
152 | - /*! |
153 | - \qmlproperty string iconName |
154 | - The icon associated with the BottomEdgeHint in the icon theme. |
155 | - |
156 | - If both iconSource and iconName are defined, iconName will be ignored. |
157 | - */ |
158 | - property string iconName |
159 | - |
160 | - /*! |
161 | - The property holds the flickable, which when flicked hides the hint. |
162 | - \e Hidden state is reached when this property is set to a Flickable |
163 | - which is flicking or moving. It is recommended to set the property |
164 | - when the hint is placed above a flickable content. Defaults to null. |
165 | - */ |
166 | - property Flickable flickable: null |
167 | - |
168 | - /*! |
169 | - \qmlproperty string state |
170 | - BottomEdgeHint can take 4 states of visibility: "Hidden", "Idle", "Active" |
171 | - and "Locked". |
172 | - |
173 | - When \e Hidden, the hint is not shown at all. When \e Active, the full hint |
174 | - with its content is shown. When \e Idle, only part of the hint is visible |
175 | - leaving more space for application content. \e Idle extends the empty state. |
176 | - \e Locked is similar to Active, except that it is a final state, meaning the |
177 | - hint will be shown no matter of the flickable's status. |
178 | - |
179 | - Defaults to \e Idle. |
180 | - */ |
181 | - state: "Idle" |
182 | - |
183 | - styleName: "BottomEdgeHintStyle" |
184 | -} |
185 | |
186 | === modified file 'src/Ubuntu/Components/ComponentModule.pro' |
187 | --- src/Ubuntu/Components/ComponentModule.pro 2015-10-20 11:05:53 +0000 |
188 | +++ src/Ubuntu/Components/ComponentModule.pro 2015-11-05 14:29:06 +0000 |
189 | @@ -132,7 +132,6 @@ |
190 | 1.3/UbuntuListView.qml \ |
191 | 1.3/UbuntuNumberAnimation.qml \ |
192 | 1.3/ListItemPopover.qml \ |
193 | - 1.3/BottomEdgeHint.qml \ |
194 | 1.3/PageColumn.qml \ |
195 | 1.3/PageColumnsLayout.qml \ |
196 | 1.3/ProgressionSlot.qml \ |
197 | |
198 | === modified file 'src/Ubuntu/Components/Themes/Ambiance/1.3/BottomEdgeHintStyle.qml' |
199 | --- src/Ubuntu/Components/Themes/Ambiance/1.3/BottomEdgeHintStyle.qml 2015-10-23 16:37:49 +0000 |
200 | +++ src/Ubuntu/Components/Themes/Ambiance/1.3/BottomEdgeHintStyle.qml 2015-11-05 14:29:06 +0000 |
201 | @@ -23,15 +23,22 @@ |
202 | implicitWidth: styledItem.parent.width |
203 | implicitHeight: units.gu(4) |
204 | |
205 | - state: styledItem.state |
206 | + readonly property BottomEdgeHint hint: styledItem |
207 | + |
208 | + // translate hint status into state |
209 | + state: { |
210 | + switch (hint.status) { |
211 | + case BottomEdgeHint.Hidden: return "Hidden"; |
212 | + case BottomEdgeHint.Inactive: return "Inactive" |
213 | + case BottomEdgeHint.Active: return "Active" |
214 | + case BottomEdgeHint.Locked: return "Locked" |
215 | + } |
216 | + } |
217 | |
218 | states: [ |
219 | State { |
220 | - name: "Idle" |
221 | + name: "Inactive" |
222 | extend: "" |
223 | - StateChangeScript { |
224 | - script: turnToIdleTimer.stop() |
225 | - } |
226 | }, |
227 | State { |
228 | name: "Active" |
229 | @@ -46,18 +53,11 @@ |
230 | }, |
231 | State { |
232 | name: "Hidden" |
233 | - when: styledItem.flickable && (styledItem.flickable.flicking || styledItem.flickable.moving) |
234 | PropertyChanges { |
235 | target: styledItem |
236 | opacity: 0.0 |
237 | } |
238 | - PropertyChanges { |
239 | - target: mouseHover |
240 | - enabled: false |
241 | - } |
242 | }, |
243 | - // FIXME: locked should be set and be final if mouse is attached |
244 | - // requires QSystemInfo support, which is ongoing work upstream |
245 | State { |
246 | name: "Locked" |
247 | PropertyChanges { |
248 | @@ -68,11 +68,6 @@ |
249 | target: h2 |
250 | anchors.topMargin: 0 |
251 | } |
252 | - PropertyChanges { |
253 | - target: turnToIdleTimer |
254 | - running: false |
255 | - |
256 | - } |
257 | } |
258 | ] |
259 | transitions: [ |
260 | @@ -97,27 +92,6 @@ |
261 | } |
262 | ] |
263 | |
264 | - // FIXME ZSOMBI: temporary functionality till SwipeGesture integration |
265 | - MouseArea { |
266 | - id: mouseHover |
267 | - anchors.fill: parent |
268 | - hoverEnabled: true |
269 | - acceptedButtons: Qt.NoButton |
270 | - enabled: styledItem.state != "Locked" |
271 | - onEntered: { |
272 | - styledItem.state = "Active"; |
273 | - turnToIdleTimer.stop(); |
274 | - } |
275 | - onExited: if (styledItem.state == "Active") turnToIdleTimer.restart() |
276 | - } |
277 | - |
278 | - Timer { |
279 | - id: turnToIdleTimer |
280 | - interval: 800 |
281 | - repeat: false |
282 | - onTriggered: styledItem.state = "Idle" |
283 | - } |
284 | - |
285 | clip: true |
286 | |
287 | Icon { |
288 | @@ -145,15 +119,6 @@ |
289 | anchors.top: parent.top |
290 | } |
291 | |
292 | - MouseArea { |
293 | - anchors.fill: parent |
294 | - onClicked: { |
295 | - Haptics.play(); |
296 | - styledItem.clicked(); |
297 | - mouse.accepted = false; |
298 | - } |
299 | - } |
300 | - |
301 | Row { |
302 | anchors { |
303 | top: parent.top |
304 | |
305 | === modified file 'src/Ubuntu/Components/plugin/plugin.cpp' |
306 | --- src/Ubuntu/Components/plugin/plugin.cpp 2015-10-20 09:19:50 +0000 |
307 | +++ src/Ubuntu/Components/plugin/plugin.cpp 2015-11-05 14:29:06 +0000 |
308 | @@ -70,6 +70,7 @@ |
309 | #include "ucheader.h" |
310 | #include "uclabel.h" |
311 | #include "uclistitemlayout.h" |
312 | +#include "ucbottomedgehint.h" |
313 | |
314 | #include <sys/types.h> |
315 | #include <unistd.h> |
316 | @@ -248,6 +249,7 @@ |
317 | qmlRegisterType<UCListItemLayout>(uri, 1, 3, "ListItemLayout"); |
318 | qmlRegisterType<UCHeader>(uri, 1, 3, "Header"); |
319 | qmlRegisterType<UCLabel>(uri, 1, 3, "Label"); |
320 | + qmlRegisterType<UCBottomEdgeHint>(uri, 1, 3, "BottomEdgeHint"); |
321 | } |
322 | |
323 | void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
324 | |
325 | === modified file 'src/Ubuntu/Components/plugin/plugin.pri' |
326 | --- src/Ubuntu/Components/plugin/plugin.pri 2015-10-01 12:40:52 +0000 |
327 | +++ src/Ubuntu/Components/plugin/plugin.pri 2015-11-05 14:29:06 +0000 |
328 | @@ -92,7 +92,9 @@ |
329 | $$PWD/uclabel.h \ |
330 | $$PWD/uclistitemlayout.h \ |
331 | $$PWD/privates/threelabelsslot_p.h \ |
332 | - $$PWD/ucimportversionchecker_p.h |
333 | + $$PWD/ucimportversionchecker_p.h \ |
334 | + $$PWD/ucbottomedgehint.h \ |
335 | + $$PWD/privates/gesturedetector.h |
336 | |
337 | SOURCES += $$PWD/plugin.cpp \ |
338 | $$PWD/uctheme.cpp \ |
339 | @@ -155,7 +157,9 @@ |
340 | $$PWD/uclabel.cpp \ |
341 | $$PWD/uclistitemlayout.cpp \ |
342 | $$PWD/privates/threelabelsslot_p.cpp \ |
343 | - $$PWD/ucimportversionchecker_p.cpp |
344 | + $$PWD/ucimportversionchecker_p.cpp \ |
345 | + $$PWD/ucbottomedgehint.cpp \ |
346 | + $$PWD/privates/gesturedetector.cpp |
347 | |
348 | # adapters |
349 | SOURCES += $$PWD/adapters/alarmsadapter_organizer.cpp |
350 | |
351 | === added file 'src/Ubuntu/Components/plugin/privates/gesturedetector.cpp' |
352 | --- src/Ubuntu/Components/plugin/privates/gesturedetector.cpp 1970-01-01 00:00:00 +0000 |
353 | +++ src/Ubuntu/Components/plugin/privates/gesturedetector.cpp 2015-11-05 14:29:06 +0000 |
354 | @@ -0,0 +1,132 @@ |
355 | +/* |
356 | + * Copyright 2015 Canonical Ltd. |
357 | + * |
358 | + * This program is free software; you can redistribute it and/or modify |
359 | + * it under the terms of the GNU Lesser General Public License as published by |
360 | + * the Free Software Foundation; version 3. |
361 | + * |
362 | + * This program is distributed in the hope that it will be useful, |
363 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
364 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
365 | + * GNU Lesser General Public License for more details. |
366 | + * |
367 | + * You should have received a copy of the GNU Lesser General Public License |
368 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
369 | + * |
370 | + * Authors: Zsombor Egri <zsombor.egri@canonical.com> |
371 | + */ |
372 | + |
373 | +#include "gesturedetector.h" |
374 | +#include "ucunits.h" |
375 | +#include <QtCore/QEvent> |
376 | +#include <QtCore/QRectF> |
377 | +#include <QtGui/QTouchEvent> |
378 | +#include <QtQuick/QQuickItem> |
379 | +#include <QtGui/QGuiApplication> |
380 | +#include <QtGui/QStyleHints> |
381 | + |
382 | +#define DETECTION_AREA_THICKNESS_GU 1.2 |
383 | + |
384 | +GestureDetector::GestureDetector(QObject *parent) |
385 | + : QObject(parent) |
386 | + , m_owner(qobject_cast<QQuickItem*>(parent)) |
387 | + , m_status(Ready) |
388 | + , m_bottomUpSwipeDetected(false) |
389 | +{ |
390 | + Q_ASSERT(m_owner); |
391 | +} |
392 | +GestureDetector::~GestureDetector() |
393 | +{ |
394 | + Q_FOREACH(QObject *object, m_filteredItems) { |
395 | + object->removeEventFilter(this); |
396 | + } |
397 | + m_filteredItems.clear(); |
398 | +} |
399 | + |
400 | +void GestureDetector::setStatus(Status status) |
401 | +{ |
402 | + if (status == m_status) { |
403 | + return; |
404 | + } |
405 | + m_status = status; |
406 | + Q_EMIT statusChanged(m_status); |
407 | +} |
408 | + |
409 | +bool GestureDetector::isDetecting() |
410 | +{ |
411 | + return (m_status > Ready && m_status < Completed); |
412 | +} |
413 | + |
414 | +void GestureDetector::setItemFilter(QObject *item) |
415 | +{ |
416 | + m_filteredItems.append(item); |
417 | + item->installEventFilter(this); |
418 | +} |
419 | + |
420 | +void GestureDetector::removeItemFilter(QObject *item) |
421 | +{ |
422 | + m_filteredItems.removeAll(item); |
423 | + item->removeEventFilter(this); |
424 | +} |
425 | + |
426 | +bool GestureDetector::handleTouchEvent(QObject *target, QTouchEvent *event) |
427 | +{ |
428 | + switch (event->type()) { |
429 | + case QEvent::TouchBegin: { |
430 | + setStatus(Ready); |
431 | + QPointF itemPoint = m_owner->mapFromScene(event->touchPoints()[0].scenePos()); |
432 | + qreal thickness = UCUnits::instance().gu(DETECTION_AREA_THICKNESS_GU); |
433 | + QRectF detectionArea(0.0, m_owner->height() - thickness, m_owner->width(), thickness); |
434 | + if (detectionArea.contains(itemPoint)) { |
435 | + m_startPoint = itemPoint; |
436 | + setStatus(Started); |
437 | + if (target == parent()) { |
438 | + event->accept(); |
439 | + return true; |
440 | + } |
441 | + } |
442 | + return false; |
443 | + } |
444 | + case QEvent::TouchEnd: |
445 | + { |
446 | + m_startPoint = QPointF(); |
447 | + setStatus(Completed); |
448 | + return false; |
449 | + } |
450 | + case QEvent::TouchCancel: { |
451 | + m_startPoint = QPointF(); |
452 | + setStatus(Ready); |
453 | + return false; |
454 | + } |
455 | + case QEvent::TouchUpdate: { |
456 | + if (m_status == Started) { |
457 | + QPointF itemPoint = m_owner->mapFromScene(event->touchPoints()[0].scenePos()); |
458 | + if (abs(m_startPoint.y() - itemPoint.y()) >= qApp->styleHints()->startDragDistance()) { |
459 | + setStatus(Detected); |
460 | + Q_EMIT bottomUpSwipeDetected(); |
461 | + } |
462 | + } |
463 | + return false; |
464 | + } |
465 | + default: return false; |
466 | + } |
467 | +} |
468 | + |
469 | +bool GestureDetector::eventFilter(QObject *target, QEvent *event) |
470 | +{ |
471 | + if (m_filteredItems.contains(target)) { |
472 | + QEvent::Type type = event->type(); |
473 | + if (type == QEvent::TouchBegin |
474 | + || type == QEvent::TouchUpdate |
475 | + || type == QEvent::TouchEnd |
476 | + || type == QEvent::TouchCancel) { |
477 | + QTouchEvent *touch = static_cast<QTouchEvent*>(event); |
478 | + return handleTouchEvent(target, touch); |
479 | + } else { |
480 | + // pass it on |
481 | + return false; |
482 | + } |
483 | + } else { |
484 | + return QObject::eventFilter(target, event); |
485 | + } |
486 | +} |
487 | |
488 | === added file 'src/Ubuntu/Components/plugin/privates/gesturedetector.h' |
489 | --- src/Ubuntu/Components/plugin/privates/gesturedetector.h 1970-01-01 00:00:00 +0000 |
490 | +++ src/Ubuntu/Components/plugin/privates/gesturedetector.h 2015-11-05 14:29:06 +0000 |
491 | @@ -0,0 +1,73 @@ |
492 | +/* |
493 | + * Copyright 2015 Canonical Ltd. |
494 | + * |
495 | + * This program is free software; you can redistribute it and/or modify |
496 | + * it under the terms of the GNU Lesser General Public License as published by |
497 | + * the Free Software Foundation; version 3. |
498 | + * |
499 | + * This program is distributed in the hope that it will be useful, |
500 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
501 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
502 | + * GNU Lesser General Public License for more details. |
503 | + * |
504 | + * You should have received a copy of the GNU Lesser General Public License |
505 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
506 | + * |
507 | + * Authors: Zsombor Egri <zsombor.egri@canonical.com> |
508 | + */ |
509 | + |
510 | +#ifndef GESTUREDETECTOR_H |
511 | +#define GESTUREDETECTOR_H |
512 | + |
513 | +#include <QtCore/QObject> |
514 | +#include <QtCore/QPointF> |
515 | +#include <QtCore/QList> |
516 | + |
517 | +/* |
518 | + * A simple gesture detection filter class that can be used in components to detect |
519 | + * various gestures. Yet swipe from bottom up is the only gesture handled. |
520 | + * It does not grab or consume the event from the environment, acts as a filter. |
521 | + */ |
522 | +class QTouchEvent; |
523 | +class QQuickItem; |
524 | +class GestureDetector : public QObject |
525 | +{ |
526 | + Q_OBJECT |
527 | +public: |
528 | + enum Status { |
529 | + Ready, |
530 | + Started, |
531 | + Detected, |
532 | + Completed |
533 | + }; |
534 | + explicit GestureDetector(QObject *parent = 0); |
535 | + ~GestureDetector(); |
536 | + |
537 | + bool isDetecting(); |
538 | + |
539 | + void setItemFilter(QObject *item); |
540 | + void removeItemFilter(QObject *item); |
541 | + |
542 | + bool handleTouchEvent(QObject *target, QTouchEvent *event); |
543 | + |
544 | +Q_SIGNALS: |
545 | + void statusChanged(Status status); |
546 | + |
547 | + void bottomUpSwipeDetected(); |
548 | + |
549 | +public Q_SLOTS: |
550 | + |
551 | +protected: |
552 | + bool eventFilter(QObject *target, QEvent *event); |
553 | + |
554 | + void setStatus(Status status); |
555 | + |
556 | +private: |
557 | + QList<QObject*> m_filteredItems; |
558 | + QPointF m_startPoint; |
559 | + QQuickItem *m_owner; |
560 | + Status m_status; |
561 | + bool m_bottomUpSwipeDetected:1; |
562 | +}; |
563 | + |
564 | +#endif // GESTUREDETECTOR_H |
565 | |
566 | === modified file 'src/Ubuntu/Components/plugin/quickutils.cpp' |
567 | --- src/Ubuntu/Components/plugin/quickutils.cpp 2015-08-24 12:55:50 +0000 |
568 | +++ src/Ubuntu/Components/plugin/quickutils.cpp 2015-11-05 14:29:06 +0000 |
569 | @@ -32,7 +32,8 @@ |
570 | |
571 | QuickUtils::QuickUtils(QObject *parent) : |
572 | QObject(parent), |
573 | - m_rootView(0) |
574 | + m_rootView(0), |
575 | + m_mouseAttached(true) |
576 | { |
577 | QGuiApplication::instance()->installEventFilter(this); |
578 | m_omitIM << "ibus" << "none" << "compose"; |
579 | |
580 | === modified file 'src/Ubuntu/Components/plugin/quickutils.h' |
581 | --- src/Ubuntu/Components/plugin/quickutils.h 2015-08-24 12:55:50 +0000 |
582 | +++ src/Ubuntu/Components/plugin/quickutils.h 2015-11-05 14:29:06 +0000 |
583 | @@ -31,6 +31,7 @@ |
584 | Q_PROPERTY(QQuickItem *rootObject READ rootObject NOTIFY rootObjectChanged) |
585 | Q_PROPERTY(QString inputMethodProvider READ inputMethodProvider) |
586 | Q_PROPERTY(bool touchScreenAvailable READ touchScreenAvailable NOTIFY touchScreenAvailableChanged) |
587 | + Q_PROPERTY(bool mouseAttached MEMBER m_mouseAttached NOTIFY mouseAttachedChanged) |
588 | public: |
589 | static QuickUtils& instance() |
590 | { |
591 | @@ -48,11 +49,17 @@ |
592 | QObject* createQmlObject(const QUrl &url, QQmlEngine *engine); |
593 | static bool showDeprecationWarnings(); |
594 | |
595 | + bool mouseAttached() |
596 | + { |
597 | + return m_mouseAttached; |
598 | + } |
599 | + |
600 | Q_SIGNALS: |
601 | void rootObjectChanged(); |
602 | void activated(); |
603 | void deactivated(); |
604 | void touchScreenAvailableChanged(); |
605 | + void mouseAttachedChanged(); |
606 | |
607 | protected: |
608 | bool eventFilter(QObject *, QEvent *); |
609 | @@ -61,6 +68,7 @@ |
610 | explicit QuickUtils(QObject *parent = 0); |
611 | QPointer<QQuickView> m_rootView; |
612 | QStringList m_omitIM; |
613 | + bool m_mouseAttached; |
614 | |
615 | void lookupQuickView(); |
616 | }; |
617 | |
618 | === added file 'src/Ubuntu/Components/plugin/ucbottomedgehint.cpp' |
619 | --- src/Ubuntu/Components/plugin/ucbottomedgehint.cpp 1970-01-01 00:00:00 +0000 |
620 | +++ src/Ubuntu/Components/plugin/ucbottomedgehint.cpp 2015-11-05 14:29:06 +0000 |
621 | @@ -0,0 +1,347 @@ |
622 | +/* |
623 | + * Copyright 2015 Canonical Ltd. |
624 | + * |
625 | + * This program is free software; you can redistribute it and/or modify |
626 | + * it under the terms of the GNU Lesser General Public License as published by |
627 | + * the Free Software Foundation; version 3. |
628 | + * |
629 | + * This program is distributed in the hope that it will be useful, |
630 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
631 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
632 | + * GNU Lesser General Public License for more details. |
633 | + * |
634 | + * You should have received a copy of the GNU Lesser General Public License |
635 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
636 | + * |
637 | + * Authors: Zsombor Egri <zsombor.egri@canonical.com> |
638 | + */ |
639 | + |
640 | +#include "ucbottomedgehint.h" |
641 | +#include "ucstyleditembase_p.h" |
642 | +#include "quickutils.h" |
643 | +#include "ucunits.h" |
644 | +#include <QtQml/private/qqmlproperty_p.h> |
645 | +#include <QtQuick/private/qquickflickable_p.h> |
646 | + |
647 | +/*! |
648 | + \qmltype BottomEdgeHint |
649 | + \inqmlmodule Ubuntu.Components 1.3 |
650 | + \ingroup ubuntu |
651 | + \inherits StyledItem |
652 | + \brief The BottomEdgeHint shows the availability of extra features |
653 | + available from the bottom edge of the application. |
654 | + |
655 | + It displays a label and/or an icon at the bottom of the component it is |
656 | + attached to. |
657 | + |
658 | + When used with a mouse it acts like a button. The typical action associated |
659 | + with clicking on it should be revealing the extra features provided by the |
660 | + bottom edge. |
661 | + |
662 | + Example: |
663 | + \qml |
664 | + BottomEdgeHint { |
665 | + id: bottomEdgeHint |
666 | + text: i18n.tr("Favorites") |
667 | + onClicked: revealBottomEdge() |
668 | + } |
669 | + \endqml |
670 | + |
671 | + The component is styled through \b BottomEdgeHintStyle. |
672 | +*/ |
673 | +UCBottomEdgeHint::UCBottomEdgeHint(QQuickItem *parent) |
674 | + : UCStyledItemBase(parent) |
675 | + , m_gestureDetector(this) |
676 | + , m_flickable(Q_NULLPTR) |
677 | + , m_deactivateTimeout(800) |
678 | + // FIXME: we need QInputDeviceInfo to be complete with the locked!! |
679 | + , m_status(QuickUtils::instance().mouseAttached() ? Locked : Inactive) |
680 | + , m_pressed(false) |
681 | +{ |
682 | + /* |
683 | + * we cannot use setStyleName as that will trigger style loading |
684 | + * and the qmlEngine is not known at this phase of the of the initialization |
685 | + * Therefore we simply set the style name default. Style loading will |
686 | + * happen during component completion. |
687 | + */ |
688 | + UCStyledItemBasePrivate::get(this)->styleDocument = "BottomEdgeHintStyle"; |
689 | + |
690 | + // connect old stateChanged |
691 | + connect(this, &QQuickItem::stateChanged, this, &UCBottomEdgeHint::stateChanged); |
692 | + |
693 | + // connect to gesture detection |
694 | + connect(&m_gestureDetector, &GestureDetector::bottomUpSwipeDetected, |
695 | + this, &UCBottomEdgeHint::onBottomUpSwipeDetected); |
696 | + connect(&m_gestureDetector, &GestureDetector::statusChanged, |
697 | + this, &UCBottomEdgeHint::onGestureStatusChanged); |
698 | + |
699 | + // FIXME: use QInputDeviceInfo once available |
700 | + connect(&QuickUtils::instance(), &QuickUtils::mouseAttachedChanged, [this]() { |
701 | + setStatus(QuickUtils::instance().mouseAttached() ? Locked : Active); |
702 | + if (m_status == Active) { |
703 | + m_deactivationTimer.start(m_deactivateTimeout, this); |
704 | + } |
705 | + }); |
706 | + |
707 | + // accept mouse events |
708 | + setAcceptedMouseButtons(Qt::LeftButton); |
709 | +} |
710 | + |
711 | +void UCBottomEdgeHint::itemChange(ItemChange change, const ItemChangeData &data) |
712 | +{ |
713 | + UCStyledItemBase::itemChange(change, data); |
714 | + if (change == ItemParentHasChanged) { |
715 | + QQmlProperty bottomAnchors(this, "anchors.bottom", qmlContext(this)); |
716 | + if (data.item && !QQmlPropertyPrivate::binding(bottomAnchors)) { |
717 | + QQuickAnchors *anchors = QQuickItemPrivate::get(this)->anchors(); |
718 | + anchors->setBottom(QQuickItemPrivate::get(data.item)->bottom()); |
719 | + } |
720 | + } |
721 | +} |
722 | + |
723 | +void UCBottomEdgeHint::timerEvent(QTimerEvent *event) |
724 | +{ |
725 | + UCStyledItemBase::timerEvent(event); |
726 | + if (event->timerId() == m_deactivationTimer.timerId()) { |
727 | + setStatus(Inactive); |
728 | + m_deactivationTimer.stop(); |
729 | + } |
730 | +} |
731 | + |
732 | +// handle clicked event when locked and enter or return is pressed |
733 | +void UCBottomEdgeHint::keyPressEvent(QKeyEvent *event) |
734 | +{ |
735 | + UCStyledItemBase::keyPressEvent(event); |
736 | + if ((status() >= Active) && (event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return)) { |
737 | + Q_EMIT clicked(); |
738 | + } |
739 | +} |
740 | + |
741 | +// handle gesture detection |
742 | +void UCBottomEdgeHint::touchEvent(QTouchEvent *event) |
743 | +{ |
744 | + UCStyledItemBase::touchEvent(event); |
745 | + m_gestureDetector.handleTouchEvent(this, event); |
746 | +} |
747 | + |
748 | +// handle click event |
749 | +void UCBottomEdgeHint::mousePressEvent(QMouseEvent *event) |
750 | +{ |
751 | + if (contains(event->localPos()) && (m_status >= Active)) { |
752 | + m_pressed = true; |
753 | + } else { |
754 | + UCStyledItemBase::mousePressEvent(event); |
755 | + } |
756 | +} |
757 | +void UCBottomEdgeHint::mouseReleaseEvent(QMouseEvent *event) |
758 | +{ |
759 | + UCStyledItemBase::mouseReleaseEvent(event); |
760 | + if (m_pressed && (m_status >= Active)) { |
761 | + Q_EMIT clicked(); |
762 | + } |
763 | +} |
764 | + |
765 | +// watch gesture detection status changes |
766 | +void UCBottomEdgeHint::onBottomUpSwipeDetected() |
767 | +{ |
768 | + m_deactivationTimer.stop(); |
769 | + setStatus(Active); |
770 | +} |
771 | + |
772 | +void UCBottomEdgeHint::onGestureStatusChanged(GestureDetector::Status status) |
773 | +{ |
774 | + if (status == GestureDetector::Completed) { |
775 | + if (m_status == Active) { |
776 | + m_deactivationTimer.start(m_deactivateTimeout, this); |
777 | + } |
778 | + } |
779 | +} |
780 | + |
781 | +/*! |
782 | + \qmlsignal void BottomEdgeHint::clicked() |
783 | + This handler is called when there is a mouse click on the BottomEdgeHint |
784 | + and the BottomEdgeHint is not disabled. |
785 | +*/ |
786 | + |
787 | +/*! |
788 | + \qmlproperty string BottomEdgeHint::text |
789 | + The label displayed by the BottomEdgeHint. |
790 | + */ |
791 | + |
792 | +/*! |
793 | + \qmlproperty url BottomEdgeHint::iconSource |
794 | + The icon displayed by the BottomEdgeHint. |
795 | + |
796 | + This is the URL of any image file. |
797 | + If both iconSource and \l iconName are defined, \l iconName will be ignored. |
798 | + */ |
799 | + |
800 | +/*! |
801 | + \qmlproperty string BottomEdgeHint::iconName |
802 | + The icon associated with the BottomEdgeHint in the icon theme. |
803 | + |
804 | + If both \l iconSource and iconName are defined, iconName will be ignored. |
805 | + */ |
806 | + |
807 | +/*! |
808 | + \qmlproperty Flickable BottomEdgeHint::flickable |
809 | + The property holds the flickable, which when flicked hides the hint. |
810 | + \e Hidden state is reached when this property is set to a Flickable |
811 | + which is flicking or moving. It is recommended to set the property |
812 | + when the hint is placed above a flickable content. Defaults to null. |
813 | + */ |
814 | +void UCBottomEdgeHint::setFlickable(QQuickFlickable *flickable) |
815 | +{ |
816 | + if (flickable == m_flickable) { |
817 | + return; |
818 | + } |
819 | + if (m_flickable) { |
820 | + disconnect(m_flickable, &QQuickFlickable::flickingChanged, |
821 | + this, &UCBottomEdgeHint::handleFlickableActivation); |
822 | + disconnect(m_flickable, &QQuickFlickable::movingChanged, |
823 | + this, &UCBottomEdgeHint::handleFlickableActivation); |
824 | + m_gestureDetector.removeItemFilter(m_flickable); |
825 | + } |
826 | + m_flickable = flickable; |
827 | + if (m_flickable) { |
828 | + connect(m_flickable, &QQuickFlickable::flickingChanged, |
829 | + this, &UCBottomEdgeHint::handleFlickableActivation, Qt::DirectConnection); |
830 | + connect(m_flickable, &QQuickFlickable::movingChanged, |
831 | + this, &UCBottomEdgeHint::handleFlickableActivation, Qt::DirectConnection); |
832 | + m_gestureDetector.setItemFilter(m_flickable); |
833 | + } |
834 | + Q_EMIT flickableChanged(); |
835 | +} |
836 | + |
837 | +// flickable moves hide the hint only if the current status is not Locked |
838 | +void UCBottomEdgeHint::handleFlickableActivation() |
839 | +{ |
840 | + if (m_status < Locked && !m_gestureDetector.isDetecting() && !m_deactivationTimer.isActive()) { |
841 | + bool moving = m_flickable->isFlicking() || m_flickable->isMoving(); |
842 | + if (moving) { |
843 | + setStatus(Hidden); |
844 | + } else if (m_status == Hidden) { |
845 | + setStatus(Inactive); |
846 | + } |
847 | + } |
848 | +} |
849 | + |
850 | +/*! |
851 | + \qmlproperty string BottomEdgeHint::state |
852 | + \deprecated |
853 | + BottomEdgeHint can take 2 states of visibility: \e Hidden, \e Visible. |
854 | + \table |
855 | + \header |
856 | + \li State |
857 | + \li Description |
858 | + \row |
859 | + \li Hidden |
860 | + \li The hint is not shown at all and cannot be activated. |
861 | + \row |
862 | + \li Visible |
863 | + \li The hint is in a state where it is visible but not active. \l clicked |
864 | + signal is not emitted. |
865 | + \endtable |
866 | + |
867 | + Defaults to \e Visible. |
868 | + */ |
869 | +QString UCBottomEdgeHint::state() const |
870 | +{ |
871 | + return QQuickItem::state(); |
872 | +} |
873 | +void UCBottomEdgeHint::setState(const QString &state) |
874 | +{ |
875 | + QQuickItem::setState(state); |
876 | + |
877 | + qmlInfo(this) << "Overloaded 'state' property deprecated, will be removed from 1.3 release. Use 'status' instead."; |
878 | + QQuickItem *style = UCStyledItemBasePrivate::get(this)->styleItem; |
879 | + if (!style) { |
880 | + return; |
881 | + } |
882 | + if (state == "Hidden") { |
883 | + setStatus(Hidden); |
884 | + } |
885 | + if (state == "Visible") { |
886 | + setStatus(Inactive); |
887 | + } |
888 | +} |
889 | + |
890 | +/*! |
891 | + \qmlproperty Status BottomEdgeHint::status |
892 | + The property represents the status of the hint. The property is writable so it |
893 | + can be set to any of the following values programatically: |
894 | + \table |
895 | + \header |
896 | + \li Status |
897 | + \li Description |
898 | + \row |
899 | + \li Hidden |
900 | + \li The hint is not shown. Equivalent with setting \e visible to \c false, |
901 | + however visuals may do animations when altering this property. It can |
902 | + only be set if the current status is not \e Locked. |
903 | + \row |
904 | + \li Inactive |
905 | + \li The hint is shown and inactive. Styles can represent this state with |
906 | + different visuals. When inactive, \l clicked signal cannot be emitted. |
907 | + \row |
908 | + \li Active |
909 | + \li The hint is shown and active, meaning \l clicked signal is emitted when |
910 | + clicked with mouse. |
911 | + \row |
912 | + \li Locked |
913 | + \li Similar to \e Active the hint is shown and active, but no automatic transition |
914 | + to any other state is allowed. This is relevant for style implementations. |
915 | + \endtable |
916 | + \note \e Locked status value is set automatically when the system detects a |
917 | + mouse attached. In this case any change into other state value than \e Locked |
918 | + is rejected. |
919 | + Defaults to |
920 | + \list |
921 | + \li Inactive if no mouse is attached or |
922 | + \li Locked if there is a mouse detected. |
923 | + \endlist |
924 | + */ |
925 | +UCBottomEdgeHint::Status UCBottomEdgeHint::status() |
926 | +{ |
927 | + // FIXME: we won't need this once we get the QInputDeviceInfo reporting mouse attach/detach |
928 | + if (QuickUtils::instance().mouseAttached()) { |
929 | + m_status = Locked; |
930 | + } |
931 | + return m_status; |
932 | +} |
933 | + |
934 | +void UCBottomEdgeHint::setStatus(Status status) |
935 | +{ |
936 | + // FIXME: we need QInputDeviceInfo to complete this! |
937 | + // cannot unlock if mouse is attached or we don't have touch screen available |
938 | + if (status == m_status || (status != Locked && QuickUtils::instance().mouseAttached())) { |
939 | + return; |
940 | + } |
941 | + m_status = status; |
942 | + // make sure we stop the deactivation timer if Inactive or Locked |
943 | + if (status != Active && m_deactivationTimer.isActive()) { |
944 | + m_deactivationTimer.stop(); |
945 | + } |
946 | + Q_EMIT statusChanged(); |
947 | +} |
948 | + |
949 | +/*! |
950 | + * \qmlproperty int BottomEdgeHint::deactivateTimeout |
951 | + * The property specifies the timeout interval in milliseconds the \l status |
952 | + * is set to \e Inactive after a gesture based activation. Gesture based activation |
953 | + * is only possible when mouse is not attached to the device. Defaults to 800 |
954 | + * milliseconds. |
955 | + */ |
956 | + |
957 | +void UCBottomEdgeHint::setDeactivateTimeout(int timeout) |
958 | +{ |
959 | + if (timeout == m_deactivateTimeout || timeout < 0) { |
960 | + return; |
961 | + } |
962 | + m_deactivateTimeout = timeout; |
963 | + if (m_deactivationTimer.isActive()) { |
964 | + m_deactivationTimer.stop(); |
965 | + m_deactivationTimer.start(m_deactivateTimeout, this); |
966 | + } |
967 | + Q_EMIT deactivateTimeoutChanged(); |
968 | +} |
969 | |
970 | === added file 'src/Ubuntu/Components/plugin/ucbottomedgehint.h' |
971 | --- src/Ubuntu/Components/plugin/ucbottomedgehint.h 1970-01-01 00:00:00 +0000 |
972 | +++ src/Ubuntu/Components/plugin/ucbottomedgehint.h 2015-11-05 14:29:06 +0000 |
973 | @@ -0,0 +1,92 @@ |
974 | +/* |
975 | + * Copyright 2015 Canonical Ltd. |
976 | + * |
977 | + * This program is free software; you can redistribute it and/or modify |
978 | + * it under the terms of the GNU Lesser General Public License as published by |
979 | + * the Free Software Foundation; version 3. |
980 | + * |
981 | + * This program is distributed in the hope that it will be useful, |
982 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
983 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
984 | + * GNU Lesser General Public License for more details. |
985 | + * |
986 | + * You should have received a copy of the GNU Lesser General Public License |
987 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
988 | + * |
989 | + * Authors: Zsombor Egri <zsombor.egri@canonical.com> |
990 | + */ |
991 | + |
992 | +#ifndef UCBOTTOMEDGEHINT_H |
993 | +#define UCBOTTOMEDGEHINT_H |
994 | + |
995 | +#include "ucstyleditembase.h" |
996 | +#include "privates/gesturedetector.h" |
997 | + |
998 | +class QQuickFlickable; |
999 | +class UCBottomEdgeHint : public UCStyledItemBase |
1000 | +{ |
1001 | + Q_OBJECT |
1002 | + Q_ENUMS(Status) |
1003 | + Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged FINAL) |
1004 | + Q_PROPERTY(QUrl iconSource MEMBER m_iconSource NOTIFY iconSourceChanged FINAL) |
1005 | + Q_PROPERTY(QString iconName MEMBER m_iconName NOTIFY iconNameChanged FINAL) |
1006 | + Q_PROPERTY(QQuickFlickable *flickable MEMBER m_flickable WRITE setFlickable NOTIFY flickableChanged FINAL) |
1007 | + Q_PROPERTY(Status status MEMBER m_status WRITE setStatus NOTIFY statusChanged FINAL) |
1008 | + Q_PROPERTY(int deactivateTimeout MEMBER m_deactivateTimeout WRITE setDeactivateTimeout NOTIFY deactivateTimeoutChanged FINAL) |
1009 | + // deprecated |
1010 | + Q_PROPERTY(QString state READ state WRITE setState NOTIFY stateChanged) |
1011 | +public: |
1012 | + enum Status { |
1013 | + Hidden, |
1014 | + Inactive, |
1015 | + Active, |
1016 | + Locked |
1017 | + }; |
1018 | + explicit UCBottomEdgeHint(QQuickItem *parent = 0); |
1019 | + |
1020 | + void setFlickable(QQuickFlickable *flickable); |
1021 | + Status status(); |
1022 | + void setStatus(Status status); |
1023 | + |
1024 | + // deprecated |
1025 | + QString state() const; |
1026 | + void setState(const QString &state); |
1027 | + void setDeactivateTimeout(int timeout); |
1028 | + |
1029 | +Q_SIGNALS: |
1030 | + void textChanged(); |
1031 | + void iconSourceChanged(); |
1032 | + void iconNameChanged(); |
1033 | + void flickableChanged(); |
1034 | + void statusChanged(); |
1035 | + void deactivateTimeoutChanged(); |
1036 | + |
1037 | + void clicked(); |
1038 | + |
1039 | + // deprecated |
1040 | + void stateChanged(); |
1041 | +protected: |
1042 | + void itemChange(ItemChange change, const ItemChangeData &data); |
1043 | + void timerEvent(QTimerEvent *event); |
1044 | + void keyPressEvent(QKeyEvent *event); |
1045 | + void touchEvent(QTouchEvent *event); |
1046 | + void mousePressEvent(QMouseEvent *event); |
1047 | + void mouseReleaseEvent(QMouseEvent *event); |
1048 | + |
1049 | + void handleFlickableActivation(); |
1050 | + void onBottomUpSwipeDetected(); |
1051 | + void onGestureStatusChanged(GestureDetector::Status status); |
1052 | + |
1053 | +private: |
1054 | + GestureDetector m_gestureDetector; |
1055 | + QBasicTimer m_deactivationTimer; |
1056 | + QString m_text; |
1057 | + QUrl m_iconSource; |
1058 | + QString m_iconName; |
1059 | + QQuickFlickable *m_flickable; |
1060 | + int m_deactivateTimeout; |
1061 | + Status m_status; |
1062 | + bool m_pressed:1; |
1063 | +}; |
1064 | + |
1065 | +#endif // UCBOTTOMEDGEHINT_H |
1066 | |
1067 | === modified file 'src/Ubuntu/Components/qmldir' |
1068 | --- src/Ubuntu/Components/qmldir 2015-10-02 22:48:13 +0000 |
1069 | +++ src/Ubuntu/Components/qmldir 2015-11-05 14:29:06 +0000 |
1070 | @@ -139,6 +139,5 @@ |
1071 | MathUtils 1.3 1.3/mathUtils.js |
1072 | internal ColorUtils 1.3/colorUtils.js |
1073 | DateUtils 1.3 1.3/dateUtils.js |
1074 | -BottomEdgeHint 1.3 1.3/BottomEdgeHint.qml |
1075 | ProgressionSlot 1.3 1.3/ProgressionSlot.qml |
1076 | PageHeader 1.3 1.3/PageHeader.qml |
1077 | |
1078 | === modified file 'src/Ubuntu/Test/plugin/uctestextras.cpp' |
1079 | --- src/Ubuntu/Test/plugin/uctestextras.cpp 2015-02-02 11:10:46 +0000 |
1080 | +++ src/Ubuntu/Test/plugin/uctestextras.cpp 2015-11-05 14:29:06 +0000 |
1081 | @@ -37,6 +37,7 @@ |
1082 | } |
1083 | |
1084 | QTouchDevice *UCTestExtras::m_touchDevice = 0; |
1085 | +UCTestExtras *UCTestExtras::m_testExtras = 0; |
1086 | |
1087 | /*! |
1088 | * \qmltype TestExtras |
1089 | @@ -52,6 +53,7 @@ |
1090 | UCTestExtras::UCTestExtras(QObject *parent) : |
1091 | QObject(parent) |
1092 | { |
1093 | + m_testExtras = this; |
1094 | } |
1095 | |
1096 | /*! |
1097 | @@ -127,6 +129,9 @@ |
1098 | m_touchDevice = new QTouchDevice; |
1099 | m_touchDevice->setType(QTouchDevice::TouchScreen); |
1100 | QWindowSystemInterface::registerTouchDevice(m_touchDevice); |
1101 | + if (m_testExtras) { |
1102 | + Q_EMIT m_testExtras->touchDevicePresentChanged(); |
1103 | + } |
1104 | } |
1105 | } |
1106 | |
1107 | |
1108 | === modified file 'src/Ubuntu/Test/plugin/uctestextras.h' |
1109 | --- src/Ubuntu/Test/plugin/uctestextras.h 2015-01-05 13:31:51 +0000 |
1110 | +++ src/Ubuntu/Test/plugin/uctestextras.h 2015-11-05 14:29:06 +0000 |
1111 | @@ -24,10 +24,13 @@ |
1112 | class UCTestExtras : public QObject |
1113 | { |
1114 | Q_OBJECT |
1115 | - Q_PROPERTY(bool touchPresent READ touchDevicePresent) |
1116 | + Q_PROPERTY(bool touchPresent READ touchDevicePresent NOTIFY touchDevicePresentChanged) |
1117 | public: |
1118 | explicit UCTestExtras(QObject *parent = 0); |
1119 | |
1120 | +Q_SIGNALS: |
1121 | + void touchDevicePresentChanged(); |
1122 | + |
1123 | public Q_SLOTS: |
1124 | static QString openGLflavor(); |
1125 | static QString cpuArchitecture(); |
1126 | @@ -43,6 +46,7 @@ |
1127 | |
1128 | private: |
1129 | static QTouchDevice *m_touchDevice; |
1130 | + static UCTestExtras *m_testExtras; |
1131 | }; |
1132 | |
1133 | #endif // TESTEXTRAS_H |
1134 | |
1135 | === modified file 'tests/unit_x11/tst_components/tst_bottomedgehint.qml' |
1136 | --- tests/unit_x11/tst_components/tst_bottomedgehint.qml 2015-10-23 16:37:49 +0000 |
1137 | +++ tests/unit_x11/tst_components/tst_bottomedgehint.qml 2015-11-05 14:29:06 +0000 |
1138 | @@ -24,51 +24,191 @@ |
1139 | width: units.gu(40) |
1140 | height: units.gu(71) |
1141 | |
1142 | + ListView { |
1143 | + id: listView |
1144 | + anchors.fill: parent |
1145 | + model: 100 |
1146 | + delegate: Label { |
1147 | + height: units.gu(5) |
1148 | + text: "Item #" + index |
1149 | + } |
1150 | + } |
1151 | + |
1152 | BottomEdgeHint { |
1153 | id: bottomEdgeHint |
1154 | } |
1155 | + BottomEdgeHint { |
1156 | + id: floatingHint |
1157 | + anchors.bottom: parent.top |
1158 | + } |
1159 | + Item { |
1160 | + id: floatingItem |
1161 | + } |
1162 | |
1163 | UbuntuTestCase { |
1164 | name: "BottomEdgeHint" |
1165 | when: windowShown |
1166 | |
1167 | + // FIXME: the criteria must be adjusted when QSystemInfo will report |
1168 | + // attached mouses, till then we stick to the touch presence |
1169 | + property bool hasMouseAttached: QuickUtils.mouseAttached |
1170 | + |
1171 | SignalSpy { |
1172 | id: clickSpy |
1173 | target: bottomEdgeHint |
1174 | signalName: "onClicked" |
1175 | } |
1176 | |
1177 | - function cleanup() { |
1178 | - bottomEdgeHint.iconName = ""; |
1179 | - bottomEdgeHint.state = "Idle"; |
1180 | - clickSpy.clear(); |
1181 | - } |
1182 | - |
1183 | - function test_0_default_state() { |
1184 | + function initTestCase() { |
1185 | + // register test touch device |
1186 | + TestExtras.registerTouchDevice(); |
1187 | + |
1188 | + // FIXME: this test case must be adjusted after we get the QInputDeviceInfo |
1189 | + // available to detect attached mouse |
1190 | + // the test must be executed before we register touch device |
1191 | + if (!hasMouseAttached) { |
1192 | + // we don't have mouse attached, so we should be able to lock/unlock |
1193 | + compare(bottomEdgeHint.status, BottomEdgeHint.Inactive, "Wrong initial status when no mouse attached"); |
1194 | + bottomEdgeHint.status = BottomEdgeHint.Locked; |
1195 | + compare(bottomEdgeHint.status, BottomEdgeHint.Locked, "Could not toggle status"); |
1196 | + } else { |
1197 | + // we have the mouse attached, should not be able to unlock it |
1198 | + compare(bottomEdgeHint.status, BottomEdgeHint.Locked, "Wrong initial status when mouse attached"); |
1199 | + bottomEdgeHint.status = BottomEdgeHint.Inactive; |
1200 | + compare(bottomEdgeHint.status, BottomEdgeHint.Locked, "The bottom edge must not be unlockable as long as mouse is attached!"); |
1201 | + } |
1202 | + QuickUtils.mouseAttached = !QuickUtils.mouseAttached; |
1203 | + |
1204 | + // and then turn locked off if possible |
1205 | + bottomEdgeHint.status = BottomEdgeHint.Inactive; |
1206 | + if (!hasMouseAttached) { |
1207 | + compare(bottomEdgeHint.status, BottomEdgeHint.Inactive, "Cannot unlock hint!"); |
1208 | + } |
1209 | + |
1210 | + // defaults |
1211 | compare(bottomEdgeHint.iconName, ""); |
1212 | compare(bottomEdgeHint.text, ""); |
1213 | - compare(bottomEdgeHint.state, "Idle"); |
1214 | compare(bottomEdgeHint.width, mainView.width); |
1215 | compare(bottomEdgeHint.height, units.gu(4)); |
1216 | compare(bottomEdgeHint.y, mainView.height - bottomEdgeHint.height); |
1217 | + compare(bottomEdgeHint.flickable, null, "No flickable"); |
1218 | compare(clickSpy.count, 0, "The BottomEdgeHint should not have received a click."); |
1219 | + compare(bottomEdgeHint.deactivateTimeout, 800, "default deactivationTimeout"); |
1220 | + |
1221 | + // set the flickable |
1222 | + bottomEdgeHint.flickable = listView; |
1223 | + } |
1224 | + |
1225 | + function cleanup() { |
1226 | + listView.positionViewAtBeginning(); |
1227 | + bottomEdgeHint.visible = true; |
1228 | + bottomEdgeHint.iconName = ""; |
1229 | + bottomEdgeHint.status = BottomEdgeHint.Inactive; |
1230 | + clickSpy.clear(); |
1231 | + wait(400); |
1232 | } |
1233 | |
1234 | function test_hiding() { |
1235 | - bottomEdgeHint.state = "Hidden"; |
1236 | - tryCompare(bottomEdgeHint, "opacity", 0.0); |
1237 | - } |
1238 | - |
1239 | - function test_clicking() { |
1240 | - bottomEdgeHint.state = "Locked"; |
1241 | - mouseClick(bottomEdgeHint, centerOf(bottomEdgeHint).x, centerOf(bottomEdgeHint).y); |
1242 | - clickSpy.wait(); |
1243 | - } |
1244 | - |
1245 | - function test_no_clicking_while_unlocked() { |
1246 | - mouseClick(bottomEdgeHint, centerOf(bottomEdgeHint).x, centerOf(bottomEdgeHint).y); |
1247 | - expectFail("", "No click if not Locked"); |
1248 | - clickSpy.wait(200); |
1249 | + var flickDy = listView.height - units.gu(5); |
1250 | + flick(listView, centerOf(listView).x, flickDy, centerOf(listView).x, -flickDy, 0, 6); |
1251 | + if (hasMouseAttached) { |
1252 | + expectFailContinue("", "No hiding when mouse attached"); |
1253 | + } |
1254 | + tryCompare(bottomEdgeHint, "status", BottomEdgeHint.Hidden); |
1255 | + } |
1256 | + |
1257 | + function test_no_hiding_when_locked() { |
1258 | + var flickDy = listView.height - units.gu(10); |
1259 | + bottomEdgeHint.status = BottomEdgeHint.Locked; |
1260 | + flick(listView, centerOf(listView).x, flickDy, centerOf(listView).x, -flickDy, 0, 6); |
1261 | + expectFailContinue("", "No hiding when Locked"); |
1262 | + tryCompare(bottomEdgeHint, "status", BottomEdgeHint.Hidden, 500); |
1263 | + } |
1264 | + |
1265 | + function test_clicking_data() { |
1266 | + return [ |
1267 | + {tag: "when Locked", status: BottomEdgeHint.Locked, xfail: false}, |
1268 | + {tag: "when Active", status: BottomEdgeHint.Active, xfail: hasMouseAttached}, |
1269 | + {tag: "when Inactive", status: BottomEdgeHint.Inactive, xfail: true}, |
1270 | + {tag: "when Hidden", status: BottomEdgeHint.Hidden, xfail: true}, |
1271 | + ]; |
1272 | + } |
1273 | + function test_clicking(data) { |
1274 | + bottomEdgeHint.status = data.status; |
1275 | + compare(bottomEdgeHint.status, data.status); |
1276 | + mouseClick(bottomEdgeHint, centerOf(bottomEdgeHint).x, centerOf(bottomEdgeHint).y); |
1277 | + if (data.xfail) { |
1278 | + expectFailContinue(data.tag, "No click is expected"); |
1279 | + } |
1280 | + clickSpy.wait(500); |
1281 | + } |
1282 | + |
1283 | + function test_alter_deprecated_state_data() { |
1284 | + return [ |
1285 | + {tag: "Hidden", status: BottomEdgeHint.Hidden}, |
1286 | + {tag: "Visible", status: BottomEdgeHint.Inactive}, |
1287 | + ]; |
1288 | + } |
1289 | + function test_alter_deprecated_state(data) { |
1290 | + ignoreWarning(warningFormat(37, 5, "QML BottomEdgeHint: Overloaded 'state' property deprecated, will be removed from 1.3 release. Use 'status' instead.")); |
1291 | + bottomEdgeHint.state = data.tag; |
1292 | + compare(bottomEdgeHint.status, data.status, "Wrong component status: " + data.status); |
1293 | + } |
1294 | + |
1295 | + function test_anchoring() { |
1296 | + compare(floatingHint.anchors.bottom, mainView.top, "Anhors are broken"); |
1297 | + floatingHint.parent = floatingItem; |
1298 | + compare(floatingHint.anchors.bottom, floatingItem.top, "Anhors are broken after reparenting"); |
1299 | + } |
1300 | + |
1301 | + function test_no_clicking_data() { |
1302 | + return [ |
1303 | + {tag: "when hidden", property: "visible"}, |
1304 | + {tag: "when disabled", property: "enabled"}, |
1305 | + ]; |
1306 | + } |
1307 | + function test_no_clicking(data) { |
1308 | + bottomEdgeHint.status = BottomEdgeHint.Locked; |
1309 | + bottomEdgeHint[data.property] = false; |
1310 | + mouseClick(bottomEdgeHint, centerOf(bottomEdgeHint).x, centerOf(bottomEdgeHint).y); |
1311 | + expectFailContinue("", "No click " + data.tag); |
1312 | + clickSpy.wait(400); |
1313 | + } |
1314 | + |
1315 | + function test_activate_by_key_data() { |
1316 | + return [ |
1317 | + {tag: "enter and unlocked", key: Qt.Key_Return, status: BottomEdgeHint.Inactive}, |
1318 | + {tag: "return and unlocked", key: Qt.Key_Enter, status: BottomEdgeHint.Inactive}, |
1319 | + {tag: "enter and locked", key: Qt.Key_Return, status: BottomEdgeHint.Locked}, |
1320 | + {tag: "return and locked", key: Qt.Key_Enter, status: BottomEdgeHint.Locked}, |
1321 | + ]; |
1322 | + } |
1323 | + function test_activate_by_key(data) { |
1324 | + if (hasMouseAttached && !data.locked) { |
1325 | + skip(data.tag, "Test requires ability to unlock"); |
1326 | + } |
1327 | + bottomEdgeHint.status = data.status; |
1328 | + bottomEdgeHint.forceActiveFocus(); |
1329 | + keyPress(data.key); |
1330 | + if (bottomEdgeHint.status != BottomEdgeHint.Locked) { |
1331 | + expectFailContinue(data.tag, "should fail"); |
1332 | + } |
1333 | + clickSpy.wait(400); |
1334 | + keyRelease(data.key); |
1335 | + } |
1336 | + |
1337 | + // FIXME: must be executed before the test_hiding as flick with mouse affects |
1338 | + // the touch drag on ListView for some unknown reason |
1339 | + function test_touch_gesture() { |
1340 | + if (hasMouseAttached) { |
1341 | + skip("", "The test requires touch environment"); |
1342 | + } |
1343 | + bottomEdgeHint.text = "Touch Activated"; |
1344 | + var gestureStartPoint = Qt.point(centerOf(bottomEdgeHint).x, bottomEdgeHint.height - 1); |
1345 | + TestExtras.touchDrag(0, bottomEdgeHint, gestureStartPoint, Qt.point(0, -units.gu(8)), 6); |
1346 | + tryCompare(bottomEdgeHint, "status", BottomEdgeHint.Active, 400); |
1347 | + // then wait till we get back to Idle |
1348 | + tryCompare(bottomEdgeHint, "status", BottomEdgeHint.Inactive, 1000); |
1349 | } |
1350 | } |
1351 | } |
FAILED: Continuous integration, rev:1710 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- ci/2438/ jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-amd64- ci/1166 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-armhf- ci/1168 jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-armhf- ci/1168/ artifact/ work/output/ *zip*/output. zip jenkins. qa.ubuntu. com/job/ ubuntu- sdk-team- ubuntu- ui-toolkit- staging- vivid-i386- ci/1165
http://
Executed test runs:
UNSTABLE: http://
SUCCESS: http://
deb: http://
UNSTABLE: http://
Click here to trigger a rebuild: s-jenkins. ubuntu- ci:8080/ job/ubuntu- sdk-team- ubuntu- ui-toolkit- staging- ci/2438/ rebuild
http://