Merge lp:~faenil/ubuntu-ui-toolkit/addIgnoreSynthesizedEventsToMouseFilter into lp:ubuntu-ui-toolkit/staging

Proposed by Andrea Bernabei
Status: Merged
Approved by: Zsombor Egri
Approved revision: 1738
Merged at revision: 1742
Proposed branch: lp:~faenil/ubuntu-ui-toolkit/addIgnoreSynthesizedEventsToMouseFilter
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 274 lines (+166/-2)
6 files modified
components.api (+1/-0)
src/Ubuntu/Components/plugin/ucmouse.h (+6/-0)
src/Ubuntu/Components/plugin/ucmousefilters.cpp (+61/-1)
tests/unit_x11/tst_mousefilters/FilterSynthesizedEvents.qml (+34/-0)
tests/unit_x11/tst_mousefilters/tst_mousefilters.pro (+2/-1)
tests/unit_x11/tst_mousefilters/tst_mousefilterstest.cpp (+62/-0)
To merge this branch: bzr merge lp:~faenil/ubuntu-ui-toolkit/addIgnoreSynthesizedEventsToMouseFilter
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Zsombor Egri Approve
Review via email: mp+279464@code.launchpad.net

Description of the change

add the ignoreSynthesizedEvents property.

When the property is enabled, the Mouse filter ignores any synthesized mouse event, such as those created by the touch-to-mouse events synthesis.

Enabling this property makes it possible to only trigger the hovering logic ONLY when using a mouse, and not when using a touchscreen (as it happens when using MouseArea).

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Zsombor Egri (zsombi) wrote :

Nais!!! ;)

