Merge lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/shortcuts into lp:ubuntu-ui-toolkit/staging

Proposed by Cris Dywan
Status: Merged
Approved by: Zsombor Egri
Approved revision: 1542
Merged at revision: 1552
Proposed branch: lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/shortcuts
Merge into: lp:ubuntu-ui-toolkit/staging
Diff against target: 280 lines (+176/-3)
6 files modified
components.api (+2/-1)
examples/ubuntu-ui-toolkit-gallery/Buttons.qml (+8/-2)
modules/Ubuntu/Components/plugin/plugin.cpp (+1/-0)
modules/Ubuntu/Components/plugin/ucaction.cpp (+66/-0)
modules/Ubuntu/Components/plugin/ucaction.h (+7/-0)
tests/unit_x11/tst_components/tst_shortcuts.qml (+92/-0)
To merge this branch: bzr merge lp:~ubuntu-sdk-team/ubuntu-ui-toolkit/shortcuts
Reviewer Review Type Date Requested Status
PS Jenkins bot continuous-integration Approve
Zsombor Egri Approve
Review via email: mp+262413@code.launchpad.net

Commit message

Implement Action.shortcut property

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
1538. By Cris Dywan

Fix doc comment for UCAction::shortcut

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

Got some comments inline, check 'em.

review: Needs Fixing
1539. By Cris Dywan

More explicit type check and multi modifier test case

Revision history for this message
Cris Dywan (kalikiana) wrote :

> if object is expected to be a QQuickItem,
> you should cast to it and that has a window()
> that you can use to get the window context...?

No. The action itself is in fact a QObject. We're walking up the parents to find a window.

> Shouldn't we also check whether the action is enabled?

No need, trigger() has a check already.

> What about testing also Alt+Ctrl, Shift+Ctrl and Alt+Shift+Ctrl?
> Or should we just assume that Qt shortcut system works with those?

Basically yeah, I don't think we want to duplicate their tests since we don't add any functionality. But I added another one with Atl+Shift+Ctrl just to be sure we'll know if anything obvious breaks in a Qt upgrade.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Zsombor Egri (zsombi) wrote :

One more thing we forgot!

review: Needs Fixing
Revision history for this message
Zsombor Egri (zsombi) wrote :

To summarise what we talked on Mumble:
- actions to be grouped in ActionContext
- Action activation to be bound to ActionContext.active
- text input action context activation to be bound with activeFocus
- Page active to drive ActionContext.active

review: Needs Fixing
1540. By Cris Dywan

New shortcut property needs to go on revision 3

1541. By Cris Dywan

Register UCAction revision 1 to 1.3 and shortcut to 1

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) wrote :
review: Needs Fixing (continuous-integration)
1542. By Cris Dywan

Update components.api

Revision history for this message
Zsombor Egri (zsombi) wrote :

All good now, let's get it merged.

