on xenial help sign in
merged with lp:~dandrader/unity8/mouseClickSwitchesSurfaceFocus
f54591aNick Dedekind
plugins/Ubuntu/Gestures/TouchGate.cpp−2
@@ -53,8 +53,6 @@ void TouchGate::touchEvent(QTouchEvent *event)
53 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
53 m_touchInfoMap[touchPoint.id()].ownership = OwnershipRequested;
54 m_touchInfoMap[touchPoint.id()].ended = false;
54 m_touchInfoMap[touchPoint.id()].ended = false;
55 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
55 TouchRegistry::instance()->requestTouchOwnership(touchPoint.id(), this);
56−
57− Q_EMIT pressed();
58 }
56 }
59
57
60 goodToGo &= m_touchInfoMap.contains(touchPoint.id())
58 goodToGo &= m_touchInfoMap.contains(touchPoint.id())
plugins/Ubuntu/Gestures/TouchGate.h−2
@@ -53,8 +53,6 @@ public:
53Q_SIGNALS:
53Q_SIGNALS:
54 void targetItemChanged(QQuickItem *item);
54 void targetItemChanged(QQuickItem *item);
55
55
56− void pressed();
57−
58protected:
56protected:
59 void touchEvent(QTouchEvent *event) override;
57 void touchEvent(QTouchEvent *event) override;
60private:
58private:
plugins/Utils/CMakeLists.txt+1
@@ -9,6 +9,7 @@ include_directories(
9
9
10set(QMLPLUGIN_SRC
10set(QMLPLUGIN_SRC
11 constants.cpp
11 constants.cpp
12+ inputwatcher.cpp
12 qlimitproxymodelqml.cpp
13 qlimitproxymodelqml.cpp
13 unitysortfilterproxymodelqml.cpp
14 unitysortfilterproxymodelqml.cpp
14 relativetimeformatter.cpp
15 relativetimeformatter.cpp
plugins/Utils/inputwatcher.cppnew file+118
@@ -0,0 +1,118 @@
1+/*
2+ * Copyright (C) 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+ */
17+
18+#include "inputwatcher.h"
19+
20+#include <QMouseEvent>
21+
22+InputWatcher::InputWatcher(QObject *parent)
23+ : QObject(parent)
24+ , m_mousePressed(false)
25+ , m_touchPressed(false)
26+{
27+}
28+
29+QObject *InputWatcher::target() const
30+{
31+ return m_target;
32+}
33+
34+void InputWatcher::setTarget(QObject *value)
35+{
36+ if (m_target == value) {
37+ return;
38+ }
39+
40+ if (m_target) {
41+ m_target->removeEventFilter(this);
42+ }
43+
44+ setMousePressed(false);
45+ setTouchPressed(false);
46+
47+ m_target = value;
48+ if (m_target) {
49+ m_target->installEventFilter(this);
50+ }
51+
52+ Q_EMIT targetChanged(value);
53+}
54+
55+bool InputWatcher::targetPressed() const
56+{
57+ return m_mousePressed || m_touchPressed;
58+}
59+
60+bool InputWatcher::eventFilter(QObject* /*watched*/, QEvent *event)
61+{
62+ switch (event->type()) {
63+ case QEvent::TouchBegin:
64+ setTouchPressed(true);
65+ break;
66+ case QEvent::TouchEnd:
67+ setTouchPressed(false);
68+ break;
69+ case QEvent::MouseButtonPress:
70+ {
71+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
72+ if (mouseEvent->button() == Qt::LeftButton) {
73+ setMousePressed(true);
74+ }
75+ }
76+ break;
77+ case QEvent::MouseButtonRelease:
78+ {
79+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
80+ if (mouseEvent->button() == Qt::LeftButton) {
81+ setMousePressed(false);
82+ }
83+ }
84+ break;
85+ default:
86+ // Not interested
87+ break;
88+ }
89+
90+ // We never filter them out. We are just watching.
91+ return false;
92+}
93+
94+void InputWatcher::setMousePressed(bool value)
95+{
96+ if (value == m_mousePressed) {
97+ return;
98+ }
99+
100+ bool oldPressed = targetPressed();
101+ m_mousePressed = value;
102+ if (targetPressed() != oldPressed) {
103+ Q_EMIT targetPressedChanged(targetPressed());
104+ }
105+}
106+
107+void InputWatcher::setTouchPressed(bool value)
108+{
109+ if (value == m_touchPressed) {
110+ return;
111+ }
112+
113+ bool oldPressed = targetPressed();
114+ m_touchPressed = value;
115+ if (targetPressed() != oldPressed) {
116+ Q_EMIT targetPressedChanged(targetPressed());
117+ }
118+}
plugins/Utils/inputwatcher.hnew file+58
@@ -0,0 +1,58 @@
1+/*
2+ * Copyright (C) 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+ */
17+
18+#ifndef UNITY_INPUTWATCHER_H
19+#define UNITY_INPUTWATCHER_H
20+
21+#include <QObject>
22+#include <QPointer>
23+
24+/*
25+ Monitors the target object for input events to figure out whether it's pressed
26+ or not.
27+ */
28+class InputWatcher : public QObject
29+{
30+ Q_OBJECT
31+ Q_PROPERTY(QObject* target READ target WRITE setTarget NOTIFY targetChanged)
32+
33+ // Whether the target object is pressed (by either touch or mouse)
34+ Q_PROPERTY(bool targetPressed READ targetPressed NOTIFY targetPressedChanged)
35+public:
36+ InputWatcher(QObject *parent = nullptr);
37+
38+ QObject *target() const;
39+ void setTarget(QObject *value);
40+
41+ bool targetPressed() const;
42+
43+ bool eventFilter(QObject *watched, QEvent *event) override;
44+
45+Q_SIGNALS:
46+ void targetChanged(QObject *value);
47+ void targetPressedChanged(bool value);
48+
49+private:
50+ void setMousePressed(bool value);
51+ void setTouchPressed(bool value);
52+
53+ bool m_mousePressed;
54+ bool m_touchPressed;
55+ QPointer<QObject> m_target;
56+};
57+
58+#endif // UNITY_INPUTWATCHER_H
plugins/Utils/plugin.cpp+2
@@ -26,6 +26,7 @@
26#include "plugin.h"
26#include "plugin.h"
27
27
28// local
28// local
29+#include "inputwatcher.h"
29#include "qlimitproxymodelqml.h"
30#include "qlimitproxymodelqml.h"
30#include "unitysortfilterproxymodelqml.h"
31#include "unitysortfilterproxymodelqml.h"
31#include "relativetimeformatter.h"
32#include "relativetimeformatter.h"
@@ -62,6 +63,7 @@ void UtilsPlugin::registerTypes(const char *uri)
62 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
63 qmlRegisterType<GDateTimeFormatter>(uri, 0, 1, "GDateTimeFormatter");
63 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
64 qmlRegisterType<EasingCurve>(uri, 0, 1, "EasingCurve");
64 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
65 qmlRegisterType<RelativeTimeFormatter>(uri, 0, 1, "RelativeTimeFormatter");
66+ qmlRegisterType<InputWatcher>(uri, 0, 1, "InputWatcher");
65 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
67 qmlRegisterSingletonType<WindowStateStorage>(uri, 0, 1, "WindowStateStorage", createWindowStateStorage);
66 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
68 qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants);
67}
69}
qml/Stages/SurfaceContainer.qml+12−9
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License as published by
5 * it under the terms of the GNU Lesser General Public License as published by
@@ -17,6 +17,7 @@
17import QtQuick 2.0
17import QtQuick 2.0
18import Ubuntu.Components 1.1
18import Ubuntu.Components 1.1
19import Ubuntu.Gestures 0.1 // For TouchGate
19import Ubuntu.Gestures 0.1 // For TouchGate
20+import Utils 0.1 // for InputWatcher
20
21
21FocusScope {
22FocusScope {
22 id: root
23 id: root
@@ -26,8 +27,6 @@ FocusScope {
26 property int orientation
27 property int orientation
27 property bool interactive
28 property bool interactive
28
29
29− signal surfacePressed()
30−
31 onSurfaceChanged: {
30 onSurfaceChanged: {
32 if (surface) {
31 if (surface) {
33 // Set the surface focus *after* it is added to the scene to
32 // Set the surface focus *after* it is added to the scene to
@@ -45,17 +44,21 @@ FocusScope {
45 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
44 Binding { target: surface; property: "enabled"; value: root.interactive; when: surface }
46 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
45 Binding { target: surface; property: "antialiasing"; value: !root.interactive; when: surface }
47
46
47+ InputWatcher {
48+ target: root.surface
49+ onTargetPressedChanged: {
50+ if (targetPressed && root.interactive) {
51+ root.focus = true;
52+ root.forceActiveFocus();
53+ }
54+ }
55+ }
56+
48 TouchGate {
57 TouchGate {
49 targetItem: surface
58 targetItem: surface
50 anchors.fill: root
59 anchors.fill: root
51 enabled: root.surface ? root.surface.enabled : false
60 enabled: root.surface ? root.surface.enabled : false
52 z: 2
61 z: 2
53− onPressed: {
54− root.focus = true;
55− if (root.interactive) {
56− root.forceActiveFocus();
57− }
58− }
59 }
62 }
60
63
61 states: [
64 states: [
tests/mocks/Unity/Application/MirSurfaceItem.cpp+6
@@ -47,6 +47,12 @@ MirSurfaceItem::MirSurfaceItem(const QString& name,
47{
47{
48 qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
48 qDebug() << "MirSurfaceItem::MirSurfaceItem() " << this->name();
49
49
50+ setAcceptedMouseButtons(Qt::LeftButton | Qt::MiddleButton | Qt::RightButton |
51+ Qt::ExtraButton1 | Qt::ExtraButton2 | Qt::ExtraButton3 | Qt::ExtraButton4 |
52+ Qt::ExtraButton5 | Qt::ExtraButton6 | Qt::ExtraButton7 | Qt::ExtraButton8 |
53+ Qt::ExtraButton9 | Qt::ExtraButton10 | Qt::ExtraButton11 |
54+ Qt::ExtraButton12 | Qt::ExtraButton13);
55+
50 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
56 QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership);
51
57
52 connect(this, &QQuickItem::focusChanged,
58 connect(this, &QQuickItem::focusChanged,
tests/mocks/Unity/Application/MirSurfaceItem.h+5
@@ -104,6 +104,11 @@ Q_SIGNALS:
104protected:
104protected:
105 void touchEvent(QTouchEvent * event) override;
105 void touchEvent(QTouchEvent * event) override;
106
106
107+ // becaue the default implementation ignores the events
108+ void mousePressEvent(QMouseEvent *) override {}
109+ void mouseMoveEvent(QMouseEvent *) override {}
110+ void mouseReleaseEvent(QMouseEvent *) override {}
111+
107private Q_SLOTS:
112private Q_SLOTS:
108 void onFocusChanged();
113 void onFocusChanged();
109 void onComponentStatusChanged(QQmlComponent::Status status);
114 void onComponentStatusChanged(QQmlComponent::Status status);
tests/qmltests/Stages/tst_SurfaceContainer.qml+32−5
@@ -1,5 +1,5 @@
1/*
1/*
2− * Copyright 2014 Canonical Ltd.
2+ * Copyright 2014-2015 Canonical Ltd.
3 *
3 *
4 * This program is free software; you can redistribute it and/or modify
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
5 * it under the terms of the GNU General Public License as published by
@@ -16,7 +16,7 @@
16
16
17import QtQuick 2.0
17import QtQuick 2.0
18import QtTest 1.0
18import QtTest 1.0
19−import Unity.Test 0.1 as UT
19+import Unity.Test 0.1
20import ".."
20import ".."
21import "../../../qml/Stages"
21import "../../../qml/Stages"
22import Ubuntu.Components 0.1
22import Ubuntu.Components 0.1
@@ -32,6 +32,8 @@ Rectangle {
32 id: surfaceContainerComponent
32 id: surfaceContainerComponent
33 SurfaceContainer {
33 SurfaceContainer {
34 anchors.fill: parent
34 anchors.fill: parent
35+ orientation: Qt.PortraitOrientation
36+ interactive: interactiveCheckbox.checked
35 }
37 }
36 }
38 }
37 Loader {
39 Loader {
@@ -85,6 +87,12 @@ Rectangle {
85 CheckBox {id: fullscreenCheckbox; checked: true; }
87 CheckBox {id: fullscreenCheckbox; checked: true; }
86 Label { text: "fullscreen" }
88 Label { text: "fullscreen" }
87 }
89 }
90+ Row {
91+ anchors { left: parent.left; right: parent.right }
92+ CheckBox {id: interactiveCheckbox; checked: true; }
93+ Label { text: "interactive" }
94+ }
95+ MouseTouchEmulationCheckbox {}
88 }
96 }
89 }
97 }
90
98
@@ -94,7 +102,7 @@ Rectangle {
94 signalName: "surfaceDestroyed"
102 signalName: "surfaceDestroyed"
95 }
103 }
96
104
97− UT.UnityTestCase {
105+ UnityTestCase {
98 id: testCase
106 id: testCase
99 name: "SurfaceContainer"
107 name: "SurfaceContainer"
100 when: windowShown
108 when: windowShown
@@ -110,6 +118,8 @@ Rectangle {
110
118
111 tryCompare(surfaceContainerLoader.item, "surface", null);
119 tryCompare(surfaceContainerLoader.item, "surface", null);
112 surfaceSpy.clear();
120 surfaceSpy.clear();
121+
122+ interactiveCheckbox.checked = true;
113 }
123 }
114
124
115 /*
125 /*
@@ -145,14 +155,13 @@ Rectangle {
145 surfaceContainer.surface.touchPressCount = 0;
155 surfaceContainer.surface.touchPressCount = 0;
146 surfaceContainer.surface.touchReleaseCount = 0;
156 surfaceContainer.surface.touchReleaseCount = 0;
147
157
148− surfaceContainer.interactive = true;
149 tap(surfaceContainer, surfaceContainer.width / 2, surfaceContainer.height / 2);
158 tap(surfaceContainer, surfaceContainer.width / 2, surfaceContainer.height / 2);
150
159
151 // surface got touches as the surfaceContainer is interactive
160 // surface got touches as the surfaceContainer is interactive
152 compare(surfaceContainer.surface.touchPressCount, 1)
161 compare(surfaceContainer.surface.touchPressCount, 1)
153 compare(surfaceContainer.surface.touchReleaseCount, 1);
162 compare(surfaceContainer.surface.touchReleaseCount, 1);
154
163
155− surfaceContainer.interactive = false;
164+ interactiveCheckbox.checked = false;
156 tap(surfaceContainer, surfaceContainer.width / 2, surfaceContainer.height / 2);
165 tap(surfaceContainer, surfaceContainer.width / 2, surfaceContainer.height / 2);
157
166
158 // surface shouldn't get the touches from the second tap as the surfaceContainer
167 // surface shouldn't get the touches from the second tap as the surfaceContainer
@@ -160,5 +169,23 @@ Rectangle {
160 compare(surfaceContainer.surface.touchPressCount, 1)
169 compare(surfaceContainer.surface.touchPressCount, 1)
161 compare(surfaceContainer.surface.touchReleaseCount, 1);
170 compare(surfaceContainer.surface.touchReleaseCount, 1);
162 }
171 }
172+
173+ function test_surfaceGetsActiveFocusOnMousePress() {
174+ surfaceCheckbox.checked = true;
175+ verify(surfaceContainer.surface !== null);
176+
177+ compare(surfaceContainer.surface.activeFocus, false);
178+ mouseClick(surfaceContainer);
179+ compare(surfaceContainer.surface.activeFocus, true);
180+ }
181+
182+ function test_surfaceGetsActiveFocusOnTap() {
183+ surfaceCheckbox.checked = true;
184+ verify(surfaceContainer.surface !== null);
185+
186+ compare(surfaceContainer.surface.activeFocus, false);
187+ tap(surfaceContainer);
188+ compare(surfaceContainer.surface.activeFocus, true);
189+ }
163 }
190 }
164}
191}