review: Approve
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) :
review: Approve (continuous-integration)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'components.api'
2--- components.api 2015-11-30 12:43:00 +0000
3+++ components.api 2015-12-03 15:50:37 +0000
4@@ -631,6 +631,7 @@
5 property bool enabled
6 property list<Item> forwardTo
7 readonly property bool hoverEnabled
8+ property bool ignoreSynthesizedEvents
9 signal pressed(QQuickMouseEvent mouse, Item host)
10 signal released(QQuickMouseEvent mouse, Item host)
11 signal clicked(QQuickMouseEvent mouse, Item host)
12
13=== modified file 'src/Ubuntu/Components/plugin/ucmouse.h'
14--- src/Ubuntu/Components/plugin/ucmouse.h 2014-04-15 12:51:31 +0000
15+++ src/Ubuntu/Components/plugin/ucmouse.h 2015-12-03 15:50:37 +0000
16@@ -71,6 +71,10 @@
17 Q_PROPERTY(int clickAndHoldThreshold READ clickAndHoldThreshold WRITE setClickAndHoldThreshold NOTIFY clickAndHoldThresholdChanged)
18 Q_PROPERTY(QQmlListProperty<QQuickItem> forwardTo READ forwardTo)
19 Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)
20+
21+ //Added in 1.3, but we can't use REVISION because of QTBUG-40043
22+ Q_PROPERTY(bool ignoreSynthesizedEvents MEMBER m_ignoreSynthesizedEvents NOTIFY ignoreSynthesizedEventsChanged)
23+
24 Q_ENUMS(Priority)
25 public:
26 enum Priority {
27@@ -98,6 +102,7 @@
28 void hoverEnabledChanged();
29 void clickAndHoldThresholdChanged();
30 void priorityChanged();
31+ void ignoreSynthesizedEventsChanged();
32
33 void pressed(QQuickMouseEvent *mouse, QQuickItem *host);
34 void released(QQuickMouseEvent *mouse, QQuickItem *host);
35@@ -151,6 +156,7 @@
36 bool m_longPress:1;
37 bool m_hovered:1;
38 bool m_doubleClicked:1;
39+ bool m_ignoreSynthesizedEvents:1;
40 };
41 QML_DECLARE_TYPEINFO(UCMouse, QML_HAS_ATTACHED_PROPERTIES)
42
43
44=== modified file 'src/Ubuntu/Components/plugin/ucmousefilters.cpp'
45--- src/Ubuntu/Components/plugin/ucmousefilters.cpp 2015-03-03 13:47:48 +0000
46+++ src/Ubuntu/Components/plugin/ucmousefilters.cpp 2015-12-03 15:50:37 +0000
47@@ -333,8 +333,48 @@
48
49 Similar functionality for the case when the mouse event occurs outside of the
50 owner is brought by the \l InverseMouse attached property.
51+
52+ \section1 Mouse events synthesis
53+ QtQuick automatically creates artificial mouse events whenever a scene receives
54+ touch events that are not consumed by any item (either by using MultiPointTouchArea
55+ or a custom C++ item). The Mouse filter provides the possibility to ignore
56+ synthesized mouse events by enabling the \l ignoreSynthesizedEvents
57+ property.
58+
59+ This is really useful when, while developing a convergent application, the app developer
60+ wants to avoid triggering the hovering logic using a touchscreen,
61+ but still be able to handle the hover events when using a mouse, and at the same time
62+ doesn't want to stop the mouse and touch events from propagating to items underneath the
63+ MouseArea which handles the hovering.
64+ The following is an example of how that functionaly can be implemented:
65+ \qml
66+ MouseArea {
67+ id: proximityArea
68+ anchors.fill: parent
69+ propagateComposedEvents: true
70+ hoverEnabled: true
71+
72+ //We use a separate variable to detect whether the area contains
73+ //a mouse, because MouseArea's containsMouse is true even when
74+ //tapping on it using a touchscreen (due to the touch events being
75+ //converted to mouse events if no item consumes them).
76+ property bool containsPointerDevice: false
77+
78+ //handle hover events using the Mouse filter instead of MouseArea, so that
79+ //we can ignore synthesized mouse events and not trigger hover logic when the
80+ //user is interacting with the app using a touch device.
81+ Mouse.ignoreSynthesizedEvents: true
82+ Mouse.onEntered: {
83+ console.log("ONLY A MOUSE CAN TRIGGER THIS SLOT")
84+ proximityArea.containsPointerDevice = true
85+ }
86+ Mouse.onExited: proximityArea.containsPointerDevice = false
87+
88+ //let mouse and touch events propagate underneath the mouse area
89+ onPressed: mouse.accepted = false
90+ }
91+ \endqml
92 */
93-
94 UCMouse::UCMouse(QObject *parent)
95 : QObject(parent)
96 , m_owner(qobject_cast<QQuickItem*>(parent))
97@@ -349,6 +389,7 @@
98 , m_longPress(false)
99 , m_hovered(false)
100 , m_doubleClicked(false)
101+ , m_ignoreSynthesizedEvents(false)
102 {
103 // if owner is MouseArea or InverseMouseArea, connect to the acceptedButtons
104 // and hoverEnabled change signals
105@@ -420,6 +461,11 @@
106 {
107 bool result = false;
108 Q_UNUSED(target);
109+
110+ if (m_ignoreSynthesizedEvents && event->source() == Qt::MouseEventSynthesizedByQt) {
111+ return result;
112+ }
113+
114 switch (event->type()) {
115 case QEvent::MouseButtonPress:
116 {
117@@ -791,6 +837,20 @@
118 }
119
120 /*!
121+ \qmlproperty bool Mouse::ignoreSynthesizedEvents
122+ This property controls how the filter handles the mouse events
123+ synthesized by Qt (e.g. the artificial mouse events created when
124+ an original touch event is not consumed by any Item in the scene).
125+
126+ If the value is true, the filter will ignore the synthesized mouse
127+ events.
128+
129+ More info at \l{Mouse events synthesis}.
130+
131+ The default value is false.
132+ */
133+
134+/*!
135 \qmlproperty Qt::MouseButtons Mouse::acceptedButtons
136 \readonly
137 The property holds the accepted mouse buttons of the owner.
138
139=== added file 'tests/unit_x11/tst_mousefilters/FilterSynthesizedEvents.qml'
140--- tests/unit_x11/tst_mousefilters/FilterSynthesizedEvents.qml 1970-01-01 00:00:00 +0000
141+++ tests/unit_x11/tst_mousefilters/FilterSynthesizedEvents.qml 2015-12-03 15:50:37 +0000
142@@ -0,0 +1,34 @@
143+/*
144+ * Copyright 2015 Canonical Ltd.
145+ *
146+ * This program is free software; you can redistribute it and/or modify
147+ * it under the terms of the GNU Lesser General Public License as published by
148+ * the Free Software Foundation; version 3.
149+ *
150+ * This program is distributed in the hope that it will be useful,
151+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
152+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
153+ * GNU Lesser General Public License for more details.
154+ *
155+ * You should have received a copy of the GNU Lesser General Public License
156+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
157+ */
158+
159+import QtQuick 2.4
160+import Ubuntu.Components 1.3
161+
162+MouseArea {
163+ width: units.gu(40)
164+ height: units.gu(40)
165+
166+ objectName: "rootMouseArea"
167+
168+ MouseArea {
169+ signal overlayPressed()
170+ objectName: "overlayArea"
171+ anchors.fill: parent
172+
173+ Mouse.enabled: true
174+ Mouse.ignoreSynthesizedEvents: true
175+ }
176+}
177
178=== modified file 'tests/unit_x11/tst_mousefilters/tst_mousefilters.pro'
179--- tests/unit_x11/tst_mousefilters/tst_mousefilters.pro 2014-04-14 16:57:12 +0000
180+++ tests/unit_x11/tst_mousefilters/tst_mousefilters.pro 2015-12-03 15:50:37 +0000
181@@ -31,4 +31,5 @@
182 DoubleClicked.qml \
183 HoverEvent.qml \
184 ForwardComposedEvents.qml \
185- ForwardEventChained.qml
186+ ForwardEventChained.qml \
187+ FilterSynthesizedEvents.qml
188
189=== modified file 'tests/unit_x11/tst_mousefilters/tst_mousefilterstest.cpp'
190--- tests/unit_x11/tst_mousefilters/tst_mousefilterstest.cpp 2014-11-25 09:58:33 +0000
191+++ tests/unit_x11/tst_mousefilters/tst_mousefilterstest.cpp 2015-12-03 15:50:37 +0000
192@@ -29,6 +29,7 @@
193 #include "quickutils.h"
194 #include "inversemouseareatype.h"
195 #include "uctestcase.h"
196+#include "uctestextras.h"
197 #include <private/qquickevents_p_p.h>
198 #include <private/qquickmousearea_p.h>
199
200@@ -116,6 +117,8 @@
201
202 void initTestCase()
203 {
204+ UCTestExtras::registerTouchDevice();
205+
206 QString modules(UBUNTU_QML_IMPORT_PATH);
207 QVERIFY(QDir(modules).exists());
208
209@@ -1101,6 +1104,65 @@
210 QTest::mouseRelease(test.data(), Qt::LeftButton, 0, guPoint(20, 30));
211 QTest::waitForEvents();
212 }
213+
214+ void testCase_ignoreSynthesizedEvents() {
215+ QScopedPointer<UbuntuTestCase> test(new UbuntuTestCase("FilterSynthesizedEvents.qml"));
216+ QQuickMouseArea *rootMouseArea = qobject_cast<QQuickMouseArea *>(test->rootObject());
217+ QQuickMouseArea* overlayArea = test->findItem<QQuickMouseArea *>("overlayArea");
218+ UCMouse *overlayFilter = attachedFilter<UCMouse>(test->rootObject(), "overlayArea");
219+
220+ QCOMPARE(rootMouseArea != Q_NULLPTR, true);
221+ QCOMPARE(overlayArea != Q_NULLPTR, true);
222+ QCOMPARE(overlayFilter != Q_NULLPTR, true);
223+
224+ QSignalSpy areaPressed(rootMouseArea, SIGNAL(pressed(QQuickMouseEvent *)));
225+ QSignalSpy overlayAreaPressed(overlayArea, SIGNAL(pressed(QQuickMouseEvent *)));
226+ QSignalSpy overlayFilterPressed(overlayFilter, SIGNAL(pressed(QQuickMouseEvent*, QQuickItem*)));
227+ QCOMPARE(overlayFilter->property("ignoreSynthesizedEvents").toBool(), true);
228+
229+ //we're assuming the priority is set to BeforeItem, the functionality is not priority-dependent anyway,
230+ //just the outcome is.
231+ QCOMPARE(overlayFilter->priority(), UCMouse::BeforeItem);
232+ QCOMPARE(overlayArea->isEnabled(), true);
233+
234+ //send a touch event, which will be converted to a synthesized mouse event, since
235+ //no item in this QML is handling touch events
236+ UCTestExtras::touchPress(0, overlayArea, guPoint(15, 15));
237+ QTest::waitForEvents();
238+
239+ QCOMPARE(areaPressed.count(), 0);
240+ QCOMPARE(overlayAreaPressed.count(), 1);
241+ QCOMPARE(overlayFilterPressed.count(), 0);
242+ UCTestExtras::touchRelease(0, overlayArea, guPoint(15, 15));
243+
244+ QTest::waitForEvents();
245+
246+ overlayFilter->setProperty("ignoreSynthesizedEvents", false);
247+ QCOMPARE(overlayFilter->property("ignoreSynthesizedEvents").toBool(), false);
248+ UCTestExtras::touchPress(1, overlayArea, guPoint(15, 15));
249+ QTest::waitForEvents();
250+
251+ QCOMPARE(areaPressed.count(), 0);
252+ //the filter doesn't accept the pressed event by default
253+ QCOMPARE(overlayAreaPressed.count(), 2);
254+ QCOMPARE(overlayFilterPressed.count(), 1);
255+ UCTestExtras::touchRelease(1, overlayArea, guPoint(15, 15));
256+ QTest::waitForEvents();
257+
258+ overlayArea->setEnabled(false);
259+ QCOMPARE(overlayArea->isEnabled(), false);
260+
261+ UCTestExtras::touchPress(2, overlayArea, guPoint(15, 15));
262+ QTest::waitForEvents();
263+
264+ //the filter gets the event but its owner is not enabled, so we expect it
265+ //to propagate to the area underneath
266+ QCOMPARE(areaPressed.count(), 1);
267+ QCOMPARE(overlayAreaPressed.count(), 2);
268+ QCOMPARE(overlayFilterPressed.count(), 2);
269+ UCTestExtras::touchRelease(2, overlayArea, guPoint(15, 15));
270+ QTest::waitForEvents();
271+ }
272 };
273
274 QTEST_MAIN(tst_mouseFilterTest)

Subscribers

People subscribed via source and target branches