review: Approve
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-06-22 23:24:26 +0000
3+++ components.api 2015-07-07 19:08:50 +0000
4@@ -8,7 +8,7 @@
5 signal clicked()
6 signal pressAndHold()
7 property bool pressed
8-Ubuntu.Components.Action 1.0 0.1: QtObject
9+Ubuntu.Components.Action 1.3 1.0 0.1: QtObject
10 property string description
11 property bool enabled
12 property string iconName
13@@ -20,6 +20,7 @@
14 function trigger()
15 property string name
16 property Type parameterType
17+ property var shortcut
18 property string text
19 property bool visible
20 Ubuntu.Components.Action.Type: Enum
21
22=== modified file 'examples/ubuntu-ui-toolkit-gallery/Buttons.qml'
23--- examples/ubuntu-ui-toolkit-gallery/Buttons.qml 2015-04-29 07:21:29 +0000
24+++ examples/ubuntu-ui-toolkit-gallery/Buttons.qml 2015-07-07 19:08:50 +0000
25@@ -48,8 +48,14 @@
26
27 Button {
28 objectName: "button_color"
29- text: i18n.tr("Call")
30- color: UbuntuColors.green
31+ width: units.gu(20)
32+ action: Action {
33+ text: i18n.tr("Call %1").arg(shortcut)
34+ shortcut: 'Ctrl+C'
35+ property bool flipped
36+ onTriggered: flipped = !flipped
37+ }
38+ color: action.flipped ? UbuntuColors.blue : UbuntuColors.green
39 }
40 }
41
42
43=== modified file 'modules/Ubuntu/Components/plugin/plugin.cpp'
44--- modules/Ubuntu/Components/plugin/plugin.cpp 2015-05-26 17:54:51 +0000
45+++ modules/Ubuntu/Components/plugin/plugin.cpp 2015-07-07 19:08:50 +0000
46@@ -213,6 +213,7 @@
47 qmlRegisterSingletonType<UCNamespaceV13>(uri, 1, 3, "Ubuntu", registerUbuntuNamespace13);
48 qmlRegisterType<UCStyledItemBase, 2>(uri, 1, 3, "StyledItem");
49 qmlRegisterCustomType<UCStyleHints>(uri, 1, 3, "StyleHints", new UCStyleHintsParser);
50+ qmlRegisterType<UCAction, 1>(uri, 1, 3, "Action");
51 }
52
53 void UbuntuComponentsPlugin::initializeEngine(QQmlEngine *engine, const char *uri)
54
55=== modified file 'modules/Ubuntu/Components/plugin/ucaction.cpp'
56--- modules/Ubuntu/Components/plugin/ucaction.cpp 2014-11-28 13:30:05 +0000
57+++ modules/Ubuntu/Components/plugin/ucaction.cpp 2015-07-07 19:08:50 +0000
58@@ -17,6 +17,10 @@
59 #include "ucaction.h"
60
61 #include <QtDebug>
62+#include <QtQml/QQmlInfo>
63+#include <QtQuick/qquickitem.h>
64+#include <QtQuick/qquickwindow.h>
65+#include <private/qguiapplication_p.h>
66
67 /*!
68 * \qmltype Action
69@@ -152,6 +156,7 @@
70 , m_published(false)
71 , m_itemHint(0)
72 , m_parameterType(None)
73+ , m_shortcut(0)
74 {
75 generateName();
76 }
77@@ -240,6 +245,67 @@
78 qWarning() << "Action.itemHint is a DEPRECATED property. Use ActionItems to specify the representation of an Action.";
79 }
80
81+bool shortcutContextMatcher(QObject* object, Qt::ShortcutContext)
82+{
83+ UCAction* action = static_cast<UCAction*>(object);
84+ // Can't access member here because it's not public
85+ if (!action->property("enabled").toBool())
86+ return false;
87+
88+ QObject* window = object;
89+ while (window && !window->isWindowType()) {
90+ window = window->parent();
91+ if (QQuickItem* item = qobject_cast<QQuickItem*>(window))
92+ window = item->window();
93+ }
94+ return window && window == QGuiApplication::focusWindow();
95+}
96+
97+QKeySequence sequenceFromVariant(const QVariant& variant) {
98+ if (variant.type() == QVariant::Int)
99+ return static_cast<QKeySequence::StandardKey>(variant.toInt());
100+ if (variant.type() == QVariant::String)
101+ return QKeySequence::fromString(variant.toString());
102+ return QKeySequence();
103+}
104+
105+/*!
106+ * \qmlproperty var Action::shortcut
107+ * The keyboard shortcut that can be used to trigger the action.
108+ * \b StandardKey values such as \b StandardKey.Copy
109+ * as well as strings in the form "Ctrl+C" are accepted values.
110+ * \since 1.3
111+ */
112+void UCAction::setShortcut(const QVariant& shortcut)
113+{
114+ if (m_shortcut.isValid())
115+ QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, sequenceFromVariant(m_shortcut));
116+
117+ QKeySequence sequence(sequenceFromVariant(shortcut));
118+ if (!sequence.toString().isEmpty())
119+ QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, sequence, Qt::WindowShortcut, shortcutContextMatcher);
120+ else
121+ qmlInfo(this) << "Invalid shortcut: " << shortcut.toString();
122+
123+ m_shortcut = shortcut;
124+ Q_EMIT shortcutChanged(shortcut);
125+}
126+
127+bool UCAction::event(QEvent *event)
128+{
129+ if (event->type() != QEvent::Shortcut)
130+ return false;
131+
132+ QShortcutEvent *shortcut_event(static_cast<QShortcutEvent*>(event));
133+ if (shortcut_event->isAmbiguous()) {
134+ qmlInfo(this) << "Ambiguous shortcut: " << shortcut_event->key().toString();
135+ return false;
136+ }
137+
138+ trigger();
139+ return true;
140+}
141+
142 /*!
143 * \qmlmethod Action::trigger(var value)
144 * Checks the \c value against the action \l parameterType and triggers the action.
145
146=== modified file 'modules/Ubuntu/Components/plugin/ucaction.h'
147--- modules/Ubuntu/Components/plugin/ucaction.h 2015-01-07 10:15:34 +0000
148+++ modules/Ubuntu/Components/plugin/ucaction.h 2015-07-07 19:08:50 +0000
149@@ -40,6 +40,9 @@
150 Q_PROPERTY(QUrl iconSource MEMBER m_iconSource WRITE setIconSource NOTIFY iconSourceChanged)
151 Q_PROPERTY(bool visible MEMBER m_visible NOTIFY visibleChanged)
152 Q_PROPERTY(QQmlComponent *itemHint MEMBER m_itemHint WRITE setItemHint)
153+
154+ // QtQuickControls.Action
155+ Q_PROPERTY(QVariant shortcut MEMBER m_shortcut WRITE setShortcut NOTIFY shortcutChanged REVISION 1)
156 public:
157 enum Type {
158 None,
159@@ -67,6 +70,7 @@
160 void parameterTypeChanged();
161 void iconSourceChanged();
162 void visibleChanged();
163+ void shortcutChanged(const QVariant& shortcut);
164 void triggered(const QVariant &value);
165
166 public Q_SLOTS:
167@@ -85,6 +89,7 @@
168 QString m_description;
169 QString m_keywords;
170 Type m_parameterType;
171+ QVariant m_shortcut;
172
173 friend class UCActionContext;
174 friend class UCListItemPrivate;
175@@ -97,6 +102,8 @@
176 void setIconName(const QString &name);
177 void setIconSource(const QUrl &url);
178 void setItemHint(QQmlComponent *);
179+ void setShortcut(const QVariant&);
180+ bool event(QEvent *event);
181 };
182
183 #endif // UCACTION_H
184
185=== added file 'tests/unit_x11/tst_components/tst_shortcuts.qml'
186--- tests/unit_x11/tst_components/tst_shortcuts.qml 1970-01-01 00:00:00 +0000
187+++ tests/unit_x11/tst_components/tst_shortcuts.qml 2015-07-07 19:08:50 +0000
188@@ -0,0 +1,92 @@
189+/*
190+ * Copyright 2015 Canonical Ltd.
191+ *
192+ * This program is free software; you can redistribute it and/or modify
193+ * it under the terms of the GNU Lesser General Public License as published by
194+ * the Free Software Foundation; version 3.
195+ *
196+ * This program is distributed in the hope that it will be useful,
197+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
198+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
199+ * GNU Lesser General Public License for more details.
200+ *
201+ * You should have received a copy of the GNU Lesser General Public License
202+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
203+ */
204+
205+import QtQuick 2.4
206+import QtTest 1.0
207+import Ubuntu.Test 1.0
208+import Ubuntu.Components 1.3
209+
210+Item {
211+ id: root
212+ width: 400
213+ height: 600
214+
215+ Action {
216+ id: action
217+ }
218+ Action {
219+ id: other
220+ shortcut: 'Ctrl+G'
221+ }
222+
223+ TestUtil {
224+ id: util
225+ }
226+
227+ UbuntuTestCase {
228+ id: testCase
229+ name: "Shortcuts"
230+ when: windowShown
231+
232+ function initTestCase() {
233+ }
234+
235+ function init() {
236+ }
237+
238+ SignalSpy {
239+ id: spy
240+ signalName: 'triggered'
241+ target: action
242+ }
243+
244+ function ignoreQMLWarning(message) {
245+ ignoreWarning(util.callerFile() + message);
246+ }
247+
248+ function test_shortcut_triggered_data() {
249+ return [
250+ { tag: 'Multiple modifiers and letter', shortcut: 'Ctrl+Shift+Alt+A', key: Qt.Key_A, mod: Qt.ControlModifier + Qt.ShiftModifier + Qt.AltModifier },
251+ { tag: 'Modifier and letter', shortcut: 'Ctrl+A', key: Qt.Key_A, mod: Qt.ControlModifier },
252+ { tag: 'Single letter', shortcut: 'E', key: Qt.Key_E, mod: Qt.NoModifier },
253+ { tag: 'StandardKey', shortcut: StandardKey.Copy, key: Qt.Key_C, mod: Qt.ControlModifier }
254+ ];
255+ }
256+ function test_shortcut_triggered(data) {
257+ action.shortcut = data.shortcut;
258+ spy.clear();
259+ keyClick(data.key, data.mod);
260+ spy.wait();
261+ }
262+
263+ function test_shortcut_invalid_data() {
264+ return [
265+ { tag: 'Typo', shortcut: 'Ctr+F' },
266+ { tag: 'Number', shortcut: 1234567890 }
267+ ];
268+ }
269+ function test_shortcut_invalid(data) {
270+ ignoreQMLWarning(':27:5: QML Action: Invalid shortcut: ');
271+ action.shortcut = data;
272+ }
273+
274+ function test_shortcut_duplicate() {
275+ ignoreQMLWarning(':30:5: QML Action: Ambiguous shortcut: Ctrl+G');
276+ action.shortcut = other.shortcut;
277+ keyClick(Qt.Key_G, Qt.ControlModifier);
278+ }
279+ }
280+}

Subscribers

People subscribed via source and target branches