Merge lp:~nick-dedekind/unity8/desktop_menus.gmenumodel into lp:unity8
- desktop_menus.gmenumodel
- Merge into trunk
Status: | Work in progress |
---|---|
Proposed branch: | lp:~nick-dedekind/unity8/desktop_menus.gmenumodel |
Merge into: | lp:unity8 |
Diff against target: |
3885 lines (+3069/-96) 44 files modified
CMakeLists.txt (+1/-1) debian/control (+2/-2) plugins/Utils/CMakeLists.txt (+1/-0) plugins/Utils/constants.cpp (+1/-0) plugins/Utils/constants.h (+3/-0) plugins/Utils/plugin.cpp (+2/-0) plugins/Utils/shortcutaction.cpp (+122/-0) plugins/Utils/shortcutaction.h (+44/-0) plugins/Utils/windowkeysfilter.cpp (+11/-6) plugins/Utils/windowkeysfilter.h (+1/-1) qml/ApplicationMenus/AppMenuUtils.js (+107/-0) qml/ApplicationMenus/MenuBar.qml (+245/-0) qml/ApplicationMenus/MenuBarItem.qml (+56/-0) qml/ApplicationMenus/MenuBase.qml (+116/-0) qml/ApplicationMenus/MenuItemBase.qml (+32/-0) qml/ApplicationMenus/MenuItemDelegate.qml (+49/-0) qml/ApplicationMenus/MenuItemDelegateBase.qml (+82/-0) qml/ApplicationMenus/MenuItemFactory.qml (+240/-0) qml/ApplicationMenus/MenuPage.qml (+277/-0) qml/CMakeLists.txt (+1/-0) qml/Components/PanelState/PanelState.qml (+2/-1) qml/Panel/Panel.qml (+91/-7) qml/Stages/DecoratedWindow.qml (+50/-16) qml/Stages/DesktopStage.qml (+10/-3) qml/Stages/WindowDecoration.qml (+117/-45) tests/imports/check_imports.py (+2/-1) tests/mocks/QMenuModel/QMenuModel.qmltypes (+5/-1) tests/mocks/QMenuModel/unitymenumodel.cpp (+25/-3) tests/mocks/QMenuModel/unitymenumodel.h (+2/-0) tests/mocks/Unity/Application/MirSurface.cpp (+10/-0) tests/mocks/Unity/Application/MirSurface.h (+3/-0) tests/mocks/Utils/CMakeLists.txt (+1/-0) tests/mocks/Utils/constants.cpp (+7/-0) tests/mocks/Utils/constants.h (+7/-0) tests/mocks/Utils/plugin.cpp (+2/-0) tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+132/-0) tests/qmltests/ApplicationMenus/tst_MenuPage.qml (+256/-0) tests/qmltests/CMakeLists.txt (+3/-0) tests/qmltests/Panel/tst_Panel.qml (+56/-4) tests/qmltests/Stages/ApplicationCheckBox.qml (+4/-0) tests/qmltests/Stages/DesktopMenuData.qml (+702/-0) tests/qmltests/Stages/tst_DesktopStage.qml (+13/-1) tests/qmltests/Stages/tst_WindowDecoration.qml (+163/-0) tests/qmltests/tst_Shell.qml (+13/-4) |
To merge this branch: | bzr merge lp:~nick-dedekind/unity8/desktop_menus.gmenumodel |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
PS Jenkins bot (community) | continuous-integration | Needs Fixing | |
Unity8 CI Bot | continuous-integration | Needs Fixing | |
Unity Team | Pending | ||
Review via email: mp+282727@code.launchpad.net |
Commit message
Desktop Menus
Description of the change
Desktop Menus
- 2110. By Nick Dedekind
-
updated control
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2110
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
- 2111. By Nick Dedekind
-
merged with trunk
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2111
https:/
Executed test runs:
Click here to trigger a rebuild:
https:/
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2110
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
PS Jenkins bot (ps-jenkins) wrote : | # |
FAILED: Continuous integration, rev:2111
http://
Executed test runs:
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
FAILURE: http://
Click here to trigger a rebuild:
http://
Unmerged revisions
- 2111. By Nick Dedekind
-
merged with trunk
- 2110. By Nick Dedekind
-
updated control
- 2109. By Nick Dedekind
-
desktop menus
- 2108. By Launchpad Translations on behalf of unity-team
-
Launchpad automatic translations update.
Preview Diff
1 | === modified file 'CMakeLists.txt' |
2 | --- CMakeLists.txt 2015-12-16 13:58:39 +0000 |
3 | +++ CMakeLists.txt 2016-01-15 13:11:04 +0000 |
4 | @@ -57,7 +57,7 @@ |
5 | find_package(Qt5Concurrent 5.4 REQUIRED) |
6 | find_package(Qt5Sql 5.4 REQUIRED) |
7 | |
8 | -pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=12) |
9 | +pkg_check_modules(APPLICATION_API REQUIRED unity-shell-application=13) |
10 | |
11 | # Standard install paths |
12 | include(GNUInstallDirs) |
13 | |
14 | === modified file 'debian/control' |
15 | --- debian/control 2016-01-11 17:37:16 +0000 |
16 | +++ debian/control 2016-01-15 13:11:04 +0000 |
17 | @@ -29,7 +29,7 @@ |
18 | libqt5xmlpatterns5-dev, |
19 | libsystemsettings-dev, |
20 | libudev-dev, |
21 | - libunity-api-dev (>= 7.105), |
22 | + libunity-api-dev (>= 7.106), |
23 | libusermetricsoutput1-dev, |
24 | # Need those X11 libs touch emulation from mouse events in manual QML tests on a X11 desktop |
25 | libx11-dev[!armhf], |
26 | @@ -102,7 +102,7 @@ |
27 | qml-module-qtquick-xmllistmodel, |
28 | qml-module-qtsysteminfo, |
29 | qtdeclarative5-gsettings1.0, |
30 | - qtdeclarative5-qtmir-plugin (>= 0.4.5), |
31 | + qtdeclarative5-qtmir-plugin (>= 0.4.7), |
32 | qtdeclarative5-ubuntu-telephony0.1, |
33 | qtdeclarative5-ubuntu-web-plugin, |
34 | ubuntu-system-settings, |
35 | |
36 | === modified file 'plugins/Utils/CMakeLists.txt' |
37 | --- plugins/Utils/CMakeLists.txt 2015-10-28 10:32:47 +0000 |
38 | +++ plugins/Utils/CMakeLists.txt 2016-01-15 13:11:04 +0000 |
39 | @@ -24,6 +24,7 @@ |
40 | easingcurve.cpp |
41 | windowstatestorage.cpp |
42 | timezoneFormatter.cpp |
43 | + shortcutaction.cpp |
44 | plugin.cpp |
45 | ) |
46 | |
47 | |
48 | === modified file 'plugins/Utils/constants.cpp' |
49 | --- plugins/Utils/constants.cpp 2015-11-20 15:01:39 +0000 |
50 | +++ plugins/Utils/constants.cpp 2016-01-15 13:11:04 +0000 |
51 | @@ -24,4 +24,5 @@ |
52 | } else { |
53 | m_indicatorValueTimeout = 5000; |
54 | } |
55 | + m_menuHoverOpenInterval = 250; |
56 | } |
57 | |
58 | === modified file 'plugins/Utils/constants.h' |
59 | --- plugins/Utils/constants.h 2015-03-11 08:07:31 +0000 |
60 | +++ plugins/Utils/constants.h 2016-01-15 13:11:04 +0000 |
61 | @@ -30,14 +30,17 @@ |
62 | { |
63 | Q_OBJECT |
64 | Q_PROPERTY(int indicatorValueTimeout READ indicatorValueTimeout CONSTANT) |
65 | + Q_PROPERTY(int menuHoverOpenInterval READ menuHoverOpenInterval CONSTANT) |
66 | |
67 | public: |
68 | Constants(QObject *parent = 0); |
69 | |
70 | int indicatorValueTimeout() const { return m_indicatorValueTimeout; } |
71 | + int menuHoverOpenInterval() const { return m_menuHoverOpenInterval; } |
72 | |
73 | private: |
74 | int m_indicatorValueTimeout; |
75 | + int m_menuHoverOpenInterval; |
76 | }; |
77 | |
78 | #endif |
79 | |
80 | === modified file 'plugins/Utils/plugin.cpp' |
81 | --- plugins/Utils/plugin.cpp 2015-10-26 16:47:52 +0000 |
82 | +++ plugins/Utils/plugin.cpp 2016-01-15 13:11:04 +0000 |
83 | @@ -36,6 +36,7 @@ |
84 | #include "constants.h" |
85 | #include "timezoneFormatter.h" |
86 | #include "applicationsfiltermodel.h" |
87 | +#include "shortcutaction.h" |
88 | |
89 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
90 | { |
91 | @@ -68,6 +69,7 @@ |
92 | [](QQmlEngine*, QJSEngine*) -> QObject* { return new TimezoneFormatter; }); |
93 | qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger"); |
94 | qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel"); |
95 | + qmlRegisterType<ShortcutAction>(uri, 0, 1, "ShortcutAction"); |
96 | } |
97 | |
98 | void UtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
99 | |
100 | === added file 'plugins/Utils/shortcutaction.cpp' |
101 | --- plugins/Utils/shortcutaction.cpp 1970-01-01 00:00:00 +0000 |
102 | +++ plugins/Utils/shortcutaction.cpp 2016-01-15 13:11:04 +0000 |
103 | @@ -0,0 +1,122 @@ |
104 | +#include "shortcutaction.h" |
105 | + |
106 | +#include <QQuickItem> |
107 | +#include <QQuickWindow> |
108 | +#include <QVariant> |
109 | +#include <QShortcutEvent> |
110 | +#include <private/qguiapplication_p.h> |
111 | + |
112 | +namespace { |
113 | + |
114 | +bool qShortcutContextMatcher(QObject *o, Qt::ShortcutContext context) |
115 | +{ |
116 | + if (!static_cast<ShortcutAction*>(o)->isEnabled()) |
117 | + return false; |
118 | + |
119 | + switch (context) { |
120 | + case Qt::ApplicationShortcut: |
121 | + case Qt::WindowShortcut: |
122 | + break; |
123 | + case Qt::WidgetShortcut: { |
124 | + QQuickItem* target = static_cast<ShortcutAction*>(o)->target(); |
125 | + if (target && target->hasActiveFocus()) { |
126 | + return true; |
127 | + } |
128 | + return true; |
129 | + } |
130 | + case Qt::WidgetWithChildrenShortcut: |
131 | + break; |
132 | + } |
133 | + |
134 | + return false; |
135 | +} |
136 | + |
137 | +} |
138 | + |
139 | + |
140 | +ShortcutAction::ShortcutAction(QObject* parent) |
141 | + : QObject(parent) |
142 | + , m_enabled(true) |
143 | +{ |
144 | +} |
145 | + |
146 | +ShortcutAction::~ShortcutAction() |
147 | +{ |
148 | + setShortcut(QString()); |
149 | +} |
150 | + |
151 | +QVariant ShortcutAction::shortcut() const |
152 | +{ |
153 | + return m_shortcut.toString(QKeySequence::NativeText); |
154 | +} |
155 | + |
156 | +void ShortcutAction::setShortcut(const QVariant &arg) |
157 | +{ |
158 | + QKeySequence sequence; |
159 | + if (arg.type() == QVariant::Int) |
160 | + sequence = QKeySequence(static_cast<QKeySequence::StandardKey>(arg.toInt())); |
161 | + else |
162 | + sequence = QKeySequence::fromString(arg.toString()); |
163 | + |
164 | + if (sequence == m_shortcut) |
165 | + return; |
166 | + |
167 | + if (!m_shortcut.isEmpty()) |
168 | + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(0, this, m_shortcut); |
169 | + |
170 | + m_shortcut = sequence; |
171 | + |
172 | + if (!m_shortcut.isEmpty()) { |
173 | + Qt::ShortcutContext context = Qt::WidgetShortcut; |
174 | + QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(this, m_shortcut, context, qShortcutContextMatcher); |
175 | + } |
176 | + Q_EMIT shortcutChanged(shortcut()); |
177 | +} |
178 | + |
179 | +void ShortcutAction::setEnabled(bool e) |
180 | +{ |
181 | + if (e == m_enabled) return; |
182 | + m_enabled = e; |
183 | + |
184 | + Q_EMIT enabledChanged(); |
185 | +} |
186 | + |
187 | +void ShortcutAction::setTarget(QQuickItem *target) |
188 | +{ |
189 | + if (target == m_target) return; |
190 | + m_target = target; |
191 | + |
192 | + Q_EMIT targetChanged(); |
193 | +} |
194 | + |
195 | +bool ShortcutAction::event(QEvent *e) |
196 | +{ |
197 | + if (!m_enabled) |
198 | + return false; |
199 | + |
200 | + if (e->type() != QEvent::Shortcut) |
201 | + return false; |
202 | + |
203 | + QShortcutEvent *se = static_cast<QShortcutEvent *>(e); |
204 | + |
205 | + Q_ASSERT_X(se->key() == m_shortcut, |
206 | + "QQuickAction::event", |
207 | + "Received shortcut event from incorrect shortcut"); |
208 | + if (se->isAmbiguous()) { |
209 | + qWarning("QQuickAction::event: Ambiguous shortcut overload: %s", se->key().toString(QKeySequence::NativeText).toLatin1().constData()); |
210 | + return false; |
211 | + } |
212 | + |
213 | + trigger(); |
214 | + |
215 | + return true; |
216 | +} |
217 | + |
218 | +void ShortcutAction::trigger(QObject* source) |
219 | +{ |
220 | + if (!m_enabled) |
221 | + return; |
222 | + |
223 | + Q_EMIT triggered(source); |
224 | +} |
225 | + |
226 | |
227 | === added file 'plugins/Utils/shortcutaction.h' |
228 | --- plugins/Utils/shortcutaction.h 1970-01-01 00:00:00 +0000 |
229 | +++ plugins/Utils/shortcutaction.h 2016-01-15 13:11:04 +0000 |
230 | @@ -0,0 +1,44 @@ |
231 | +#ifndef SHORTCUTACTION_H |
232 | +#define SHORTCUTACTION_H |
233 | + |
234 | +#include <QObject> |
235 | +#include <QKeySequence> |
236 | +#include <QQuickItem> |
237 | + |
238 | +class ShortcutAction : public QObject |
239 | +{ |
240 | + Q_OBJECT |
241 | + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) |
242 | + Q_PROPERTY(QVariant shortcut READ shortcut WRITE setShortcut NOTIFY shortcutChanged) |
243 | + Q_PROPERTY(QQuickItem* target READ target WRITE setTarget NOTIFY targetChanged) |
244 | + |
245 | +public: |
246 | + ShortcutAction(QObject* parent = nullptr); |
247 | + ~ShortcutAction(); |
248 | + |
249 | + QVariant shortcut() const; |
250 | + void setShortcut(const QVariant &shortcut); |
251 | + |
252 | + bool isEnabled() const { return m_enabled; } |
253 | + void setEnabled(bool e); |
254 | + |
255 | + QQuickItem* target() const { return m_target; } |
256 | + void setTarget(QQuickItem* target); |
257 | + |
258 | + bool event(QEvent *e) override; |
259 | + |
260 | + void trigger(QObject* source = nullptr); |
261 | + |
262 | +Q_SIGNALS: |
263 | + void targetChanged(); |
264 | + void enabledChanged(); |
265 | + void triggered(QObject* source = nullptr); |
266 | + void shortcutChanged(QVariant shortcut); |
267 | + |
268 | +private: |
269 | + QKeySequence m_shortcut; |
270 | + bool m_enabled; |
271 | + QQuickItem* m_target; |
272 | +}; |
273 | + |
274 | +#endif // SHORTCUTACTION_H |
275 | |
276 | === modified file 'plugins/Utils/windowkeysfilter.cpp' |
277 | --- plugins/Utils/windowkeysfilter.cpp 2015-10-22 17:37:26 +0000 |
278 | +++ plugins/Utils/windowkeysfilter.cpp 2016-01-15 13:11:04 +0000 |
279 | @@ -24,12 +24,14 @@ |
280 | : QQuickItem(parent), |
281 | m_currentEventTimestamp(0) |
282 | { |
283 | - connect(this, &QQuickItem::windowChanged, |
284 | - this, &WindowKeysFilter::setupFilterOnWindow); |
285 | + connect(this, &QQuickItem::windowChanged, this, &WindowKeysFilter::setupFilter); |
286 | + connect(this, &QQuickItem::enabledChanged, this, &WindowKeysFilter::setupFilter); |
287 | } |
288 | |
289 | bool WindowKeysFilter::eventFilter(QObject *watched, QEvent *event) |
290 | { |
291 | + if (!isEnabled()) return false; |
292 | + |
293 | Q_ASSERT(!m_filteredWindow.isNull()); |
294 | Q_ASSERT(watched == static_cast<QObject*>(m_filteredWindow.data())); |
295 | Q_UNUSED(watched); |
296 | @@ -53,16 +55,19 @@ |
297 | } |
298 | } |
299 | |
300 | -void WindowKeysFilter::setupFilterOnWindow(QQuickWindow *window) |
301 | +void WindowKeysFilter::setupFilter() |
302 | { |
303 | + if (!m_filteredWindow.isNull() == isEnabled() && m_filteredWindow.data() == window()) return; |
304 | + |
305 | if (!m_filteredWindow.isNull()) { |
306 | m_filteredWindow->removeEventFilter(this); |
307 | m_filteredWindow.clear(); |
308 | } |
309 | |
310 | - if (window) { |
311 | - window->installEventFilter(this); |
312 | - m_filteredWindow = window; |
313 | + QQuickWindow *wnd = window(); |
314 | + if (wnd && isEnabled()) { |
315 | + wnd->installEventFilter(this); |
316 | + m_filteredWindow = wnd; |
317 | } |
318 | } |
319 | |
320 | |
321 | === modified file 'plugins/Utils/windowkeysfilter.h' |
322 | --- plugins/Utils/windowkeysfilter.h 2015-10-22 17:37:26 +0000 |
323 | +++ plugins/Utils/windowkeysfilter.h 2016-01-15 13:11:04 +0000 |
324 | @@ -48,7 +48,7 @@ |
325 | void currentEventTimestampChanged(); |
326 | |
327 | private Q_SLOTS: |
328 | - void setupFilterOnWindow(QQuickWindow *window); |
329 | + void setupFilter(); |
330 | |
331 | private: |
332 | QPointer<QQuickWindow> m_filteredWindow; |
333 | |
334 | === added directory 'qml/ApplicationMenus' |
335 | === added file 'qml/ApplicationMenus/AppMenuUtils.js' |
336 | --- qml/ApplicationMenus/AppMenuUtils.js 1970-01-01 00:00:00 +0000 |
337 | +++ qml/ApplicationMenus/AppMenuUtils.js 2016-01-15 13:11:04 +0000 |
338 | @@ -0,0 +1,107 @@ |
339 | +/* |
340 | + * Copyright 2015 Canonical Ltd. |
341 | + * |
342 | + * This program is free software; you can redistribute it and/or modify |
343 | + * it under the terms of the GNU Lesser General Public License as published by |
344 | + * the Free Software Foundation; version 3. |
345 | + * |
346 | + * This program is distributed in the hope that it will be useful, |
347 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
348 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
349 | + * GNU Lesser General Public License for more details. |
350 | + * |
351 | + * You should have received a copy of the GNU Lesser General Public License |
352 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
353 | + */ |
354 | + |
355 | +.pragma library |
356 | + |
357 | +String.prototype.htmlLabelFromMenuLabel = function (underline) { |
358 | + var replacements = [ |
359 | + { |
360 | + char: "_", |
361 | + searchExpr: /_/g, |
362 | + doubleReplacement: "_" |
363 | + }, { |
364 | + char: "&", |
365 | + searchExpr: /&(?!amp;)/g, |
366 | + doubleReplacement: "&" |
367 | + } |
368 | + ]; |
369 | + var text = this; |
370 | + |
371 | + var i = 0; |
372 | + for (; i < replacements.length; i++) { |
373 | + text = parseShortcutChararacter(text, underline, replacements[i]); |
374 | + } |
375 | + return text; |
376 | +} |
377 | + |
378 | +function parseShortcutChararacter(text, underline, replacement) { |
379 | + text = text.replace(new RegExp(replacement.char + replacement.char, "g"), replacement.doubleReplacement); |
380 | + |
381 | + if (!underline) { |
382 | + return text.replace(replacement.searchExpr, ""); |
383 | + } |
384 | + |
385 | + var original = text; |
386 | + var output = ""; |
387 | + var current = 0 |
388 | + var next = text.search(replacement.searchExpr); |
389 | + if (next === -1) return text; |
390 | + |
391 | + while (next !== -1) { |
392 | + output += text.slice(current, next); |
393 | + current = next+1; |
394 | + |
395 | + var rest = text.slice(current); |
396 | + text = "<u>" + rest[0] + "</u>" + rest.slice(1); |
397 | + next = text.search(replacement.searchExpr); |
398 | + } |
399 | + output += text; |
400 | + return output; |
401 | +} |
402 | + |
403 | +String.prototype.actionKeyFromMenuLabel = function () { |
404 | + var replacements = [ |
405 | + { |
406 | + char: "_", |
407 | + searchExpr: /_/g, |
408 | + doubleReplacement: "_" |
409 | + }, { |
410 | + char: "&", |
411 | + searchExpr: /&(?!amp;)/g, |
412 | + doubleReplacement: "&" |
413 | + } |
414 | + ]; |
415 | + var text = this; |
416 | + |
417 | + var i = 0; |
418 | + var replaced = false; |
419 | + for (; i < replacements.length; i++) { |
420 | + var actionKey = parseActionKeyChararacter(text, replacements[i]); |
421 | + if (actionKey !== "") return actionKey; |
422 | + } |
423 | + return ""; |
424 | +} |
425 | + |
426 | +function parseActionKeyChararacter(original, replacement) { |
427 | + var text = original.replace(new RegExp(replacement.char + replacement.char, "g"), replacement.doubleReplacement); |
428 | + |
429 | + var output = ""; |
430 | + var iter = 0; |
431 | + var next = text.search(replacement.searchExpr); |
432 | + if (next === -1) return ""; |
433 | + |
434 | + while (next !== -1) { |
435 | + var char = text.slice(next+1, next+2); |
436 | + if (char === "") break; |
437 | + |
438 | + if (iter > 0) output += "+"; |
439 | + output += char; |
440 | + |
441 | + text = text.slice(next+2); |
442 | + next = text.search(replacement.searchExpr) |
443 | + } |
444 | + return output; |
445 | +} |
446 | |
447 | === added file 'qml/ApplicationMenus/MenuBar.qml' |
448 | --- qml/ApplicationMenus/MenuBar.qml 1970-01-01 00:00:00 +0000 |
449 | +++ qml/ApplicationMenus/MenuBar.qml 2016-01-15 13:11:04 +0000 |
450 | @@ -0,0 +1,245 @@ |
451 | +/* |
452 | + * Copyright 2015 Canonical Ltd. |
453 | + * |
454 | + * This program is free software; you can redistribute it and/or modify |
455 | + * it under the terms of the GNU Lesser General Public License as published by |
456 | + * the Free Software Foundation; version 3. |
457 | + * |
458 | + * This program is distributed in the hope that it will be useful, |
459 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
460 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
461 | + * GNU Lesser General Public License for more details. |
462 | + * |
463 | + * You should have received a copy of the GNU Lesser General Public License |
464 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
465 | + */ |
466 | + |
467 | +import QtQuick 2.4 |
468 | +import QtQuick.Layouts 1.1 |
469 | +import Ubuntu.Components 1.3 |
470 | + |
471 | +MenuBase { |
472 | + id: root |
473 | + objectName: "menuBar" |
474 | + |
475 | + property var focusWindow: undefined |
476 | + property alias menuModel: rootDelegate.menuModel |
477 | + implicitWidth: listView.width |
478 | + readonly property bool hasChildren: repeater.count > 0 |
479 | + submenuOffset: -units.gu(1) |
480 | + |
481 | + function close() { |
482 | + closePopup(); |
483 | + listView.selectedItem = undefined; |
484 | + } |
485 | + |
486 | + FocusScope { |
487 | + id: scope |
488 | + anchors { |
489 | + left: parent.left |
490 | + } |
491 | + width: listView.width |
492 | + height: parent.height |
493 | + |
494 | + Keys.onLeftPressed: listView.selectPrevious() |
495 | + Keys.onRightPressed: listView.selectNext() |
496 | + Keys.onEscapePressed: { |
497 | + focus = false; |
498 | + if (focusWindow !== undefined) { |
499 | + focusWindow.forceActiveFocus(); |
500 | + } |
501 | + } |
502 | + |
503 | + onFocusChanged: { |
504 | + if (!focus && root.openItem) { |
505 | + root.close(); |
506 | + } |
507 | + } |
508 | + |
509 | + InverseMouseArea { |
510 | + enabled: root.openItem !== undefined |
511 | + acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton |
512 | + anchors.fill: parent |
513 | + |
514 | + onPressed: { |
515 | + // stop the inverse mouse area stealing events from mouse areas under. |
516 | + mouse.accepted = false; |
517 | + root.close(); |
518 | + } |
519 | + } |
520 | + |
521 | + MenuItemDelegateBase { |
522 | + id: rootDelegate |
523 | + focusWindow: root.focusWindow |
524 | + } |
525 | + |
526 | + Row { |
527 | + id: listView |
528 | + anchors.left: parent.left |
529 | + height: parent.height |
530 | + spacing: units.gu(2) |
531 | + |
532 | + property var selectedItem: undefined |
533 | + |
534 | + function selectNext() { |
535 | + var delegate; |
536 | + var newIndex = 0; |
537 | + if (listView.selectedItem === undefined && repeater.count > 0) { |
538 | + while (repeater.count > newIndex) { |
539 | + delegate = repeater.itemAt(newIndex++); |
540 | + if (delegate.enabled) { |
541 | + delegate.selected = true; |
542 | + break; |
543 | + } |
544 | + } |
545 | + } else if (listView.selectedItem !== undefined && repeater.count > 1) { |
546 | + var startIndex = (listView.selectedItem.ownIndex + 1) % repeater.count; |
547 | + newIndex = startIndex; |
548 | + do { |
549 | + delegate = repeater.itemAt(newIndex); |
550 | + if (delegate.enabled) { |
551 | + delegate.selected = true; |
552 | + break; |
553 | + } |
554 | + newIndex = (newIndex + 1) % repeater.count; |
555 | + } while (newIndex !== startIndex) |
556 | + } |
557 | + } |
558 | + |
559 | + function selectPrevious() { |
560 | + var delegate; |
561 | + var newIndex = repeater.count-1; |
562 | + if (listView.selectedItem === undefined && repeater.count > 0) { |
563 | + while (repeater.count > newIndex) { |
564 | + delegate = repeater.itemAt(newIndex--); |
565 | + if (delegate.enabled) { |
566 | + delegate.selected = true; |
567 | + break; |
568 | + } |
569 | + } |
570 | + } else if (listView.selectedItem !== undefined && repeater.count > 1) { |
571 | + var startIndex = listView.selectedItem.ownIndex - 1; |
572 | + newIndex = startIndex; |
573 | + do { |
574 | + if (newIndex < 0) newIndex = repeater.count - 1; |
575 | + delegate = repeater.itemAt(newIndex--); |
576 | + if (delegate.enabled) { |
577 | + delegate.selected = true; |
578 | + break; |
579 | + } |
580 | + } while (newIndex !== startIndex) |
581 | + } |
582 | + } |
583 | + |
584 | + Repeater { |
585 | + id: repeater |
586 | + model: rootDelegate.submenuItems |
587 | + |
588 | + MenuBarItem { |
589 | + id: repeaterItem |
590 | + height: listView.height |
591 | + |
592 | + enableMnemonic: root.enableMnemonic |
593 | + menuItemDelegate: model.delegate |
594 | + |
595 | + property int modelIndex: index |
596 | + property bool selected: false |
597 | + enabled: model.delegate.sensitive && !model.delegate.isSeparator |
598 | + |
599 | + Rectangle { |
600 | + anchors { |
601 | + left: parent.left |
602 | + right: parent.right |
603 | + bottom: parent.bottom |
604 | + leftMargin: -units.gu(1) |
605 | + rightMargin: -units.gu(1) |
606 | + } |
607 | + height: units.dp(4) |
608 | + color: "#E95420" |
609 | + visible: root.openItem == menuItem |
610 | + } |
611 | + |
612 | + MenuItemBase { |
613 | + id: menuItem |
614 | + objectName: root.objectName + "-menu" + index |
615 | + property int ownIndex: index |
616 | + |
617 | + anchors.fill: parent |
618 | + delegate: model.delegate |
619 | + |
620 | + mnemonicAction { |
621 | + property string action: model.delegate.label.actionKeyFromMenuLabel() |
622 | + enabled: enableMnemonic && repeaterItem.enabled |
623 | + shortcut: action !== "" ? "Alt+" + action : "" |
624 | + |
625 | + onTriggered: { |
626 | + if (model.delegate.hasSubmenu) { |
627 | + root.open(menuItem, true); |
628 | + } |
629 | + else { |
630 | + model.delegate.activate(); |
631 | + } |
632 | + } |
633 | + } |
634 | + } |
635 | + |
636 | + MouseArea { |
637 | + id: titleMouseArea |
638 | + anchors.fill: parent |
639 | + hoverEnabled: listView.selectedItem !== undefined |
640 | + |
641 | + onEntered: { |
642 | + if (listView.selectedItem !== undefined && !repeaterItem.selected) { |
643 | + repeaterItem.selected = true; |
644 | + } |
645 | + } |
646 | + onClicked: { |
647 | + if (model.delegate.hasSubmenu) { |
648 | + root.open(menuItem, true); |
649 | + } |
650 | + else if (root.enabled) { |
651 | + model.delegate.activate() |
652 | + } |
653 | + } |
654 | + } |
655 | + |
656 | + onSelectedChanged: { |
657 | + if (selected) { |
658 | + listView.selectedItem = menuItem; |
659 | + if (model.delegate.hasSubmenu && root.openItem !== menuItem) { |
660 | + root.open(menuItem, true); |
661 | + } |
662 | + } |
663 | + else if (listView.selectedItem === menuItem) { |
664 | + listView.selectedItem = undefined; |
665 | + if (root.openItem == menuItem) { |
666 | + root.closePopup(); |
667 | + } |
668 | + } |
669 | + } |
670 | + |
671 | + Connections { |
672 | + target: listView |
673 | + onSelectedItemChanged: { |
674 | + if (repeaterItem.selected && (listView.selectedItem === undefined || listView.selectedItem !== menuItem)) { |
675 | + repeaterItem.selected = false; |
676 | + if (root.openItem == menuItem) { |
677 | + root.closePopup(); |
678 | + } |
679 | + } |
680 | + } |
681 | + } |
682 | + |
683 | + Connections { |
684 | + target: root |
685 | + onOpenItemChanged: { |
686 | + if (root.openItem == menuItem) { |
687 | + repeaterItem.selected = true; |
688 | + } |
689 | + } |
690 | + } |
691 | + } |
692 | + } |
693 | + } |
694 | + } |
695 | +} |
696 | |
697 | === added file 'qml/ApplicationMenus/MenuBarItem.qml' |
698 | --- qml/ApplicationMenus/MenuBarItem.qml 1970-01-01 00:00:00 +0000 |
699 | +++ qml/ApplicationMenus/MenuBarItem.qml 2016-01-15 13:11:04 +0000 |
700 | @@ -0,0 +1,56 @@ |
701 | +/* |
702 | + * Copyright 2015 Canonical Ltd. |
703 | + * |
704 | + * This program is free software; you can redistribute it and/or modify |
705 | + * it under the terms of the GNU Lesser General Public License as published by |
706 | + * the Free Software Foundation; version 3. |
707 | + * |
708 | + * This program is distributed in the hope that it will be useful, |
709 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
710 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
711 | + * GNU Lesser General Public License for more details. |
712 | + * |
713 | + * You should have received a copy of the GNU Lesser General Public License |
714 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
715 | + */ |
716 | + |
717 | +import QtQuick 2.4 |
718 | +import Ubuntu.Settings.Menus 0.1 as Menus |
719 | +import Ubuntu.Components.ListItems 1.3 as ListItems |
720 | +import Ubuntu.Components 1.3 |
721 | +import QtQuick.Layouts 1.1 |
722 | + |
723 | +import "AppMenuUtils.js" as AppMenuUtils |
724 | + |
725 | +Item { |
726 | + id: root |
727 | + property var menuItemDelegate: undefined |
728 | + property bool enableMnemonic: false |
729 | + |
730 | + implicitWidth: column.implicitWidth |
731 | + implicitHeight: column.height |
732 | + |
733 | + RowLayout { |
734 | + id: column |
735 | + spacing: units.gu(1) |
736 | + anchors { |
737 | + centerIn: parent |
738 | + } |
739 | + |
740 | + Icon { |
741 | + Layout.preferredWidth: units.gu(2) |
742 | + Layout.preferredHeight: units.gu(2) |
743 | + Layout.alignment: Qt.AlignVCenter |
744 | + |
745 | + visible: menuItemDelegate && menuItemDelegate.icon |
746 | + source: menuItemDelegate && menuItemDelegate.icon || "" |
747 | + } |
748 | + |
749 | + Label { |
750 | + id: _title |
751 | + text: menuItemDelegate.label.htmlLabelFromMenuLabel(enableMnemonic) || "" |
752 | + horizontalAlignment: Text.AlignLeft |
753 | + color: enabled ? "white" : "#5d5d5d" |
754 | + } |
755 | + } |
756 | +} |
757 | |
758 | === added file 'qml/ApplicationMenus/MenuBase.qml' |
759 | --- qml/ApplicationMenus/MenuBase.qml 1970-01-01 00:00:00 +0000 |
760 | +++ qml/ApplicationMenus/MenuBase.qml 2016-01-15 13:11:04 +0000 |
761 | @@ -0,0 +1,116 @@ |
762 | +/* |
763 | + * Copyright 2015 Canonical Ltd. |
764 | + * |
765 | + * This program is free software; you can redistribute it and/or modify |
766 | + * it under the terms of the GNU Lesser General Public License as published by |
767 | + * the Free Software Foundation; version 3. |
768 | + * |
769 | + * This program is distributed in the hope that it will be useful, |
770 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
771 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
772 | + * GNU Lesser General Public License for more details. |
773 | + * |
774 | + * You should have received a copy of the GNU Lesser General Public License |
775 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
776 | + */ |
777 | + |
778 | +import QtQuick 2.4 |
779 | +import Utils 0.1 |
780 | + |
781 | +Item { |
782 | + id: root |
783 | + |
784 | + property bool enableMnemonic: false |
785 | + property bool opensVertically: true |
786 | + readonly property alias openItem: d._openItem |
787 | + property real submenuOffset: 0 |
788 | + |
789 | + function open(menuItem, focus, focusIndex) { |
790 | + openDelayTimer.stop(); |
791 | + |
792 | + if (d._openItem !== menuItem && menuItem.delegate.hasSubmenu) { |
793 | + d.closePopup(); |
794 | + |
795 | + var menuPageComponent = Qt.createComponent("MenuPage.qml"); |
796 | + |
797 | + if (menuPageComponent.status === Component.Ready) { |
798 | + d._requestedOpenItem = menuItem; |
799 | + d.popup = menuPageComponent.createObject(menuItem, |
800 | + { |
801 | + "objectName": root.objectName + "-subMenu" + menuItem.delegate.index, |
802 | + "vertical": true, |
803 | + "x": opensVertically ? submenuOffset : menuItem.width, |
804 | + "y": opensVertically ? menuItem.height : submenuOffset, |
805 | + "delegateModel": menuItem.delegate.submenuItems |
806 | + }); |
807 | + } else if (menuPageComponent.status === Component.Error) { |
808 | + console.log(menuPageComponent.errorString()); |
809 | + } |
810 | + } |
811 | + if (focus && d.popup) d.popup.forceActiveFocus(); |
812 | + if (focusIndex !== undefined && d.popup) d.popup.setSelectedItem(focusIndex); |
813 | + } |
814 | + |
815 | + function openWithDelay(menuItem, focus, index) { |
816 | + openDelayTimer.stop(); |
817 | + if (d._openItem === menuItem) { |
818 | + open(menuItem, focus, index); |
819 | + } else { |
820 | + d._requestedOpenItem = menuItem; |
821 | + openDelayTimer.openFocus = focus; |
822 | + openDelayTimer.openIndex = index; |
823 | + openDelayTimer.restart(); |
824 | + } |
825 | + } |
826 | + |
827 | + function closePopup(focus) { |
828 | + openDelayTimer.stop(); |
829 | + if (openItem !== undefined) { |
830 | + d.closePopup(); |
831 | + } |
832 | + if (focus) { |
833 | + forceActiveFocus(); |
834 | + } |
835 | + } |
836 | + |
837 | + Timer { |
838 | + id: openDelayTimer |
839 | + |
840 | + property var openFocus |
841 | + property var openIndex |
842 | + |
843 | + interval: Constants.menuHoverOpenInterval |
844 | + onTriggered: { |
845 | + open(d._requestedOpenItem, openDelayTimer.openFocus, openDelayTimer.openIndex); |
846 | + } |
847 | + } |
848 | + |
849 | + QtObject { |
850 | + id: d |
851 | + property var _requestedOpenItem: undefined |
852 | + property var _openItem: undefined |
853 | + |
854 | + property QtObject popup: null |
855 | + onPopupChanged: { |
856 | + d._openItem = popup !== null ? _requestedOpenItem : undefined |
857 | + } |
858 | + |
859 | + signal closePopup |
860 | + } |
861 | + |
862 | + Connections { |
863 | + target: d |
864 | + onClosePopup: { |
865 | + if (d.popup) { |
866 | + // recusive close to preserve focusing |
867 | + d.popup.closePopup(false); |
868 | + d.popup.visible = false; |
869 | + d.popup.destroy(); |
870 | + d.popup = null; |
871 | + |
872 | + d._openItem = undefined; |
873 | + d._requestedOpenItem = undefined; |
874 | + } |
875 | + } |
876 | + } |
877 | +} |
878 | |
879 | === added file 'qml/ApplicationMenus/MenuItemBase.qml' |
880 | --- qml/ApplicationMenus/MenuItemBase.qml 1970-01-01 00:00:00 +0000 |
881 | +++ qml/ApplicationMenus/MenuItemBase.qml 2016-01-15 13:11:04 +0000 |
882 | @@ -0,0 +1,32 @@ |
883 | +/* |
884 | + * Copyright 2015 Canonical Ltd. |
885 | + * |
886 | + * This program is free software; you can redistribute it and/or modify |
887 | + * it under the terms of the GNU Lesser General Public License as published by |
888 | + * the Free Software Foundation; version 3. |
889 | + * |
890 | + * This program is distributed in the hope that it will be useful, |
891 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
892 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
893 | + * GNU Lesser General Public License for more details. |
894 | + * |
895 | + * You should have received a copy of the GNU Lesser General Public License |
896 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
897 | + */ |
898 | + |
899 | +import QtQuick 2.4 |
900 | +import QtQuick.Controls 1.3 as Controls |
901 | + |
902 | +// Item which represents visual location of a menu item |
903 | +// Also contains common convenience properties. |
904 | +Item { |
905 | + id: root |
906 | + |
907 | + property var delegate: undefined |
908 | + property alias mnemonicAction: mnemonic |
909 | + |
910 | + /* Quick action. eg "F" from "_File" in the label. Only available while bar/popup is opened */ |
911 | + Controls.Action { |
912 | + id: mnemonic |
913 | + } |
914 | +} |
915 | |
916 | === added file 'qml/ApplicationMenus/MenuItemDelegate.qml' |
917 | --- qml/ApplicationMenus/MenuItemDelegate.qml 1970-01-01 00:00:00 +0000 |
918 | +++ qml/ApplicationMenus/MenuItemDelegate.qml 2016-01-15 13:11:04 +0000 |
919 | @@ -0,0 +1,49 @@ |
920 | +/* |
921 | + * Copyright 2015 Canonical Ltd. |
922 | + * |
923 | + * This program is free software; you can redistribute it and/or modify |
924 | + * it under the terms of the GNU Lesser General Public License as published by |
925 | + * the Free Software Foundation; version 3. |
926 | + * |
927 | + * This program is distributed in the hope that it will be useful, |
928 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
929 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
930 | + * GNU Lesser General Public License for more details. |
931 | + * |
932 | + * You should have received a copy of the GNU Lesser General Public License |
933 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
934 | + */ |
935 | + |
936 | +import QtQuick 2.4 |
937 | +import QtQuick.Controls 1.3 as Controls |
938 | +import Utils 0.1 as UnityUtils |
939 | + |
940 | +MenuItemDelegateBase { |
941 | + id: root |
942 | + property var index: undefined |
943 | + property var menuData: undefined |
944 | + |
945 | + readonly property string label: menuData ? menuData.label : "" |
946 | + readonly property string action: menuData ? menuData.action : "" |
947 | + readonly property string icon: menuData ? menuData.icon : "" |
948 | + readonly property var shortcut: menuData ? menuData.shortcut : undefined |
949 | + readonly property bool sensitive: menuData ? menuData.sensitive : false |
950 | + readonly property bool hasSubmenu: menuData ? menuData.hasSubmenu : false |
951 | + readonly property bool isSeparator: menuData ? menuData.isSeparator : false |
952 | + readonly property bool isCheck: menuData ? menuData.isCheck : false |
953 | + readonly property bool isRadio: menuData ? menuData.isRadio : false |
954 | + readonly property bool isToggled: menuData ? menuData.isToggled : false |
955 | + |
956 | + signal activate() |
957 | + |
958 | + UnityUtils.ShortcutAction { |
959 | + shortcut: root.shortcut |
960 | + target: focusWindow ? focusWindow : null |
961 | + |
962 | + onTriggered: { |
963 | + if (root.enabled) { |
964 | + activate(); |
965 | + } |
966 | + } |
967 | + } |
968 | +} |
969 | |
970 | === added file 'qml/ApplicationMenus/MenuItemDelegateBase.qml' |
971 | --- qml/ApplicationMenus/MenuItemDelegateBase.qml 1970-01-01 00:00:00 +0000 |
972 | +++ qml/ApplicationMenus/MenuItemDelegateBase.qml 2016-01-15 13:11:04 +0000 |
973 | @@ -0,0 +1,82 @@ |
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 | + |
990 | +import QtQuick 2.4 |
991 | +import Utils 0.1 |
992 | + |
993 | +Item { |
994 | + id: root |
995 | + property alias menuModel: subMenuRepeater.model |
996 | + readonly property alias submenuItems: submenuDelegates |
997 | + property var focusWindow: undefined |
998 | + |
999 | + ListModel { |
1000 | + id: submenuDelegates |
1001 | + } |
1002 | + |
1003 | + Repeater { |
1004 | + id: subMenuRepeater |
1005 | + |
1006 | + onItemAdded: { |
1007 | + submenuDelegates.insert(index, { delegate: item.item }); |
1008 | + } |
1009 | + onItemRemoved: { |
1010 | + for (var i = 0; i < submenuDelegates.count; i++) { |
1011 | + if (item.item === submenuDelegates.get(i).delegate) { |
1012 | + submenuDelegates.remove(i, 1); |
1013 | + return; |
1014 | + } |
1015 | + } |
1016 | + } |
1017 | + onCountChanged: { |
1018 | + if (count == 0) { |
1019 | + submenuDelegates.clear(); |
1020 | + } |
1021 | + } |
1022 | + |
1023 | + delegate: Loader { |
1024 | + id: loader |
1025 | + source: "MenuItemDelegate.qml" |
1026 | + |
1027 | + property int modelIndex: index |
1028 | + |
1029 | + Binding { |
1030 | + target: loader.item |
1031 | + property: "index" |
1032 | + value: loader.modelIndex |
1033 | + } |
1034 | + Binding { |
1035 | + target: loader.item |
1036 | + property: "menuModel" |
1037 | + value: model.hasSubmenu ? menuModel.submenu(loader.modelIndex) : undefined |
1038 | + } |
1039 | + Binding { |
1040 | + target: loader.item |
1041 | + property: "menuData" |
1042 | + value: model |
1043 | + } |
1044 | + Binding { |
1045 | + target: loader.item |
1046 | + property: "focusWindow" |
1047 | + value: focusWindow |
1048 | + } |
1049 | + Connections { |
1050 | + target: loader.item |
1051 | + onActivate: menuModel.activate(loader.modelIndex) |
1052 | + } |
1053 | + } |
1054 | + } |
1055 | +} |
1056 | |
1057 | === added file 'qml/ApplicationMenus/MenuItemFactory.qml' |
1058 | --- qml/ApplicationMenus/MenuItemFactory.qml 1970-01-01 00:00:00 +0000 |
1059 | +++ qml/ApplicationMenus/MenuItemFactory.qml 2016-01-15 13:11:04 +0000 |
1060 | @@ -0,0 +1,240 @@ |
1061 | +/* |
1062 | + * Copyright 2015 Canonical Ltd. |
1063 | + * |
1064 | + * This program is free software; you can redistribute it and/or modify |
1065 | + * it under the terms of the GNU Lesser General Public License as published by |
1066 | + * the Free Software Foundation; version 3. |
1067 | + * |
1068 | + * This program is distributed in the hope that it will be useful, |
1069 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1070 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1071 | + * GNU Lesser General Public License for more details. |
1072 | + * |
1073 | + * You should have received a copy of the GNU Lesser General Public License |
1074 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1075 | + */ |
1076 | + |
1077 | +import QtQuick 2.4 |
1078 | +import Ubuntu.Settings.Menus 0.1 as Menus |
1079 | +import Ubuntu.Components.ListItems 1.3 as ListItems |
1080 | +import Ubuntu.Components 1.3 |
1081 | +import QtQuick.Layouts 1.1 |
1082 | + |
1083 | +import "AppMenuUtils.js" as AppMenuUtils |
1084 | + |
1085 | +Loader { |
1086 | + id: root |
1087 | + |
1088 | + property bool showShortcut: true |
1089 | + property var menuItemDelegate: undefined |
1090 | + |
1091 | + property string text: menuItemDelegate && menuItemDelegate.label || "" |
1092 | + |
1093 | + sourceComponent: { |
1094 | + if (menuItemDelegate.isSeparator) { |
1095 | + return separatorMenu; |
1096 | + } |
1097 | + if (menuItemDelegate.hasSubmenu) { |
1098 | + return subMenu; |
1099 | + } |
1100 | + if (menuItemDelegate.isCheck || menuItemDelegate.isRadio) { |
1101 | + return checkableMenu; |
1102 | + } |
1103 | + return standardMenu; |
1104 | + } |
1105 | + |
1106 | + Component { |
1107 | + id: separatorMenu |
1108 | + |
1109 | + Item { |
1110 | + implicitHeight: units.dp(6) |
1111 | + anchors { |
1112 | + left: parent.left |
1113 | + right: parent.right |
1114 | + } |
1115 | + |
1116 | + Rectangle { |
1117 | + height: units.dp(2) |
1118 | + color: "#5D5D5D" |
1119 | + anchors { |
1120 | + left: parent.left |
1121 | + right: parent.right |
1122 | + verticalCenter: parent.verticalCenter |
1123 | + } |
1124 | + } |
1125 | + } |
1126 | + } |
1127 | + |
1128 | + Component { |
1129 | + id: standardMenu |
1130 | + |
1131 | + Item { |
1132 | + implicitWidth: column.implicitWidth + units.gu(2) |
1133 | + implicitHeight: column.implicitHeight + units.gu(2) |
1134 | + |
1135 | + RowLayout { |
1136 | + id: column |
1137 | + spacing: units.gu(0.5) |
1138 | + anchors { |
1139 | + left: parent.left |
1140 | + right: parent.right |
1141 | + top: parent.top |
1142 | + margins: units.gu(1) |
1143 | + } |
1144 | + |
1145 | + Item { |
1146 | + Layout.preferredWidth: units.gu(1.5) |
1147 | + Layout.preferredHeight: units.gu(1.5) |
1148 | + } |
1149 | + |
1150 | + Icon { |
1151 | + Layout.preferredWidth: units.gu(2) |
1152 | + Layout.preferredHeight: units.gu(2) |
1153 | + Layout.alignment: Qt.AlignVCenter |
1154 | + |
1155 | + visible: menuItemDelegate && menuItemDelegate.icon || false |
1156 | + source: menuItemDelegate && menuItemDelegate.icon || "" |
1157 | + } |
1158 | + |
1159 | + RowLayout { |
1160 | + Layout.fillWidth: true |
1161 | + spacing: units.gu(5) |
1162 | + |
1163 | + Label { |
1164 | + text: menuItemDelegate ? menuItemDelegate.label.htmlLabelFromMenuLabel(showShortcut) : "" |
1165 | + Layout.fillWidth: true |
1166 | + horizontalAlignment: Text.AlignLeft |
1167 | + color: enabled ? "#FFFFFF" : "#888888" |
1168 | + textFormat: Text.StyledText |
1169 | + fontSize: "small" |
1170 | + } |
1171 | + |
1172 | + Label { |
1173 | + text: menuItemDelegate && menuItemDelegate.shortcut !== undefined ? menuItemDelegate.shortcut : "" |
1174 | + Layout.alignment: Qt.AlignRight |
1175 | + color: "#5D5D5D" |
1176 | + fontSize: "small" |
1177 | + } |
1178 | + } |
1179 | + } |
1180 | + } |
1181 | + } |
1182 | + |
1183 | + Component { |
1184 | + id: subMenu |
1185 | + |
1186 | + Item { |
1187 | + implicitWidth: column.implicitWidth + units.gu(2) |
1188 | + implicitHeight: column.implicitHeight + units.gu(2) |
1189 | + |
1190 | + RowLayout { |
1191 | + id: column |
1192 | + spacing: units.gu(0.5) |
1193 | + anchors { |
1194 | + left: parent.left |
1195 | + right: parent.right |
1196 | + top: parent.top |
1197 | + margins: units.gu(1) |
1198 | + } |
1199 | + |
1200 | + Item { |
1201 | + Layout.preferredWidth: units.gu(1.5) |
1202 | + Layout.preferredHeight: units.gu(1.5) |
1203 | + } |
1204 | + |
1205 | + Icon { |
1206 | + Layout.preferredWidth: units.gu(2) |
1207 | + Layout.preferredHeight: units.gu(2) |
1208 | + Layout.alignment: Qt.AlignVCenter |
1209 | + |
1210 | + visible: menuItemDelegate && menuItemDelegate.icon || false |
1211 | + source: menuItemDelegate && menuItemDelegate.icon || "" |
1212 | + } |
1213 | + |
1214 | + Label { |
1215 | + id: _title |
1216 | + text: menuItemDelegate && menuItemDelegate.label.htmlLabelFromMenuLabel(showShortcut) || "" |
1217 | + Layout.fillWidth: true |
1218 | + horizontalAlignment: Text.AlignLeft |
1219 | + color: enabled ? "#FFFFFF" : "#5D5D5D" |
1220 | + textFormat: Text.StyledText |
1221 | + fontSize: "small" |
1222 | + } |
1223 | + |
1224 | + Icon { |
1225 | + id: chevron |
1226 | + visible: menuItemDelegate.hasSubmenu |
1227 | + |
1228 | + Layout.preferredHeight: units.gu(1.5) |
1229 | + Layout.alignment: Qt.AlignVCenter |
1230 | + name: "chevron" |
1231 | + color: enabled ? "#FFFFFF" : "#5D5D5D" |
1232 | + } |
1233 | + } |
1234 | + } |
1235 | + } |
1236 | + |
1237 | + |
1238 | + Component { |
1239 | + id: checkableMenu |
1240 | + |
1241 | + Item { |
1242 | + implicitWidth: column.implicitWidth + units.gu(2) |
1243 | + implicitHeight: column.height + units.gu(2) |
1244 | + |
1245 | + RowLayout { |
1246 | + id: column |
1247 | + spacing: units.gu(0.5) |
1248 | + anchors { |
1249 | + left: parent.left |
1250 | + right: parent.right |
1251 | + top: parent.top |
1252 | + margins: units.gu(1) |
1253 | + } |
1254 | + |
1255 | + Item { |
1256 | + Layout.preferredWidth: units.gu(1.5) |
1257 | + Layout.preferredHeight: units.gu(1.5) |
1258 | + Layout.alignment: Qt.AlignVCenter |
1259 | + |
1260 | + Icon { |
1261 | + anchors.fill: parent |
1262 | + visible: menuItemDelegate && menuItemDelegate.isToggled |
1263 | + name: "tick" |
1264 | + color: enabled ? "#FFFFFF" : "#5D5D5D" |
1265 | + } |
1266 | + } |
1267 | + |
1268 | + Icon { |
1269 | + Layout.preferredWidth: units.gu(2) |
1270 | + Layout.preferredHeight: units.gu(2) |
1271 | + Layout.alignment: Qt.AlignVCenter |
1272 | + |
1273 | + visible: menuItemDelegate && menuItemDelegate.icon |
1274 | + source: menuItemDelegate && menuItemDelegate.icon || "" |
1275 | + } |
1276 | + |
1277 | + RowLayout { |
1278 | + Layout.fillWidth: true |
1279 | + spacing: units.gu(5) |
1280 | + |
1281 | + Label { |
1282 | + text: menuItemDelegate ? menuItemDelegate.label.htmlLabelFromMenuLabel(showShortcut) : "" |
1283 | + Layout.fillWidth: true |
1284 | + horizontalAlignment: Text.AlignLeft |
1285 | + color: enabled ? "#FFFFFF" : "#888888" |
1286 | + textFormat: Text.StyledText |
1287 | + fontSize: "small" |
1288 | + } |
1289 | + |
1290 | + Label { |
1291 | + text: menuItemDelegate && menuItemDelegate.shortcut !== undefined ? menuItemDelegate.shortcut : "" |
1292 | + Layout.alignment: Qt.AlignRight |
1293 | + color: "#5D5D5D" |
1294 | + fontSize: "small" |
1295 | + } |
1296 | + } |
1297 | + } |
1298 | + } |
1299 | + } |
1300 | +} |
1301 | |
1302 | === added file 'qml/ApplicationMenus/MenuPage.qml' |
1303 | --- qml/ApplicationMenus/MenuPage.qml 1970-01-01 00:00:00 +0000 |
1304 | +++ qml/ApplicationMenus/MenuPage.qml 2016-01-15 13:11:04 +0000 |
1305 | @@ -0,0 +1,277 @@ |
1306 | +/* |
1307 | + * Copyright 2015 Canonical Ltd. |
1308 | + * |
1309 | + * This program is free software; you can redistribute it and/or modify |
1310 | + * it under the terms of the GNU Lesser General Public License as published by |
1311 | + * the Free Software Foundation; version 3. |
1312 | + * |
1313 | + * This program is distributed in the hope that it will be useful, |
1314 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1315 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1316 | + * GNU Lesser General Public License for more details. |
1317 | + * |
1318 | + * You should have received a copy of the GNU Lesser General Public License |
1319 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1320 | + */ |
1321 | + |
1322 | +import QtQuick 2.4 |
1323 | +import QtQuick.Layouts 1.1 |
1324 | +import Ubuntu.Components 1.3 |
1325 | + |
1326 | +MenuBase { |
1327 | + id: root |
1328 | + objectName: "menuPage" |
1329 | + |
1330 | + property bool vertical: false |
1331 | + property alias delegateModel: repeater.model |
1332 | + |
1333 | + width: listView.width |
1334 | + height: listView.height |
1335 | + opensVertically: false |
1336 | + enableMnemonic: focus |
1337 | + |
1338 | + Keys.onDownPressed: { |
1339 | + listView.selectNext(); |
1340 | + event.accepted = true; |
1341 | + } |
1342 | + Keys.onUpPressed: { |
1343 | + listView.selectPrevious(); |
1344 | + event.accepted = true; |
1345 | + } |
1346 | + |
1347 | + Keys.onLeftPressed: { |
1348 | + if (root.openItem !== undefined) { |
1349 | + if (!focus) { |
1350 | + // If we don't have the focus, then a child must. |
1351 | + // Close the child and let our parent handle the left press |
1352 | + root.closePopup(true); |
1353 | + event.accepted = true; |
1354 | + return; |
1355 | + } |
1356 | + } |
1357 | + event.accepted = false; |
1358 | + } |
1359 | + Keys.onRightPressed: { |
1360 | + if (listView.selectedItem) { |
1361 | + if (listView.selectedItem.delegate.hasSubmenu) { |
1362 | + root.open(listView.selectedItem, true, 0); |
1363 | + event.accepted = true; |
1364 | + return; |
1365 | + } |
1366 | + } |
1367 | + event.accepted = false; |
1368 | + } |
1369 | + Keys.onReturnPressed: { |
1370 | + if (listView.selectedItem) { |
1371 | + if (listView.selectedItem.delegate.hasSubmenu) { |
1372 | + root.open(listView.selectedItem, true, 0); |
1373 | + event.accepted = true; |
1374 | + return; |
1375 | + } else { |
1376 | + listView.selectedItem.delegate.activate(); |
1377 | + event.accepted = true; |
1378 | + } |
1379 | + } |
1380 | + } |
1381 | + |
1382 | + function setSelectedItem(index) { |
1383 | + if (repeater.count <= index) return; |
1384 | + var item = repeater.itemAt(index); |
1385 | + item.selected = true; |
1386 | + } |
1387 | + |
1388 | + BorderImage { |
1389 | + anchors { |
1390 | + fill: root |
1391 | + margins: -units.gu(1) |
1392 | + } |
1393 | + source: "../Stages/graphics/dropshadow2gu.sci" |
1394 | + opacity: 0.3 |
1395 | + } |
1396 | + |
1397 | + Rectangle { |
1398 | + anchors.fill: parent |
1399 | + color: "#292929" |
1400 | + } |
1401 | + |
1402 | + ColumnLayout { |
1403 | + id: listView |
1404 | + objectName: root.objectName+"-ListView" |
1405 | + spacing: 0 |
1406 | + |
1407 | + property var selectedItem: undefined |
1408 | + |
1409 | + function selectNext() { |
1410 | + var delegate; |
1411 | + var newIndex = 0; |
1412 | + if (listView.selectedItem === undefined && repeater.count > 0) { |
1413 | + while (repeater.count > newIndex) { |
1414 | + delegate = repeater.itemAt(newIndex++); |
1415 | + if (delegate.enabled) { |
1416 | + delegate.selected = true; |
1417 | + break; |
1418 | + } |
1419 | + } |
1420 | + } else if (listView.selectedItem !== undefined && repeater.count > 1) { |
1421 | + var startIndex = (listView.selectedItem.ownIndex + 1) % repeater.count; |
1422 | + newIndex = startIndex; |
1423 | + do { |
1424 | + delegate = repeater.itemAt(newIndex); |
1425 | + if (delegate.enabled) { |
1426 | + delegate.selected = true; |
1427 | + break; |
1428 | + } |
1429 | + newIndex = (newIndex + 1) % repeater.count; |
1430 | + } while (newIndex !== startIndex) |
1431 | + } |
1432 | + } |
1433 | + |
1434 | + function selectPrevious() { |
1435 | + var delegate; |
1436 | + var newIndex = repeater.count-1; |
1437 | + if (listView.selectedItem === undefined && repeater.count > 0) { |
1438 | + while (repeater.count > newIndex) { |
1439 | + delegate = repeater.itemAt(newIndex--); |
1440 | + if (delegate.enabled) { |
1441 | + delegate.selected = true; |
1442 | + break; |
1443 | + } |
1444 | + } |
1445 | + } else if (listView.selectedItem !== undefined && repeater.count > 1) { |
1446 | + var startIndex = listView.selectedItem.ownIndex - 1; |
1447 | + newIndex = startIndex; |
1448 | + do { |
1449 | + if (newIndex < 0) newIndex = repeater.count - 1; |
1450 | + delegate = repeater.itemAt(newIndex--); |
1451 | + if (delegate.enabled) { |
1452 | + delegate.selected = true; |
1453 | + break; |
1454 | + } |
1455 | + } while (newIndex !== startIndex) |
1456 | + } |
1457 | + } |
1458 | + |
1459 | + Repeater { |
1460 | + id: repeater |
1461 | + |
1462 | + // This fixes the ordering issues with using repeater in Layouts. |
1463 | + onCountChanged: { |
1464 | + var i = 0; |
1465 | + for (; i < repeater.count; i++) { |
1466 | + var item = repeater.itemAt(i) |
1467 | + item.parent = null; |
1468 | + item.parent = listView; |
1469 | + } |
1470 | + } |
1471 | + onItemRemoved: { |
1472 | + if (item.menuItem === listView.selectedItem) { |
1473 | + if (root.openItem == item.menuItem) { |
1474 | + root.closePopup(true); |
1475 | + } |
1476 | + listView.selectedItem = undefined; |
1477 | + } |
1478 | + } |
1479 | + |
1480 | + delegate: MenuItemFactory { |
1481 | + id: repeaterItem |
1482 | + property bool selected: false |
1483 | + enabled: model.delegate.sensitive && !model.delegate.isSeparator |
1484 | + |
1485 | + menuItemDelegate: model.delegate |
1486 | + showShortcut: enableMnemonic |
1487 | + |
1488 | + Layout.fillWidth: true |
1489 | + Layout.preferredHeight: repeaterItem.implicitHeight |
1490 | + |
1491 | + Rectangle { |
1492 | + visible: repeaterItem.selected |
1493 | + anchors.fill: parent |
1494 | + gradient: UbuntuColors.orangeGradient |
1495 | + } |
1496 | + |
1497 | + property alias menuItem: _menuItem |
1498 | + |
1499 | + MenuItemBase { |
1500 | + id: _menuItem |
1501 | + objectName: root.objectName + "-menu" + index |
1502 | + property int ownIndex: index |
1503 | + |
1504 | + anchors.fill: parent |
1505 | + delegate: model.delegate |
1506 | + |
1507 | + mnemonicAction { |
1508 | + property string action: model.delegate.label.actionKeyFromMenuLabel() |
1509 | + enabled: enableMnemonic && repeaterItem.enabled |
1510 | + shortcut: action !== "" ? action : "" |
1511 | + |
1512 | + onTriggered: { |
1513 | + if (model.delegate.hasSubmenu) { |
1514 | + root.open(menuItem, true, 0); |
1515 | + } |
1516 | + else { |
1517 | + model.delegate.activate(); |
1518 | + } |
1519 | + } |
1520 | + } |
1521 | + } |
1522 | + |
1523 | + MouseArea { |
1524 | + anchors.fill: parent |
1525 | + hoverEnabled: true |
1526 | + z: 100 |
1527 | + |
1528 | + onEntered: { |
1529 | + repeaterItem.selected = true; |
1530 | + } |
1531 | + onClicked: { |
1532 | + if (model.delegate.hasSubmenu) { |
1533 | + root.open(menuItem, true); |
1534 | + } |
1535 | + else if (root.enabled) { |
1536 | + model.delegate.activate() |
1537 | + } |
1538 | + } |
1539 | + } |
1540 | + |
1541 | + onSelectedChanged: { |
1542 | + if (selected) { |
1543 | + listView.selectedItem = menuItem; |
1544 | + if (root.openItem !== menuItem) { |
1545 | + root.closePopup(true); |
1546 | + if (model.delegate.hasSubmenu) { |
1547 | + root.openWithDelay(menuItem, false); |
1548 | + } |
1549 | + } |
1550 | + } |
1551 | + else if (listView.selectedItem === menuItem) { |
1552 | + listView.selectedItem = undefined; |
1553 | + if (root.openItem == menuItem) { |
1554 | + root.closePopup(true); |
1555 | + } |
1556 | + } |
1557 | + } |
1558 | + |
1559 | + Connections { |
1560 | + target: listView |
1561 | + onSelectedItemChanged: { |
1562 | + if (repeaterItem.selected && (listView.selectedItem === undefined || listView.selectedItem !== menuItem)) { |
1563 | + repeaterItem.selected = false; |
1564 | + if (root.openItem == menuItem) { |
1565 | + root.closePopup(true); |
1566 | + } |
1567 | + } |
1568 | + } |
1569 | + } |
1570 | + |
1571 | + Connections { |
1572 | + target: root |
1573 | + onOpenItemChanged: { |
1574 | + if (root.openItem == menuItem) { |
1575 | + repeaterItem.selected = true; |
1576 | + } |
1577 | + } |
1578 | + } |
1579 | + } |
1580 | + } |
1581 | + } |
1582 | +} |
1583 | |
1584 | === modified file 'qml/CMakeLists.txt' |
1585 | --- qml/CMakeLists.txt 2015-03-06 04:44:11 +0000 |
1586 | +++ qml/CMakeLists.txt 2016-01-15 13:11:04 +0000 |
1587 | @@ -5,6 +5,7 @@ |
1588 | ) |
1589 | |
1590 | set(QML_DIRS |
1591 | + ApplicationMenus |
1592 | Components |
1593 | Dash |
1594 | graphics |
1595 | |
1596 | === modified file 'qml/Components/PanelState/PanelState.qml' |
1597 | --- qml/Components/PanelState/PanelState.qml 2015-11-20 13:06:14 +0000 |
1598 | +++ qml/Components/PanelState/PanelState.qml 2016-01-15 13:11:04 +0000 |
1599 | @@ -21,9 +21,10 @@ |
1600 | id: root |
1601 | |
1602 | property string title: "" |
1603 | - property bool buttonsVisible: false |
1604 | + property bool decorationsVisible: false |
1605 | property bool dropShadow: false |
1606 | property int panelHeight: units.gu(3) |
1607 | + property string maximizedApplication: "" |
1608 | |
1609 | signal close() |
1610 | signal minimize() |
1611 | |
1612 | === modified file 'qml/Panel/Panel.qml' |
1613 | --- qml/Panel/Panel.qml 2015-11-25 13:57:34 +0000 |
1614 | +++ qml/Panel/Panel.qml 2016-01-15 13:11:04 +0000 |
1615 | @@ -17,6 +17,9 @@ |
1616 | import QtQuick 2.4 |
1617 | import Ubuntu.Components 1.3 |
1618 | import Unity.Application 0.1 |
1619 | +import Unity.Indicators 0.1 |
1620 | +import Utils 0.1 |
1621 | +import "../ApplicationMenus" |
1622 | import "../Components" |
1623 | import "../Components/PanelState" |
1624 | import ".." |
1625 | @@ -101,13 +104,14 @@ |
1626 | } |
1627 | |
1628 | MouseArea { |
1629 | + id: decorationMouseArea |
1630 | anchors { |
1631 | top: parent.top |
1632 | left: parent.left |
1633 | right: indicators.left |
1634 | } |
1635 | height: indicators.minimizedPanelHeight |
1636 | - hoverEnabled: true |
1637 | + hoverEnabled: !indicators.shown |
1638 | onClicked: callHint.visible ? callHint.showLiveCall() : PanelState.focusMaximizedApp() |
1639 | onDoubleClicked: PanelState.maximize() |
1640 | |
1641 | @@ -124,12 +128,80 @@ |
1642 | bottomMargin: units.gu(0.5) |
1643 | } |
1644 | height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin |
1645 | - visible: PanelState.buttonsVisible && parent.containsMouse && !root.locked && !callHint.visible |
1646 | - active: PanelState.buttonsVisible |
1647 | + visible: d.showDecorations |
1648 | + active: PanelState.decorationsVisible |
1649 | onClose: PanelState.close() |
1650 | onMinimize: PanelState.minimize() |
1651 | onMaximize: PanelState.maximize() |
1652 | } |
1653 | + |
1654 | + WindowKeysFilter { |
1655 | + id: altFilter |
1656 | + property bool altPressed: false |
1657 | + property bool longAltPressed: false |
1658 | + enabled: d.enableMenus |
1659 | + Keys.onPressed: { |
1660 | + if (event.key === Qt.Key_Alt && !event.isAutoRepeat) { |
1661 | + altPressed = true; |
1662 | + longAltPressed = false; |
1663 | + menuBarShortcutTimer.start(); |
1664 | + return; |
1665 | + } |
1666 | + event.accepted = false; |
1667 | + } |
1668 | + Keys.onReleased: { |
1669 | + if (event.key === Qt.Key_Alt) { |
1670 | + menuBarShortcutTimer.stop(); |
1671 | + altPressed = false; |
1672 | + longAltPressed = false; |
1673 | + return; |
1674 | + } |
1675 | + event.accepted = false |
1676 | + } |
1677 | + |
1678 | + Timer { |
1679 | + id: menuBarShortcutTimer |
1680 | + interval: 200 |
1681 | + repeat: false |
1682 | + onTriggered: { |
1683 | + altFilter.longAltPressed = true; |
1684 | + } |
1685 | + } |
1686 | + } |
1687 | + |
1688 | + Loader { |
1689 | + id: menuBarLoader |
1690 | + anchors { |
1691 | + left: windowControlButtons.right |
1692 | + leftMargin: units.gu(3) |
1693 | + top: parent.top |
1694 | + } |
1695 | + height: parent.height |
1696 | + visible: d.showDecorations |
1697 | + |
1698 | + sourceComponent: PanelState.maximizedApplication ? menuBarComponent : undefined |
1699 | + Component { |
1700 | + id: menuBarComponent |
1701 | + MenuBar { |
1702 | + id: menuBar |
1703 | + height: menuBarLoader.height |
1704 | + enableMnemonic: altFilter.altPressed |
1705 | + enabled: d.enableMenus |
1706 | + menuModel: sharedAppModel.model |
1707 | + |
1708 | + SharedUnityMenuModel { |
1709 | + id: sharedAppModel |
1710 | + |
1711 | + property var application: ApplicationManager.findApplication(PanelState.maximizedApplication) |
1712 | + property var surface: application ? application.session ? application.session.lastSurface : null : null |
1713 | + |
1714 | + busName: surface ? surface.dbusMenuName : "" |
1715 | + menuObjectPath: surface ? surface.dbusMenuObjectPath : "" |
1716 | + actions: surface && surface.dbusMenuObjectPath ? { "unity": surface.dbusMenuObjectPath } : {} |
1717 | + } |
1718 | + } |
1719 | + } |
1720 | + } |
1721 | } |
1722 | |
1723 | IndicatorsMenu { |
1724 | @@ -162,6 +234,9 @@ |
1725 | callHint.showLiveCall(); |
1726 | } |
1727 | } |
1728 | + onShownChanged: { |
1729 | + if (d.menuBar) d.menuBar.close(); |
1730 | + } |
1731 | } |
1732 | |
1733 | Label { |
1734 | @@ -174,17 +249,15 @@ |
1735 | topMargin: units.gu(0.5) |
1736 | bottomMargin: units.gu(0.5) |
1737 | } |
1738 | - color: PanelState.buttonsVisible ? "#ffffff" : "#5d5d5d" |
1739 | + color: PanelState.decorationsVisible ? "#ffffff" : "#5d5d5d" |
1740 | height: indicators.minimizedPanelHeight - anchors.topMargin - anchors.bottomMargin |
1741 | - visible: !windowControlButtons.visible && !root.locked && !callHint.visible |
1742 | + visible: !d.showDecorations |
1743 | verticalAlignment: Text.AlignVCenter |
1744 | fontSize: "medium" |
1745 | font.weight: Font.Normal |
1746 | text: PanelState.title |
1747 | } |
1748 | |
1749 | - // TODO here would the Locally integrated menus come |
1750 | - |
1751 | ActiveCallHint { |
1752 | id: __callHint |
1753 | anchors { |
1754 | @@ -200,6 +273,17 @@ |
1755 | id: d |
1756 | objectName: "panelPriv" |
1757 | readonly property real indicatorHeight: indicators.minimizedPanelHeight |
1758 | + |
1759 | + property var menuBar: menuBarLoader.item |
1760 | + |
1761 | + property bool enableMenus: PanelState.decorationsVisible && !root.locked && !callHint.visible && |
1762 | + menuBar && |
1763 | + menuBar.hasChildren |
1764 | + |
1765 | + property bool showDecorations : PanelState.decorationsVisible && !root.locked && !callHint.visible && |
1766 | + (altFilter.longAltPressed || |
1767 | + (decorationMouseArea.hoverEnabled && decorationMouseArea.containsMouse) || |
1768 | + (menuBar && menuBar.openItem !== undefined)) |
1769 | } |
1770 | |
1771 | states: [ |
1772 | |
1773 | === modified file 'qml/Stages/DecoratedWindow.qml' |
1774 | --- qml/Stages/DecoratedWindow.qml 2015-11-23 15:09:45 +0000 |
1775 | +++ qml/Stages/DecoratedWindow.qml 2016-01-15 13:11:04 +0000 |
1776 | @@ -19,6 +19,11 @@ |
1777 | import QtQuick 2.4 |
1778 | import Ubuntu.Components 1.3 |
1779 | import Unity.Application 0.1 |
1780 | +import Unity.Indicators 0.1 as Indicators |
1781 | +import GlobalShortcut 1.0 |
1782 | +import "../Components/PanelState" |
1783 | + |
1784 | +import QMenuModel 0.1 as MenuModel |
1785 | |
1786 | FocusScope { |
1787 | id: root |
1788 | @@ -69,22 +74,6 @@ |
1789 | enabled: !fullscreen |
1790 | } |
1791 | |
1792 | - WindowDecoration { |
1793 | - id: decoration |
1794 | - target: root.parent |
1795 | - objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null" |
1796 | - anchors { left: parent.left; top: parent.top; right: parent.right } |
1797 | - height: units.gu(3) |
1798 | - width: root.width |
1799 | - title: window.title |
1800 | - visible: root.decorationShown |
1801 | - |
1802 | - onClose: root.close(); |
1803 | - onMaximize: { root.decorationPressed(); root.maximize(); } |
1804 | - onMinimize: root.minimize(); |
1805 | - onPressed: root.decorationPressed(); |
1806 | - } |
1807 | - |
1808 | ApplicationWindow { |
1809 | id: applicationWindow |
1810 | objectName: application ? "appWindow_" + application.appId : "appWindow_null" |
1811 | @@ -95,4 +84,49 @@ |
1812 | interactive: true |
1813 | focus: true |
1814 | } |
1815 | + |
1816 | + MouseArea { |
1817 | + anchors { left: parent.left; top: parent.top; right: parent.right } |
1818 | + height: units.gu(3) |
1819 | + |
1820 | + drag.target: root.parent |
1821 | + drag.filterChildren: true |
1822 | + drag.threshold: 0 |
1823 | + drag.minimumY: PanelState.panelHeight |
1824 | + drag.onActiveChanged: { |
1825 | + Mir.cursorName = active ? "grabbing" : "" |
1826 | + } |
1827 | + |
1828 | + // Parent is handling drag, this handles clicks. |
1829 | + MouseArea { |
1830 | + anchors.fill: parent |
1831 | + onPressed: root.decorationPressed() |
1832 | + onDoubleClicked: root.maximize() |
1833 | + } |
1834 | + |
1835 | + WindowDecoration { |
1836 | + id: decoration |
1837 | + objectName: application ? "appWindowDecoration_" + application.appId : "appWindowDecoration_null" |
1838 | + anchors.fill: parent |
1839 | + appId: application ? application.appId : "" |
1840 | + title: window.title |
1841 | + visible: root.decorationShown |
1842 | + |
1843 | + onClose: root.close(); |
1844 | + onMaximize: { root.decorationPressed(); root.maximize(); } |
1845 | + onMinimize: root.minimize(); |
1846 | + |
1847 | + menu: sharedAppModel.model |
1848 | + target: applicationWindow |
1849 | + |
1850 | + Indicators.SharedUnityMenuModel { |
1851 | + id: sharedAppModel |
1852 | + property var surface: application ? application.session ? application.session.lastSurface : null : null |
1853 | + |
1854 | + busName: surface ? surface.dbusMenuName : "" |
1855 | + menuObjectPath: surface ? surface.dbusMenuObjectPath : "" |
1856 | + actions: surface && surface.dbusMenuObjectPath ? { "unity": surface.dbusMenuObjectPath } : {} |
1857 | + } |
1858 | + } |
1859 | + } |
1860 | } |
1861 | |
1862 | === modified file 'qml/Stages/DesktopStage.qml' |
1863 | --- qml/Stages/DesktopStage.qml 2016-01-08 13:29:03 +0000 |
1864 | +++ qml/Stages/DesktopStage.qml 2016-01-15 13:11:04 +0000 |
1865 | @@ -187,13 +187,20 @@ |
1866 | |
1867 | Binding { |
1868 | target: PanelState |
1869 | - property: "buttonsVisible" |
1870 | - value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized // FIXME for Locally integrated menus |
1871 | + property: "decorationsVisible" |
1872 | + value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized |
1873 | && spread.state == "" |
1874 | } |
1875 | |
1876 | Binding { |
1877 | target: PanelState |
1878 | + property: "maximizedApplication" |
1879 | + value: priv.focusedAppDelegate !== null && priv.focusedAppDelegate.maximized |
1880 | + && spread.state == "" ? priv.focusedAppId : "" |
1881 | + } |
1882 | + |
1883 | + Binding { |
1884 | + target: PanelState |
1885 | property: "title" |
1886 | value: { |
1887 | if (priv.focusedAppDelegate !== null && spread.state == "") { |
1888 | @@ -215,7 +222,7 @@ |
1889 | |
1890 | Component.onDestruction: { |
1891 | PanelState.title = ""; |
1892 | - PanelState.buttonsVisible = false; |
1893 | + PanelState.decorationsVisible = false; |
1894 | PanelState.dropShadow = false; |
1895 | } |
1896 | |
1897 | |
1898 | === modified file 'qml/Stages/WindowDecoration.qml' |
1899 | --- qml/Stages/WindowDecoration.qml 2015-11-25 13:57:34 +0000 |
1900 | +++ qml/Stages/WindowDecoration.qml 2016-01-15 13:11:04 +0000 |
1901 | @@ -15,90 +15,162 @@ |
1902 | */ |
1903 | |
1904 | import QtQuick 2.4 |
1905 | +import QtQuick.Layouts 1.1 |
1906 | import Unity.Application 0.1 // For Mir singleton |
1907 | import Ubuntu.Components 1.3 |
1908 | +import Utils 0.1 |
1909 | import "../Components" |
1910 | import "../Components/PanelState" |
1911 | +import "../ApplicationMenus" |
1912 | |
1913 | -MouseArea { |
1914 | +Item { |
1915 | id: root |
1916 | - clip: true |
1917 | |
1918 | property Item target |
1919 | + property string appId |
1920 | property alias title: titleLabel.text |
1921 | property bool active: false |
1922 | - hoverEnabled: true |
1923 | + property var menu: undefined |
1924 | |
1925 | signal close() |
1926 | signal minimize() |
1927 | signal maximize() |
1928 | |
1929 | - onDoubleClicked: root.maximize() |
1930 | - |
1931 | QtObject { |
1932 | id: priv |
1933 | property real distanceX |
1934 | property real distanceY |
1935 | property bool dragging |
1936 | - } |
1937 | - |
1938 | - onPressedChanged: { |
1939 | - if (pressed) { |
1940 | - var pos = mapToItem(root.target, mouseX, mouseY); |
1941 | - priv.distanceX = pos.x; |
1942 | - priv.distanceY = pos.y; |
1943 | - priv.dragging = true; |
1944 | - } else { |
1945 | - priv.dragging = false; |
1946 | - Mir.cursorName = ""; |
1947 | - } |
1948 | - } |
1949 | - |
1950 | - onPositionChanged: { |
1951 | - if (priv.dragging) { |
1952 | - Mir.cursorName = "grabbing"; |
1953 | - var pos = mapToItem(root.target.parent, mouseX, mouseY); |
1954 | - root.target.x = pos.x - priv.distanceX; |
1955 | - root.target.y = Math.max(pos.y - priv.distanceY, PanelState.panelHeight); |
1956 | - } |
1957 | - } |
1958 | - |
1959 | - Rectangle { |
1960 | - anchors.fill: parent |
1961 | - anchors.bottomMargin: -radius |
1962 | + |
1963 | + property var menuBar: menuBarLoader.item |
1964 | + |
1965 | + property bool enableMenus: root.active && |
1966 | + (!PanelState.decorationsVisible || PanelState.maximizedApplication !== appId) && |
1967 | + menuBar && |
1968 | + menuBar.hasChildren |
1969 | + |
1970 | + property bool shouldShowMenus : enableMenus && |
1971 | + (altFilter.longAltPressed || menuBarHover.containsMouse || menuBar.openItem !== undefined) |
1972 | + } |
1973 | + |
1974 | + WindowKeysFilter { |
1975 | + id: altFilter |
1976 | + property bool altPressed: false |
1977 | + property bool longAltPressed: false |
1978 | + enabled: priv.enableMenus |
1979 | + Keys.onPressed: { |
1980 | + if (event.key === Qt.Key_Alt && !event.isAutoRepeat) { |
1981 | + altPressed = true; |
1982 | + longAltPressed = false; |
1983 | + menuBarShortcutTimer.start(); |
1984 | + return; |
1985 | + } |
1986 | + event.accepted = false; |
1987 | + } |
1988 | + Keys.onReleased: { |
1989 | + if (event.key === Qt.Key_Alt) { |
1990 | + menuBarShortcutTimer.stop(); |
1991 | + altPressed = false; |
1992 | + longAltPressed = false; |
1993 | + return; |
1994 | + } |
1995 | + event.accepted = false |
1996 | + } |
1997 | + |
1998 | + Timer { |
1999 | + id: menuBarShortcutTimer |
2000 | + interval: 200 |
2001 | + repeat: false |
2002 | + onTriggered: { |
2003 | + altFilter.longAltPressed = true; |
2004 | + } |
2005 | + } |
2006 | + } |
2007 | + |
2008 | + // non rounded for bottom of decoration |
2009 | + Rectangle { |
2010 | + anchors.fill: parent |
2011 | + anchors.topMargin: units.gu(.5) |
2012 | + color: "#292929" |
2013 | + } |
2014 | + |
2015 | + // rounded for top of decoration |
2016 | + Rectangle { |
2017 | + anchors.fill: parent |
2018 | radius: units.gu(.5) |
2019 | color: "#292929" |
2020 | } |
2021 | |
2022 | - Row { |
2023 | + RowLayout { |
2024 | anchors { |
2025 | fill: parent |
2026 | leftMargin: units.gu(1) |
2027 | rightMargin: units.gu(1) |
2028 | - topMargin: units.gu(0.5) |
2029 | - bottomMargin: units.gu(0.5) |
2030 | } |
2031 | spacing: units.gu(3) |
2032 | |
2033 | WindowControlButtons { |
2034 | id: buttons |
2035 | - height: parent.height |
2036 | + anchors { |
2037 | + top: parent.top |
2038 | + bottom: parent.bottom |
2039 | + topMargin: units.gu(0.5) |
2040 | + bottomMargin: units.gu(0.5) |
2041 | + } |
2042 | active: root.active |
2043 | onClose: root.close(); |
2044 | onMinimize: root.minimize(); |
2045 | onMaximize: root.maximize(); |
2046 | } |
2047 | |
2048 | - Label { |
2049 | - id: titleLabel |
2050 | - objectName: "windowDecorationTitle" |
2051 | - color: root.active ? "white" : "#5d5d5d" |
2052 | - height: parent.height |
2053 | - width: parent.width - buttons.width - parent.anchors.rightMargin - parent.anchors.leftMargin |
2054 | - verticalAlignment: Text.AlignVCenter |
2055 | - fontSize: "medium" |
2056 | - font.weight: root.active ? Font.Light : Font.Normal |
2057 | - elide: Text.ElideRight |
2058 | + Item { |
2059 | + Layout.preferredHeight: parent.height |
2060 | + Layout.fillWidth: true |
2061 | + |
2062 | + MouseArea { |
2063 | + id: menuBarHover |
2064 | + hoverEnabled: true |
2065 | + anchors.fill: parent |
2066 | + onPressed: { mouse.accepted = false; } // just monitoring |
2067 | + } |
2068 | + |
2069 | + Label { |
2070 | + id: titleLabel |
2071 | + objectName: "windowDecorationTitle" |
2072 | + color: root.active ? "white" : "#5d5d5d" |
2073 | + height: parent.height |
2074 | + width: parent.width |
2075 | + verticalAlignment: Text.AlignVCenter |
2076 | + fontSize: "medium" |
2077 | + font.weight: root.active ? Font.Light : Font.Normal |
2078 | + elide: Text.ElideRight |
2079 | + |
2080 | + opacity: priv.shouldShowMenus ? 0 : 1 |
2081 | + Behavior on opacity { UbuntuNumberAnimation { } } |
2082 | + } |
2083 | + |
2084 | + Loader { |
2085 | + id: menuBarLoader |
2086 | + objectName: "windowDecorationMenuBarLoader" |
2087 | + anchors.bottom: parent.bottom |
2088 | + height: parent.height |
2089 | + width: parent.width |
2090 | + sourceComponent: root.menu ? menuBarComponent : undefined |
2091 | + Component { |
2092 | + id: menuBarComponent |
2093 | + MenuBar { |
2094 | + id: menuBar |
2095 | + height: menuBarLoader.height |
2096 | + focusWindow: root.target |
2097 | + menuModel: root.menu |
2098 | + enableMnemonic: altFilter.altPressed |
2099 | + enabled: priv.enableMenus |
2100 | + } |
2101 | + } |
2102 | + |
2103 | + opacity: priv.shouldShowMenus ? 1 : 0 |
2104 | + Behavior on opacity { UbuntuNumberAnimation { } } |
2105 | + } |
2106 | } |
2107 | } |
2108 | } |
2109 | |
2110 | === modified file 'tests/imports/check_imports.py' |
2111 | --- tests/imports/check_imports.py 2015-07-15 15:13:18 +0000 |
2112 | +++ tests/imports/check_imports.py 2016-01-15 13:11:04 +0000 |
2113 | @@ -53,6 +53,7 @@ |
2114 | quick_good_pat = re.compile(r'.*import QtQuick 2\.4.*$') |
2115 | quick_layouts_good_pat = re.compile(r'.*import QtQuick.Layouts 1\.1.*$') |
2116 | quick_window_good_pat = re.compile(r'.*import QtQuick.Window 2\.2.*$') |
2117 | +quick_controls_good_pat = re.compile(r'.*import QtQuick.Controls 1\.4.*$') |
2118 | |
2119 | # Ubuntu Components patterns |
2120 | ubuntu_components_pat = re.compile(r'.*import Ubuntu.Components.*') |
2121 | @@ -111,7 +112,7 @@ |
2122 | for file in files: |
2123 | path = os.path.join(root, file) |
2124 | if not (ignore and path.startswith(ignore)) and pat.match(file): |
2125 | - quick_good_pats = [quick_good_pat, quick_layouts_good_pat, quick_window_good_pat] |
2126 | + quick_good_pats = [quick_good_pat, quick_layouts_good_pat, quick_window_good_pat, quick_controls_good_pat] |
2127 | if scan_for_bad_import(path, quick_pat, quick_good_pats): |
2128 | found_bad_import = True |
2129 | if scan_for_bad_import(path, ubuntu_components_pat, [ubuntu_good_components_pat]): |
2130 | |
2131 | === modified file 'tests/mocks/QMenuModel/QMenuModel.qmltypes' |
2132 | --- tests/mocks/QMenuModel/QMenuModel.qmltypes 2015-02-13 09:01:16 +0000 |
2133 | +++ tests/mocks/QMenuModel/QMenuModel.qmltypes 2016-01-15 13:11:04 +0000 |
2134 | @@ -1,4 +1,4 @@ |
2135 | -import QtQuick.tooling 1.1 |
2136 | +import QtQuick.tooling 1.2 |
2137 | |
2138 | // This file describes the plugin-supplied types contained in the library. |
2139 | // It is used for QML tooling purposes only. |
2140 | @@ -57,6 +57,10 @@ |
2141 | Property { name: "actionStateParser"; type: "ActionStateParser"; isPointer: true } |
2142 | Property { name: "nameOwner"; type: "string"; isReadonly: true } |
2143 | Property { name: "modelData"; type: "QVariant" } |
2144 | + Signal { |
2145 | + name: "activated" |
2146 | + Parameter { name: "action"; type: "string" } |
2147 | + } |
2148 | Method { |
2149 | name: "insertRow" |
2150 | Parameter { name: "row"; type: "int" } |
2151 | |
2152 | === modified file 'tests/mocks/QMenuModel/unitymenumodel.cpp' |
2153 | --- tests/mocks/QMenuModel/unitymenumodel.cpp 2014-10-07 10:28:59 +0000 |
2154 | +++ tests/mocks/QMenuModel/unitymenumodel.cpp 2016-01-15 13:11:04 +0000 |
2155 | @@ -18,6 +18,8 @@ |
2156 | |
2157 | #include "unitymenumodel.h" |
2158 | |
2159 | +#include <QDebug> |
2160 | + |
2161 | enum MenuRoles { |
2162 | LabelRole = Qt::DisplayRole + 1, |
2163 | SensitiveRole, |
2164 | @@ -29,7 +31,9 @@ |
2165 | ActionStateRole, |
2166 | IsCheckRole, |
2167 | IsRadioRole, |
2168 | - IsToggledRole |
2169 | + IsToggledRole, |
2170 | + ShortcutRole, |
2171 | + HasSubmenuRole |
2172 | }; |
2173 | |
2174 | UnityMenuModel::UnityMenuModel(QObject *parent) |
2175 | @@ -149,7 +153,10 @@ |
2176 | if (m_modelData.count() <= row) { |
2177 | return QVariantMap(); |
2178 | } |
2179 | - return m_modelData[row].toMap()["rowData"].toMap(); |
2180 | + QVariantMap vRow = m_modelData[row].toMap(); |
2181 | + QVariantMap map = vRow["rowData"].toMap(); |
2182 | + map["hasSubmenu"] = vRow.contains("submenu"); |
2183 | + return map; |
2184 | } |
2185 | |
2186 | QVariant UnityMenuModel::subMenuData(int row) const |
2187 | @@ -182,6 +189,8 @@ |
2188 | names[IsCheckRole] = "isCheck"; |
2189 | names[IsRadioRole] = "isRadio"; |
2190 | names[IsToggledRole] = "isToggled"; |
2191 | + names[ShortcutRole] = "shortcut"; |
2192 | + names[HasSubmenuRole] = "hasSubmenu"; |
2193 | |
2194 | return names; |
2195 | } |
2196 | @@ -201,6 +210,7 @@ |
2197 | UnityMenuModel*& model = submenus[position]; |
2198 | if (!model) { |
2199 | model = new UnityMenuModel(this); |
2200 | + connect(model, &UnityMenuModel::activated, this, &UnityMenuModel::activated); |
2201 | } |
2202 | if (model->modelData() != submenuData) { |
2203 | model->setModelData(submenuData); |
2204 | @@ -228,8 +238,20 @@ |
2205 | return this->data(this->index(row, 0), roles[role]); |
2206 | } |
2207 | |
2208 | -void UnityMenuModel::activate(int, const QVariant&) |
2209 | +void UnityMenuModel::activate(int row, const QVariant&) |
2210 | { |
2211 | + QVariantMap vModelData = m_modelData[row].toMap(); |
2212 | + QVariantMap rd = vModelData["rowData"].toMap(); |
2213 | + |
2214 | + bool isCheckable = rd[roleNames()[IsCheckRole]].toBool() || rd[roleNames()[IsRadioRole]].toBool(); |
2215 | + if (isCheckable) { |
2216 | + rd[roleNames()[IsToggledRole]] = !rd[roleNames()[IsToggledRole]].toBool(); |
2217 | + vModelData["rowData"] = rd; |
2218 | + m_modelData[row] = vModelData; |
2219 | + |
2220 | + dataChanged(index(row, 0), index(row, 0), QVector<int>() << IsToggledRole); |
2221 | + } |
2222 | + Q_EMIT activated(rd[roleNames()[ActionRole]].toString()); |
2223 | } |
2224 | |
2225 | void UnityMenuModel::changeState(int, const QVariant&) |
2226 | |
2227 | === modified file 'tests/mocks/QMenuModel/unitymenumodel.h' |
2228 | --- tests/mocks/QMenuModel/unitymenumodel.h 2015-04-30 09:31:51 +0000 |
2229 | +++ tests/mocks/QMenuModel/unitymenumodel.h 2016-01-15 13:11:04 +0000 |
2230 | @@ -88,6 +88,8 @@ |
2231 | // Internal mock usage |
2232 | void modelDataChanged(); |
2233 | |
2234 | + void activated(const QString& action); |
2235 | + |
2236 | private: |
2237 | QVariantMap rowData(int row) const; |
2238 | QVariant subMenuData(int row) const; |
2239 | |
2240 | === modified file 'tests/mocks/Unity/Application/MirSurface.cpp' |
2241 | --- tests/mocks/Unity/Application/MirSurface.cpp 2015-11-06 15:52:24 +0000 |
2242 | +++ tests/mocks/Unity/Application/MirSurface.cpp 2016-01-15 13:11:04 +0000 |
2243 | @@ -129,6 +129,16 @@ |
2244 | Q_EMIT orientationAngleChanged(angle); |
2245 | } |
2246 | |
2247 | +QString MirSurface::dbusMenuName() const |
2248 | +{ |
2249 | + return name(); |
2250 | +} |
2251 | + |
2252 | +QString MirSurface::dbusMenuObjectPath() const |
2253 | +{ |
2254 | + return QString("/%1").arg(name()); |
2255 | +} |
2256 | + |
2257 | |
2258 | |
2259 | void MirSurface::registerView(qintptr viewId) |
2260 | |
2261 | === modified file 'tests/mocks/Unity/Application/MirSurface.h' |
2262 | --- tests/mocks/Unity/Application/MirSurface.h 2015-11-06 15:52:24 +0000 |
2263 | +++ tests/mocks/Unity/Application/MirSurface.h 2016-01-15 13:11:04 +0000 |
2264 | @@ -66,6 +66,9 @@ |
2265 | Mir::OrientationAngle orientationAngle() const override; |
2266 | void setOrientationAngle(Mir::OrientationAngle) override; |
2267 | |
2268 | + QString dbusMenuName() const override; |
2269 | + QString dbusMenuObjectPath() const override; |
2270 | + |
2271 | //// |
2272 | // API for tests |
2273 | |
2274 | |
2275 | === modified file 'tests/mocks/Utils/CMakeLists.txt' |
2276 | --- tests/mocks/Utils/CMakeLists.txt 2015-10-28 10:32:47 +0000 |
2277 | +++ tests/mocks/Utils/CMakeLists.txt 2016-01-15 13:11:04 +0000 |
2278 | @@ -19,6 +19,7 @@ |
2279 | ${CMAKE_SOURCE_DIR}/plugins/Utils/easingcurve.cpp |
2280 | ${CMAKE_SOURCE_DIR}/plugins/Utils/inputwatcher.cpp |
2281 | ${CMAKE_SOURCE_DIR}/plugins/Utils/applicationsfiltermodel.cpp |
2282 | + ${CMAKE_SOURCE_DIR}/plugins/Utils/shortcutaction.cpp |
2283 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
2284 | constants.cpp |
2285 | plugin.cpp |
2286 | |
2287 | === modified file 'tests/mocks/Utils/constants.cpp' |
2288 | --- tests/mocks/Utils/constants.cpp 2015-04-15 14:52:01 +0000 |
2289 | +++ tests/mocks/Utils/constants.cpp 2016-01-15 13:11:04 +0000 |
2290 | @@ -20,4 +20,11 @@ |
2291 | : QObject(parent) |
2292 | { |
2293 | m_indicatorValueTimeout = 5000; |
2294 | + m_menuHoverOpenInterval = 250; |
2295 | +} |
2296 | + |
2297 | +void Constants::setMenuHoverOpenInterval(int menuHoverOpenInterval) |
2298 | +{ |
2299 | + m_menuHoverOpenInterval = menuHoverOpenInterval; |
2300 | + Q_EMIT menuHoverOpenIntervalChanged(); |
2301 | } |
2302 | |
2303 | === modified file 'tests/mocks/Utils/constants.h' |
2304 | --- tests/mocks/Utils/constants.h 2015-04-15 14:52:01 +0000 |
2305 | +++ tests/mocks/Utils/constants.h 2016-01-15 13:11:04 +0000 |
2306 | @@ -30,14 +30,21 @@ |
2307 | { |
2308 | Q_OBJECT |
2309 | Q_PROPERTY(int indicatorValueTimeout READ indicatorValueTimeout CONSTANT) |
2310 | + Q_PROPERTY(int menuHoverOpenInterval READ menuHoverOpenInterval WRITE setMenuHoverOpenInterval NOTIFY menuHoverOpenIntervalChanged) |
2311 | |
2312 | public: |
2313 | Constants(QObject *parent = 0); |
2314 | |
2315 | int indicatorValueTimeout() const { return m_indicatorValueTimeout; } |
2316 | + int menuHoverOpenInterval() const { return m_menuHoverOpenInterval; } |
2317 | + void setMenuHoverOpenInterval(int menuHoverOpenInterval); |
2318 | + |
2319 | +Q_SIGNALS: |
2320 | + void menuHoverOpenIntervalChanged(); |
2321 | |
2322 | private: |
2323 | int m_indicatorValueTimeout; |
2324 | + int m_menuHoverOpenInterval; |
2325 | }; |
2326 | |
2327 | #endif |
2328 | |
2329 | === modified file 'tests/mocks/Utils/plugin.cpp' |
2330 | --- tests/mocks/Utils/plugin.cpp 2015-10-28 10:32:47 +0000 |
2331 | +++ tests/mocks/Utils/plugin.cpp 2016-01-15 13:11:04 +0000 |
2332 | @@ -36,6 +36,7 @@ |
2333 | #include <windowscreenshotprovider.h> |
2334 | #include <easingcurve.h> |
2335 | #include <applicationsfiltermodel.h> |
2336 | +#include <shortcutaction.h> |
2337 | |
2338 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
2339 | { |
2340 | @@ -65,6 +66,7 @@ |
2341 | qmlRegisterSingletonType<Constants>(uri, 0, 1, "Constants", createConstants); |
2342 | qmlRegisterType<ActiveFocusLogger>(uri, 0, 1, "ActiveFocusLogger"); |
2343 | qmlRegisterType<ApplicationsFilterModel>(uri, 0, 1, "ApplicationsFilterModel"); |
2344 | + qmlRegisterType<ShortcutAction>(uri, 0, 1, "ShortcutAction"); |
2345 | } |
2346 | |
2347 | void FakeUtilsPlugin::initializeEngine(QQmlEngine *engine, const char *uri) |
2348 | |
2349 | === added directory 'tests/qmltests/ApplicationMenus' |
2350 | === added file 'tests/qmltests/ApplicationMenus/tst_MenuBar.qml' |
2351 | --- tests/qmltests/ApplicationMenus/tst_MenuBar.qml 1970-01-01 00:00:00 +0000 |
2352 | +++ tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2016-01-15 13:11:04 +0000 |
2353 | @@ -0,0 +1,132 @@ |
2354 | +/* |
2355 | + * Copyright (C) 2016 Canonical, Ltd. |
2356 | + * |
2357 | + * This program is free software; you can redistribute it and/or modify |
2358 | + * it under the terms of the GNU General Public License as published by |
2359 | + * the Free Software Foundation; version 3. |
2360 | + * |
2361 | + * This program is distributed in the hope that it will be useful, |
2362 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2363 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2364 | + * GNU General Public License for more details. |
2365 | + * |
2366 | + * You should have received a copy of the GNU General Public License |
2367 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2368 | + */ |
2369 | + |
2370 | +import QtQuick 2.4 |
2371 | +import QtTest 1.0 |
2372 | +import Ubuntu.Components 1.3 |
2373 | +import Ubuntu.Components.ListItems 1.3 |
2374 | +import Unity.Application 0.1 |
2375 | +import QMenuModel 0.1 |
2376 | +import Unity.Test 0.1 |
2377 | +import Utils 0.1 |
2378 | + |
2379 | +import "../../../qml/ApplicationMenus" |
2380 | +import "../Stages" |
2381 | + |
2382 | +Item { |
2383 | + id: root |
2384 | + width: units.gu(100) |
2385 | + height: units.gu(50) |
2386 | + |
2387 | + Component.onCompleted: { |
2388 | + Constants.menuHoverOpenInterval = 1; |
2389 | + } |
2390 | + |
2391 | + DesktopMenuData { id: desktopMenuData } |
2392 | + |
2393 | + Rectangle { |
2394 | + anchors { |
2395 | + left: parent.left |
2396 | + right: parent.right |
2397 | + top: parent.top |
2398 | + margins: units.gu(1) |
2399 | + } |
2400 | + height: units.gu(3) |
2401 | + color: "grey" |
2402 | + |
2403 | + MenuBar { |
2404 | + id: menuBar |
2405 | + anchors.fill: parent |
2406 | + |
2407 | + focusWindow: root |
2408 | + enableMnemonic: true |
2409 | + menuModel: UnityMenuModel { |
2410 | + id: menuBackend |
2411 | + modelData: desktopMenuData.testData |
2412 | + } |
2413 | + } |
2414 | + } |
2415 | + |
2416 | + SignalSpy { |
2417 | + id: activatedSpy |
2418 | + target: menuBackend |
2419 | + signalName: "activated" |
2420 | + } |
2421 | + |
2422 | + UnityTestCase { |
2423 | + id: testCase |
2424 | + name: "MenuPage" |
2425 | + when: windowShown |
2426 | + |
2427 | + property bool clickNavigate: true |
2428 | + |
2429 | + function init() { |
2430 | + menuBar.closePopup(); |
2431 | + menuBackend.modelData = desktopMenuData.generateTestData(3, 2, 0); |
2432 | + activatedSpy.clear(); |
2433 | + } |
2434 | + |
2435 | + function test_mnemonics_data() { |
2436 | + return [ |
2437 | + { tag: "a" }, |
2438 | + { tag: "b" }, |
2439 | + ] |
2440 | + } |
2441 | + |
2442 | + function test_mnemonics(data) { |
2443 | + menuBackend.modelData = desktopMenuData.generateTestData(3, 2, 0); |
2444 | + |
2445 | + keyPress(data.tag, Qt.AltModifier, 100); |
2446 | + tryCompareFunction(function() { return menuBar.openItem !== undefined; }, true); |
2447 | + } |
2448 | + |
2449 | + function test_navigateRight(data) { |
2450 | + var menuItem0 = findChild(menuBar, "menuBar-menu0"); verify(menuItem0); |
2451 | + var menuItem1 = findChild(menuBar, "menuBar-menu1"); verify(menuItem1); |
2452 | + var menuItem2 = findChild(menuBar, "menuBar-menu2"); verify(menuItem2); |
2453 | + |
2454 | + menuBar.open(menuItem0, true); |
2455 | + compare(menuBar.openItem, menuItem0); |
2456 | + |
2457 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
2458 | + compare(menuBar.openItem, menuItem1); |
2459 | + |
2460 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
2461 | + compare(menuBar.openItem, menuItem2); |
2462 | + |
2463 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
2464 | + compare(menuBar.openItem, menuItem0); |
2465 | + } |
2466 | + |
2467 | + function test_navigateLeft(data) { |
2468 | + var menuItem0 = findChild(menuBar, "menuBar-menu0"); verify(menuItem0); |
2469 | + var menuItem1 = findChild(menuBar, "menuBar-menu1"); verify(menuItem1); |
2470 | + var menuItem2 = findChild(menuBar, "menuBar-menu2"); verify(menuItem2); |
2471 | + |
2472 | + menuBar.open(menuItem0, true); |
2473 | + compare(menuBar.openItem, menuItem0); |
2474 | + |
2475 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
2476 | + compare(menuBar.openItem, menuItem2); |
2477 | + |
2478 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
2479 | + compare(menuBar.openItem, menuItem1); |
2480 | + |
2481 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
2482 | + compare(menuBar.openItem, menuItem0); |
2483 | + } |
2484 | + } |
2485 | +} |
2486 | |
2487 | === added file 'tests/qmltests/ApplicationMenus/tst_MenuPage.qml' |
2488 | --- tests/qmltests/ApplicationMenus/tst_MenuPage.qml 1970-01-01 00:00:00 +0000 |
2489 | +++ tests/qmltests/ApplicationMenus/tst_MenuPage.qml 2016-01-15 13:11:04 +0000 |
2490 | @@ -0,0 +1,256 @@ |
2491 | +/* |
2492 | + * Copyright (C) 2016 Canonical, Ltd. |
2493 | + * |
2494 | + * This program is free software; you can redistribute it and/or modify |
2495 | + * it under the terms of the GNU General Public License as published by |
2496 | + * the Free Software Foundation; version 3. |
2497 | + * |
2498 | + * This program is distributed in the hope that it will be useful, |
2499 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2500 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2501 | + * GNU General Public License for more details. |
2502 | + * |
2503 | + * You should have received a copy of the GNU General Public License |
2504 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2505 | + */ |
2506 | + |
2507 | +import QtQuick 2.4 |
2508 | +import QtTest 1.0 |
2509 | +import Ubuntu.Components 1.3 |
2510 | +import Ubuntu.Components.ListItems 1.3 |
2511 | +import Unity.Application 0.1 |
2512 | +import QMenuModel 0.1 |
2513 | +import Unity.Test 0.1 |
2514 | +import Utils 0.1 |
2515 | + |
2516 | +import "../../../qml/ApplicationMenus" |
2517 | +import "../Stages" |
2518 | + |
2519 | +Item { |
2520 | + id: root |
2521 | + width: Math.max(units.gu(100), page.width + units.gu(6)) |
2522 | + height: Math.max(units.gu(50), page.height + units.gu(6)) |
2523 | + |
2524 | + Component.onCompleted: { |
2525 | + Constants.menuHoverOpenInterval = 1; |
2526 | + } |
2527 | + |
2528 | + DesktopMenuData { id: desktopMenuData } |
2529 | + |
2530 | + Keys.onEscapePressed: { |
2531 | + page.closePopup(true); |
2532 | + } |
2533 | + |
2534 | + MenuPage { |
2535 | + id: page |
2536 | + focus: true |
2537 | + |
2538 | + anchors { |
2539 | + left: parent.left |
2540 | + top: parent.top |
2541 | + leftMargin: units.gu(3) |
2542 | + topMargin: units.gu(3) |
2543 | + } |
2544 | + |
2545 | + delegateModel: rootDelegate.submenuItems |
2546 | + MenuItemDelegateBase { |
2547 | + id: rootDelegate |
2548 | + menuModel: UnityMenuModel { |
2549 | + id: menuBackend |
2550 | + modelData: desktopMenuData.generateTestData(3, 3, 0); |
2551 | + } |
2552 | + } |
2553 | + } |
2554 | + |
2555 | + SignalSpy { |
2556 | + id: activatedSpy |
2557 | + target: menuBackend |
2558 | + signalName: "activated" |
2559 | + } |
2560 | + |
2561 | + UnityTestCase { |
2562 | + id: testCase |
2563 | + name: "MenuPage" |
2564 | + when: windowShown |
2565 | + |
2566 | + property bool clickNavigate: true |
2567 | + |
2568 | + function init() { |
2569 | + page.closePopup(true); |
2570 | + menuBackend.modelData = desktopMenuData.generateTestData(3, 3, 0); |
2571 | + activatedSpy.clear(); |
2572 | + } |
2573 | + |
2574 | + // visit and verify that all the backend menus have been created |
2575 | + function recurseMenuConstruction(rows, menuPageName) { |
2576 | + for (var i = 0; i < rows.length; ++i) { |
2577 | + var rowData = rows[i]["rowData"]; |
2578 | + |
2579 | + var menuPage = findChild(page, menuPageName); |
2580 | + verify(menuPage); |
2581 | + var menuItem = findChild(menuPage, menuPageName+"-menu"+i); |
2582 | + verify(menuItem); |
2583 | + |
2584 | + // recurse into submenu |
2585 | + var submenu = rows[i]["submenu"]; |
2586 | + if (submenu) { |
2587 | + if (clickNavigate) { |
2588 | + mouseClick(menuItem, menuItem.width/2, menuItem.height/2); |
2589 | + } else { |
2590 | + mouseMove(menuItem, menuItem.width/2, menuItem.height/2); |
2591 | + } |
2592 | + tryCompare(menuPage, "openItem", menuItem); |
2593 | + recurseMenuConstruction(submenu, menuPageName+"-subMenu"+i); |
2594 | + } |
2595 | + } |
2596 | + } |
2597 | + |
2598 | + function test_hoverNavigation_data() { |
2599 | + return [ |
2600 | + { tag: "long", testData: desktopMenuData.generateTestData(4, 2, 0) }, |
2601 | + { tag: "deep", testData: desktopMenuData.generateTestData(2, 4, 0) } |
2602 | + ] |
2603 | + } |
2604 | + |
2605 | + function test_hoverNavigation(data) { |
2606 | + clickNavigate = false; |
2607 | + menuBackend.modelData = data.testData; |
2608 | + |
2609 | + recurseMenuConstruction(data.testData, "menuPage"); |
2610 | + } |
2611 | + |
2612 | + function test_clickNavigation_data() { |
2613 | + return [ |
2614 | + { tag: "long", testData: desktopMenuData.generateTestData(4, 2, 0) }, |
2615 | + { tag: "deep", testData: desktopMenuData.generateTestData(2, 4, 0) } |
2616 | + ] |
2617 | + } |
2618 | + |
2619 | + function test_clickNavigation(data) { |
2620 | + clickNavigate = true; |
2621 | + menuBackend.modelData = data.testData; |
2622 | + |
2623 | + recurseMenuConstruction(data.testData, "menuPage"); |
2624 | + } |
2625 | + |
2626 | + function test_checkableMenuTogglesOnClick() { |
2627 | + menuBackend.modelData = desktopMenuData.singleCheckable; |
2628 | + |
2629 | + var menuItem = findChild(page, "menuPage-menu0"); |
2630 | + verify(menuItem); |
2631 | + verify(menuItem.delegate.isCheck); |
2632 | + verify(menuItem.delegate.isToggled === false); |
2633 | + |
2634 | + mouseClick(menuItem, menuItem.width/2, menuItem.height/2); |
2635 | + |
2636 | + compare(menuItem.delegate.isToggled, true, "Checkable menu should have toggled"); |
2637 | + } |
2638 | + |
2639 | + function test_keyboardNavigation_DownKeySelectsAndOpensNextMenuItemAndRotates() { |
2640 | + menuBackend.modelData = desktopMenuData.generateTestData(4, 2, 3); |
2641 | + var listView = findChild(page, "menuPage-ListView"); |
2642 | + verify(listView); |
2643 | + |
2644 | + var menuItem0 = findChild(page, "menuPage-menu0"); verify(menuItem0); |
2645 | + var menuItem1 = findChild(page, "menuPage-menu1"); verify(menuItem1); |
2646 | + var menuItem2 = findChild(page, "menuPage-menu2"); verify(menuItem2); |
2647 | + verify(menuItem2.delegate.isSeparator); |
2648 | + var menuItem3 = findChild(page, "menuPage-menu3"); verify(menuItem3); |
2649 | + |
2650 | + keyClick(Qt.Key_Down, Qt.NoModifier, 100); |
2651 | + compare(listView.selectedItem, menuItem0); |
2652 | + tryCompare(page, "openItem", menuItem0); |
2653 | + |
2654 | + keyClick(Qt.Key_Down, Qt.NoModifier, 100); |
2655 | + compare(listView.selectedItem, menuItem1); |
2656 | + tryCompare(page, "openItem", menuItem1); |
2657 | + |
2658 | + // Skip separator |
2659 | + |
2660 | + keyClick(Qt.Key_Down, Qt.NoModifier, 100); |
2661 | + compare(listView.selectedItem, menuItem3); |
2662 | + tryCompare(page, "openItem", menuItem3); |
2663 | + |
2664 | + keyClick(Qt.Key_Down, Qt.NoModifier, 100); |
2665 | + compare(listView.selectedItem, menuItem0); |
2666 | + tryCompare(page, "openItem", menuItem0); |
2667 | + } |
2668 | + |
2669 | + function test_keyboardNavigation_UpKeySelectsAndOpensPreviousMenuItemAndRotates() { |
2670 | + menuBackend.modelData = desktopMenuData.generateTestData(4, 2, 3); |
2671 | + var listView = findChild(page, "menuPage-ListView"); |
2672 | + verify(listView); |
2673 | + |
2674 | + var menuItem0 = findChild(page, "menuPage-menu0"); verify(menuItem0); |
2675 | + var menuItem1 = findChild(page, "menuPage-menu1"); verify(menuItem1); |
2676 | + var menuItem2 = findChild(page, "menuPage-menu2"); verify(menuItem2); |
2677 | + verify(menuItem2.delegate.isSeparator); |
2678 | + var menuItem3 = findChild(page, "menuPage-menu3"); verify(menuItem3); |
2679 | + |
2680 | + keyClick(Qt.Key_Up, Qt.NoModifier, 100); |
2681 | + compare(listView.selectedItem, menuItem3); |
2682 | + tryCompare(page, "openItem", menuItem3); |
2683 | + |
2684 | + // Skip separator |
2685 | + |
2686 | + keyClick(Qt.Key_Up, Qt.NoModifier, 100); |
2687 | + compare(listView.selectedItem, menuItem1); |
2688 | + tryCompare(page, "openItem", menuItem1); |
2689 | + |
2690 | + keyClick(Qt.Key_Up, Qt.NoModifier, 100); |
2691 | + compare(listView.selectedItem, menuItem0); |
2692 | + tryCompare(page, "openItem", menuItem0); |
2693 | + |
2694 | + keyClick(Qt.Key_Up, Qt.NoModifier, 100); |
2695 | + compare(listView.selectedItem, menuItem3); |
2696 | + tryCompare(page, "openItem", menuItem3); |
2697 | + } |
2698 | + |
2699 | + function test_keyboardNavigation_RightKeyEntersSubMenu() { |
2700 | + menuBackend.modelData = desktopMenuData.generateTestData(2, 2, 0); |
2701 | + |
2702 | + var menuItem = findChild(page, "menuPage-menu0"); |
2703 | + verify(menuItem); |
2704 | + page.open(menuItem, true); |
2705 | + compare(page.openItem, menuItem); |
2706 | + |
2707 | + var submenu = findChild(page, "menuPage-subMenu0"); |
2708 | + verify(submenu); |
2709 | + var listView = findChild(page, "menuPage-subMenu0-ListView"); |
2710 | + verify(listView); |
2711 | + menuItem = findChild(page, "menuPage-subMenu0-menu0"); |
2712 | + verify(menuItem); |
2713 | + |
2714 | + keyClick(Qt.Key_Right, Qt.NoModifier); |
2715 | + compare(listView.selectedItem, menuItem); |
2716 | + } |
2717 | + |
2718 | + function test_keyboardNavigation_LeftKeyClosesSubMenu() { |
2719 | + menuBackend.modelData = desktopMenuData.generateTestData(2, 2, 0); |
2720 | + |
2721 | + var menuItem = findChild(page, "menuPage-menu0"); |
2722 | + verify(menuItem); |
2723 | + page.open(menuItem, true, 0); // quick open & select item 0 |
2724 | + |
2725 | + compare(page.openItem, menuItem); |
2726 | + keyClick(Qt.Key_Left, Qt.NoModifier); |
2727 | + compare(page.openItem, undefined); |
2728 | + } |
2729 | + |
2730 | + function test_mnemonics() { |
2731 | + var menuItem0 = findChild(page, "menuPage-menu0"); verify(menuItem0); |
2732 | + |
2733 | + keyClick(Qt.Key_A, Qt.NoModifier); |
2734 | + tryCompare(page, "openItem", menuItem0); |
2735 | + |
2736 | + var submenu0 = findChild(menuItem0, "menuPage-subMenu0"); verify(submenu0); |
2737 | + var submenu0_menuItem1 = findChild(submenu0, "menuPage-subMenu0-menu1"); verify(submenu0_menuItem1); |
2738 | + |
2739 | + keyClick(Qt.Key_B, Qt.NoModifier); |
2740 | + tryCompare(submenu0, "openItem", submenu0_menuItem1); |
2741 | + |
2742 | + keyClick(Qt.Key_B, Qt.NoModifier); |
2743 | + compare(activatedSpy.signalArguments, [{ "0": "menuA.B.B" }], "Activate should have been emmited once"); |
2744 | + } |
2745 | + } |
2746 | +} |
2747 | |
2748 | === modified file 'tests/qmltests/CMakeLists.txt' |
2749 | --- tests/qmltests/CMakeLists.txt 2016-01-11 17:37:50 +0000 |
2750 | +++ tests/qmltests/CMakeLists.txt 2016-01-15 13:11:04 +0000 |
2751 | @@ -5,6 +5,8 @@ |
2752 | add_unity8_qmltest(. DisabledScreenNotice) |
2753 | add_unity8_qmltest(. Shell LIGHTDM) |
2754 | add_unity8_qmltest(. ShellWithPin LIGHTDM) |
2755 | +add_unity8_qmltest(ApplicationMenus MenuBar) |
2756 | +add_unity8_qmltest(ApplicationMenus MenuPage) |
2757 | add_unity8_qmltest(Components Background) |
2758 | add_unity8_qmltest(Components Carousel) |
2759 | add_unity8_qmltest(Components Dialogs LIGHTDM) |
2760 | @@ -85,6 +87,7 @@ |
2761 | add_unity8_qmltest(Stages SessionContainer) |
2762 | add_unity8_qmltest(Stages TabletStage) |
2763 | add_unity8_qmltest(Stages WindowResizeArea) |
2764 | +add_unity8_qmltest(Stages WindowDecoration) |
2765 | add_unity8_qmltest(Stages Splash) |
2766 | add_unity8_qmltest(Tutorial Tutorial LIGHTDM) |
2767 | add_unity8_qmltest(Wizard Wizard ENVIRONMENT "OXIDE_NO_SANDBOX=1") |
2768 | |
2769 | === modified file 'tests/qmltests/Panel/tst_Panel.qml' |
2770 | --- tests/qmltests/Panel/tst_Panel.qml 2015-11-18 15:12:56 +0000 |
2771 | +++ tests/qmltests/Panel/tst_Panel.qml 2016-01-15 13:11:04 +0000 |
2772 | @@ -24,10 +24,11 @@ |
2773 | import Ubuntu.Telephony 0.1 as Telephony |
2774 | import "../../../qml/Panel" |
2775 | import "../../../qml/Components/PanelState" |
2776 | +import "../Stages" |
2777 | |
2778 | IndicatorTest { |
2779 | id: root |
2780 | - width: units.gu(100) |
2781 | + width: units.gu(120) |
2782 | height: units.gu(71) |
2783 | color: "white" |
2784 | |
2785 | @@ -37,6 +38,17 @@ |
2786 | value: !windowControlsCB.checked |
2787 | } |
2788 | |
2789 | + DesktopMenuData { id: appMenuData } |
2790 | + |
2791 | + Component.onCompleted: { |
2792 | + Indicators.UnityMenuModelCache.setCachedModelData("/dialer-app", appMenuData.dialerData); |
2793 | + } |
2794 | + |
2795 | + Rectangle { |
2796 | + anchors.fill: parent |
2797 | + color: "darkgrey" |
2798 | + } |
2799 | + |
2800 | RowLayout { |
2801 | anchors.fill: parent |
2802 | anchors.margins: units.gu(1) |
2803 | @@ -105,10 +117,11 @@ |
2804 | Layout.fillWidth: true |
2805 | CheckBox { |
2806 | id: windowControlsCB |
2807 | - onClicked: PanelState.buttonsVisible = checked |
2808 | + onClicked: PanelState.decorationsVisible = checked |
2809 | } |
2810 | Label { |
2811 | - text: "Show window controls" |
2812 | + text: "Show window decorations" |
2813 | + color: "white" |
2814 | } |
2815 | } |
2816 | |
2817 | @@ -124,6 +137,7 @@ |
2818 | } |
2819 | Label { |
2820 | text: "Show fake window title" |
2821 | + color: "white" |
2822 | } |
2823 | } |
2824 | |
2825 | @@ -133,6 +147,19 @@ |
2826 | color: "black" |
2827 | } |
2828 | |
2829 | + ApplicationCheckBox { |
2830 | + id: applicationCheckBox |
2831 | + Layout.fillWidth: true |
2832 | + appId: "dialer-app" |
2833 | + onTriggered: PanelState.maximizedApplication = checked ? "dialer-app" : ""; |
2834 | + } |
2835 | + |
2836 | + Rectangle { |
2837 | + Layout.preferredHeight: units.dp(1); |
2838 | + Layout.fillWidth: true; |
2839 | + color: "black" |
2840 | + } |
2841 | + |
2842 | Repeater { |
2843 | model: root.originalModelData |
2844 | RowLayout { |
2845 | @@ -143,6 +170,7 @@ |
2846 | Label { |
2847 | Layout.fillWidth: true |
2848 | text: modelData["identifier"] |
2849 | + color: "white" |
2850 | } |
2851 | |
2852 | CheckBox { |
2853 | @@ -151,6 +179,7 @@ |
2854 | } |
2855 | Label { |
2856 | text: "visible" |
2857 | + color: "white" |
2858 | } |
2859 | } |
2860 | } |
2861 | @@ -161,7 +190,10 @@ |
2862 | color: "black" |
2863 | } |
2864 | |
2865 | - MouseTouchEmulationCheckbox { id: mouseEmulation } |
2866 | + MouseTouchEmulationCheckbox { |
2867 | + id: mouseEmulation |
2868 | + color: "white" |
2869 | + } |
2870 | } |
2871 | } |
2872 | |
2873 | @@ -492,5 +524,25 @@ |
2874 | compare(panel.indicators.shown, false); |
2875 | tryCompare(panel.indicators, "fullyClosed", true); |
2876 | } |
2877 | + |
2878 | + function test_showsDecorations() { |
2879 | + compare(panel.indicators.shown, false); |
2880 | + verify(panel.indicators.fullyClosed); |
2881 | + |
2882 | + mouseClick(panel.indicators, |
2883 | + panel.indicators.width / 2, |
2884 | + panel.indicators.minimizedPanelHeight / 2); |
2885 | + |
2886 | + compare(panel.indicators.shown, true); |
2887 | + tryCompare(panel.indicators, "fullyOpened", true); |
2888 | + |
2889 | + var handle = findChild(panel.indicators, "handle"); |
2890 | + verify(handle); |
2891 | + |
2892 | + mouseClick(handle); |
2893 | + |
2894 | + compare(panel.indicators.shown, false); |
2895 | + tryCompare(panel.indicators, "fullyClosed", true); |
2896 | + } |
2897 | } |
2898 | } |
2899 | |
2900 | === modified file 'tests/qmltests/Stages/ApplicationCheckBox.qml' |
2901 | --- tests/qmltests/Stages/ApplicationCheckBox.qml 2015-08-11 11:41:08 +0000 |
2902 | +++ tests/qmltests/Stages/ApplicationCheckBox.qml 2016-01-15 13:11:04 +0000 |
2903 | @@ -24,6 +24,8 @@ |
2904 | property string appId |
2905 | property bool checked: false |
2906 | |
2907 | + signal triggered |
2908 | + |
2909 | enabled: appId !== "unity8-dash" |
2910 | |
2911 | onCheckedChanged: { |
2912 | @@ -35,6 +37,7 @@ |
2913 | } else { |
2914 | ApplicationManager.stopApplication(root.appId); |
2915 | } |
2916 | + root.triggered(); |
2917 | d.bindGuard = false; |
2918 | } |
2919 | |
2920 | @@ -69,6 +72,7 @@ |
2921 | } else { |
2922 | ApplicationManager.stopApplication(root.appId); |
2923 | } |
2924 | + root.triggered(); |
2925 | d.bindGuard = false; |
2926 | } |
2927 | onCheckedChanged: { |
2928 | |
2929 | === added file 'tests/qmltests/Stages/DesktopMenuData.qml' |
2930 | --- tests/qmltests/Stages/DesktopMenuData.qml 1970-01-01 00:00:00 +0000 |
2931 | +++ tests/qmltests/Stages/DesktopMenuData.qml 2016-01-15 13:11:04 +0000 |
2932 | @@ -0,0 +1,702 @@ |
2933 | +import QtQuick 2.4 |
2934 | + |
2935 | +QtObject { |
2936 | + |
2937 | + property var dialerData: [{ |
2938 | + "rowData": { // 1.1 |
2939 | + "label": "_dialer1", |
2940 | + "sensitive": true, |
2941 | + "isSeparator": false, |
2942 | + "icon": "", |
2943 | + "type": "com.canonical.indicator.test", |
2944 | + "ext": {}, |
2945 | + "action": "dialer1", |
2946 | + "actionState": {}, |
2947 | + "isCheck": false, |
2948 | + "isRadio": false, |
2949 | + "isToggled": false, |
2950 | + "shortcut": "Alt+F" |
2951 | + }, |
2952 | + "submenu": [{ |
2953 | + "rowData": { // 1.1 |
2954 | + "label": "menu1.1", |
2955 | + "sensitive": true, |
2956 | + "isSeparator": false, |
2957 | + "icon": "", |
2958 | + "type": "com.canonical.indicator.test", |
2959 | + "ext": {}, |
2960 | + "action": "menu1.1", |
2961 | + "actionState": {}, |
2962 | + "isCheck": false, |
2963 | + "isRadio": false, |
2964 | + "isToggled": false, |
2965 | + "shortcut": "Alt+0" |
2966 | + }}, { |
2967 | + "rowData": { // 1.2 |
2968 | + "label": "menu1.2", |
2969 | + "sensitive": true, |
2970 | + "isSeparator": false, |
2971 | + "icon": "", |
2972 | + "type": "com.canonical.indicator.test", |
2973 | + "ext": {}, |
2974 | + "action": "menu1.2", |
2975 | + "actionState": {}, |
2976 | + "isCheck": false, |
2977 | + "isRadio": false, |
2978 | + "isToggled": false, |
2979 | + "shortcut": "Alt+1" |
2980 | + }, |
2981 | + "submenu": [{ |
2982 | + "rowData": { // 1.2.1 |
2983 | + "label": "menu1.2.1", |
2984 | + "sensitive": true, |
2985 | + "isSeparator": false, |
2986 | + "icon": "", |
2987 | + "type": "com.canonical.indicator.test", |
2988 | + "ext": {}, |
2989 | + "action": "menu1.2.1", |
2990 | + "actionState": {}, |
2991 | + "isCheck": false, |
2992 | + "isRadio": false, |
2993 | + "isToggled": false, |
2994 | + "shortcut": "" |
2995 | + }}, { |
2996 | + "rowData": { // 1.2.2 |
2997 | + "label": "menu1.2.2", |
2998 | + "sensitive": true, |
2999 | + "isSeparator": false, |
3000 | + "icon": "", |
3001 | + "type": "com.canonical.indicator.test", |
3002 | + "ext": {}, |
3003 | + "action": "menu1.2.2", |
3004 | + "actionState": {}, |
3005 | + "isCheck": false, |
3006 | + "isRadio": false, |
3007 | + "isToggled": false, |
3008 | + "shortcut": "" |
3009 | + }}, { |
3010 | + "rowData": { // 1.2.3 |
3011 | + "label": "", |
3012 | + "sensitive": false, |
3013 | + "isSeparator": true, |
3014 | + "icon": "", |
3015 | + "type": "", |
3016 | + "ext": {}, |
3017 | + "action": "", |
3018 | + "actionState": {}, |
3019 | + "isCheck": false, |
3020 | + "isRadio": false, |
3021 | + "isToggled": false, |
3022 | + "shortcut": "" |
3023 | + }}, { |
3024 | + "rowData": { // row 1.2.4 |
3025 | + "label": "menu1.2.4", |
3026 | + "sensitive": true, |
3027 | + "isSeparator": false, |
3028 | + "icon": "", |
3029 | + "type": "com.canonical.indicator.test", |
3030 | + "ext": {}, |
3031 | + "action": "menu1.2.4", |
3032 | + "actionState": {}, |
3033 | + "isCheck": false, |
3034 | + "isRadio": false, |
3035 | + "isToggled": true, |
3036 | + "shortcut": "" |
3037 | + }} |
3038 | + ]}, { |
3039 | + "rowData": { // 1.3 |
3040 | + "label": "", |
3041 | + "sensitive": false, |
3042 | + "isSeparator": true, |
3043 | + "icon": "", |
3044 | + "type": "", |
3045 | + "ext": {}, |
3046 | + "action": "", |
3047 | + "actionState": {}, |
3048 | + "isCheck": false, |
3049 | + "isRadio": false, |
3050 | + "isToggled": false, |
3051 | + "shortcut": "" |
3052 | + }}, { |
3053 | + "rowData": { // row 1.4 |
3054 | + "label": "menu1.4", |
3055 | + "sensitive": true, |
3056 | + "isSeparator": false, |
3057 | + "icon": "", |
3058 | + "type": "com.canonical.indicator.test", |
3059 | + "ext": {}, |
3060 | + "action": "menu1.4", |
3061 | + "actionState": {}, |
3062 | + "isCheck": true, |
3063 | + "isRadio": false, |
3064 | + "isToggled": true, |
3065 | + "shortcut": "Alt+2" |
3066 | + }} |
3067 | + ]}, { |
3068 | + "rowData": { // 2 |
3069 | + "label": "d_ialer2", |
3070 | + "sensitive": true, |
3071 | + "isSeparator": false, |
3072 | + "icon": "", |
3073 | + "type": "com.canonical.indicator.test", |
3074 | + "ext": {}, |
3075 | + "action": "dialer2", |
3076 | + "actionState": {}, |
3077 | + "isCheck": false, |
3078 | + "isRadio": false, |
3079 | + "isToggled": false, |
3080 | + "shortcut": "Alt+E" |
3081 | + }, |
3082 | + "submenu": [{ |
3083 | + "rowData": { // 2.1 |
3084 | + "label": "menu2.1", |
3085 | + "sensitive": true, |
3086 | + "isSeparator": false, |
3087 | + "icon": "", |
3088 | + "type": "com.canonical.indicator.test", |
3089 | + "ext": {}, |
3090 | + "action": "menu2.1", |
3091 | + "actionState": {}, |
3092 | + "isCheck": false, |
3093 | + "isRadio": false, |
3094 | + "isToggled": false, |
3095 | + "shortcut": "" |
3096 | + }} |
3097 | + ]}, { |
3098 | + "rowData": { // row 3 |
3099 | + "label": "di_aler3", |
3100 | + "sensitive": true, |
3101 | + "isSeparator": false, |
3102 | + "icon": "", |
3103 | + "type": "com.canonical.indicator.test", |
3104 | + "ext": {}, |
3105 | + "action": "dialer3", |
3106 | + "actionState": {}, |
3107 | + "isCheck": false, |
3108 | + "isRadio": false, |
3109 | + "isToggled": false, |
3110 | + "shortcut": "" |
3111 | + }, |
3112 | + "submenu": [{ |
3113 | + "rowData": { // 3.1 |
3114 | + "label": "menu3.1", |
3115 | + "sensitive": true, |
3116 | + "isSeparator": false, |
3117 | + "icon": "", |
3118 | + "type": "com.canonical.indicator.test", |
3119 | + "ext": {}, |
3120 | + "action": "menu3.1", |
3121 | + "actionState": {}, |
3122 | + "isCheck": false, |
3123 | + "isRadio": false, |
3124 | + "isToggled": false, |
3125 | + "shortcut": "" |
3126 | + }} |
3127 | + ]} |
3128 | + ] |
3129 | + |
3130 | + property var cameraData: [{ |
3131 | + "rowData": { // 1.1 |
3132 | + "label": "camera1", |
3133 | + "sensitive": true, |
3134 | + "isSeparator": false, |
3135 | + "icon": "", |
3136 | + "type": "com.canonical.indicator.test", |
3137 | + "ext": {}, |
3138 | + "action": "camera1", |
3139 | + "actionState": {}, |
3140 | + "isCheck": false, |
3141 | + "isRadio": false, |
3142 | + "isToggled": false, |
3143 | + "shortcut": "" |
3144 | + }, |
3145 | + "submenu": [{ |
3146 | + "rowData": { // 1.1 |
3147 | + "label": "menu1.1", |
3148 | + "sensitive": true, |
3149 | + "isSeparator": false, |
3150 | + "icon": "", |
3151 | + "type": "com.canonical.indicator.test", |
3152 | + "ext": {}, |
3153 | + "action": "menu1.1", |
3154 | + "actionState": {}, |
3155 | + "isCheck": false, |
3156 | + "isRadio": false, |
3157 | + "isToggled": false, |
3158 | + "shortcut": "" |
3159 | + }}, { |
3160 | + "rowData": { // 1.2 |
3161 | + "label": "menu1.2", |
3162 | + "sensitive": true, |
3163 | + "isSeparator": false, |
3164 | + "icon": "", |
3165 | + "type": "com.canonical.indicator.test", |
3166 | + "ext": {}, |
3167 | + "action": "menu1.2", |
3168 | + "actionState": {}, |
3169 | + "isCheck": false, |
3170 | + "isRadio": false, |
3171 | + "isToggled": false, |
3172 | + "shortcut": "" |
3173 | + }}, { |
3174 | + "rowData": { // row 1.2 |
3175 | + "label": "menu1.2", |
3176 | + "sensitive": true, |
3177 | + "isSeparator": false, |
3178 | + "icon": "", |
3179 | + "type": "com.canonical.indicator.test", |
3180 | + "ext": {}, |
3181 | + "action": "menu1.2", |
3182 | + "actionState": {}, |
3183 | + "isCheck": false, |
3184 | + "isRadio": false, |
3185 | + "isToggled": false, |
3186 | + "shortcut": "" |
3187 | + }} |
3188 | + ]}, { |
3189 | + "rowData": { // 2 |
3190 | + "label": "camera2", |
3191 | + "sensitive": true, |
3192 | + "isSeparator": false, |
3193 | + "icon": "", |
3194 | + "type": "com.canonical.indicator.test", |
3195 | + "ext": {}, |
3196 | + "action": "camera2", |
3197 | + "actionState": {}, |
3198 | + "isCheck": false, |
3199 | + "isRadio": false, |
3200 | + "isToggled": false, |
3201 | + "shortcut": "" |
3202 | + }} |
3203 | + ] |
3204 | + |
3205 | + property var galleryData: [{ |
3206 | + "rowData": { // 1.1 |
3207 | + "label": "gallery1", |
3208 | + "sensitive": true, |
3209 | + "isSeparator": false, |
3210 | + "icon": "", |
3211 | + "type": "com.canonical.indicator.test", |
3212 | + "ext": {}, |
3213 | + "action": "gallery1", |
3214 | + "actionState": {}, |
3215 | + "isCheck": false, |
3216 | + "isRadio": false, |
3217 | + "isToggled": false, |
3218 | + "shortcut": "" |
3219 | + }, |
3220 | + "submenu": [{ |
3221 | + "rowData": { // 1.1 |
3222 | + "label": "menu0", |
3223 | + "sensitive": true, |
3224 | + "isSeparator": false, |
3225 | + "icon": "", |
3226 | + "type": "com.canonical.indicator.test", |
3227 | + "ext": {}, |
3228 | + "action": "menu0", |
3229 | + "actionState": {}, |
3230 | + "isCheck": false, |
3231 | + "isRadio": false, |
3232 | + "isToggled": false, |
3233 | + "shortcut": "" |
3234 | + }}, { |
3235 | + "rowData": { // 1.2 |
3236 | + "label": "menu1", |
3237 | + "sensitive": true, |
3238 | + "isSeparator": false, |
3239 | + "icon": "", |
3240 | + "type": "com.canonical.indicator.test", |
3241 | + "ext": {}, |
3242 | + "action": "menu1", |
3243 | + "actionState": {}, |
3244 | + "isCheck": false, |
3245 | + "isRadio": false, |
3246 | + "isToggled": false, |
3247 | + "shortcut": "" |
3248 | + }}, { |
3249 | + "rowData": { // 1.2 |
3250 | + "label": "", |
3251 | + "sensitive": false, |
3252 | + "isSeparator": true, |
3253 | + "icon": "", |
3254 | + "type": "", |
3255 | + "ext": {}, |
3256 | + "action": "", |
3257 | + "actionState": {}, |
3258 | + "isCheck": false, |
3259 | + "isRadio": false, |
3260 | + "isToggled": false, |
3261 | + "shortcut": "" |
3262 | + }}, { |
3263 | + "rowData": { // row 1.2 |
3264 | + "label": "menu2", |
3265 | + "sensitive": true, |
3266 | + "isSeparator": false, |
3267 | + "icon": "", |
3268 | + "type": "com.canonical.indicator.test", |
3269 | + "ext": {}, |
3270 | + "action": "menu2", |
3271 | + "actionState": {}, |
3272 | + "isCheck": false, |
3273 | + "isRadio": false, |
3274 | + "isToggled": false, |
3275 | + "shortcut": "" |
3276 | + }} |
3277 | + ]}, { |
3278 | + "rowData": { // 2 |
3279 | + "label": "gallery2", |
3280 | + "sensitive": true, |
3281 | + "isSeparator": false, |
3282 | + "icon": "", |
3283 | + "type": "com.canonical.indicator.test", |
3284 | + "ext": {}, |
3285 | + "action": "gallery2", |
3286 | + "actionState": {}, |
3287 | + "isCheck": false, |
3288 | + "isRadio": false, |
3289 | + "isToggled": false, |
3290 | + "shortcut": "" |
3291 | + }}, { |
3292 | + "rowData": { // row 2 |
3293 | + "label": "gallery3", |
3294 | + "sensitive": true, |
3295 | + "isSeparator": false, |
3296 | + "icon": "", |
3297 | + "type": "com.canonical.indicator.test", |
3298 | + "ext": {}, |
3299 | + "action": "gallery3", |
3300 | + "actionState": {}, |
3301 | + "isCheck": false, |
3302 | + "isRadio": false, |
3303 | + "isToggled": false, |
3304 | + "shortcut": "" |
3305 | + }} |
3306 | + ] |
3307 | + |
3308 | + property var testData: [{ |
3309 | + "rowData": { // 1 |
3310 | + "label": "_menu1", |
3311 | + "sensitive": true, |
3312 | + "isSeparator": false, |
3313 | + "icon": "", |
3314 | + "type": "com.canonical.indicator.test", |
3315 | + "ext": {}, |
3316 | + "action": "menu1", |
3317 | + "actionState": {}, |
3318 | + "isCheck": false, |
3319 | + "isRadio": false, |
3320 | + "isToggled": false, |
3321 | + "shortcut": "Alt+F" |
3322 | + }, |
3323 | + "submenu": [{ |
3324 | + "rowData": { // 1.1 |
3325 | + "label": "menu1.1", |
3326 | + "sensitive": true, |
3327 | + "isSeparator": false, |
3328 | + "icon": "", |
3329 | + "type": "com.canonical.indicator.test", |
3330 | + "ext": {}, |
3331 | + "action": "menu1.1", |
3332 | + "actionState": {}, |
3333 | + "isCheck": false, |
3334 | + "isRadio": false, |
3335 | + "isToggled": false, |
3336 | + "shortcut": "Alt+0" |
3337 | + }}, { |
3338 | + "rowData": { // 1.2 |
3339 | + "label": "m_enu1.2", |
3340 | + "sensitive": true, |
3341 | + "isSeparator": false, |
3342 | + "icon": "", |
3343 | + "type": "com.canonical.indicator.test", |
3344 | + "ext": {}, |
3345 | + "action": "menu1.2", |
3346 | + "actionState": {}, |
3347 | + "isCheck": false, |
3348 | + "isRadio": false, |
3349 | + "isToggled": false, |
3350 | + "shortcut": "Alt+1" |
3351 | + }, |
3352 | + "submenu": [{ |
3353 | + "rowData": { // 1.2.1 |
3354 | + "label": "menu1.2.1", |
3355 | + "sensitive": true, |
3356 | + "isSeparator": false, |
3357 | + "icon": "", |
3358 | + "type": "com.canonical.indicator.test", |
3359 | + "ext": {}, |
3360 | + "action": "menu1.2.1", |
3361 | + "actionState": {}, |
3362 | + "isCheck": false, |
3363 | + "isRadio": false, |
3364 | + "isToggled": false, |
3365 | + "shortcut": "" |
3366 | + }}, { |
3367 | + "rowData": { // 1.2.2 |
3368 | + "label": "men_u1.2.2", |
3369 | + "sensitive": true, |
3370 | + "isSeparator": false, |
3371 | + "icon": "", |
3372 | + "type": "com.canonical.indicator.test", |
3373 | + "ext": {}, |
3374 | + "action": "menu1.2.2", |
3375 | + "actionState": {}, |
3376 | + "isCheck": false, |
3377 | + "isRadio": false, |
3378 | + "isToggled": false, |
3379 | + "shortcut": "" |
3380 | + }}, { |
3381 | + "rowData": { // 1.2.3 |
3382 | + "label": "", |
3383 | + "sensitive": false, |
3384 | + "isSeparator": true, |
3385 | + "icon": "", |
3386 | + "type": "", |
3387 | + "ext": {}, |
3388 | + "action": "", |
3389 | + "actionState": {}, |
3390 | + "isCheck": false, |
3391 | + "isRadio": false, |
3392 | + "isToggled": false, |
3393 | + "shortcut": "" |
3394 | + }}, { |
3395 | + "rowData": { // row 1.2.4 |
3396 | + "label": "menu1.2.4", |
3397 | + "sensitive": true, |
3398 | + "isSeparator": false, |
3399 | + "icon": "", |
3400 | + "type": "com.canonical.indicator.test", |
3401 | + "ext": {}, |
3402 | + "action": "menu1.2.4", |
3403 | + "actionState": {}, |
3404 | + "isCheck": false, |
3405 | + "isRadio": false, |
3406 | + "isToggled": true, |
3407 | + "shortcut": "" |
3408 | + }} |
3409 | + ]}, { |
3410 | + "rowData": { // 1.3 |
3411 | + "label": "", |
3412 | + "sensitive": false, |
3413 | + "isSeparator": true, |
3414 | + "icon": "", |
3415 | + "type": "", |
3416 | + "ext": {}, |
3417 | + "action": "", |
3418 | + "actionState": {}, |
3419 | + "isCheck": false, |
3420 | + "isRadio": false, |
3421 | + "isToggled": false, |
3422 | + "shortcut": "" |
3423 | + }}, { |
3424 | + "rowData": { // row 1.4 |
3425 | + "label": "menu1.4", |
3426 | + "sensitive": true, |
3427 | + "isSeparator": false, |
3428 | + "icon": "", |
3429 | + "type": "com.canonical.indicator.test", |
3430 | + "ext": {}, |
3431 | + "action": "menu1.4", |
3432 | + "actionState": {}, |
3433 | + "isCheck": true, |
3434 | + "isRadio": false, |
3435 | + "isToggled": true, |
3436 | + "shortcut": "Alt+2" |
3437 | + }} |
3438 | + ]}, { |
3439 | + "rowData": { // 2 |
3440 | + "label": "menu2", |
3441 | + "sensitive": true, |
3442 | + "isSeparator": false, |
3443 | + "icon": "", |
3444 | + "type": "com.canonical.indicator.test", |
3445 | + "ext": {}, |
3446 | + "action": "menu2", |
3447 | + "actionState": {}, |
3448 | + "isCheck": false, |
3449 | + "isRadio": false, |
3450 | + "isToggled": false, |
3451 | + "shortcut": "Alt+E" |
3452 | + }, |
3453 | + "submenu": [{ |
3454 | + "rowData": { // 2.1 |
3455 | + "label": "menu2.1", |
3456 | + "sensitive": true, |
3457 | + "isSeparator": false, |
3458 | + "icon": "", |
3459 | + "type": "com.canonical.indicator.test", |
3460 | + "ext": {}, |
3461 | + "action": "menu2.1", |
3462 | + "actionState": {}, |
3463 | + "isCheck": false, |
3464 | + "isRadio": false, |
3465 | + "isToggled": false, |
3466 | + "shortcut": "" |
3467 | + }} |
3468 | + ]}, { |
3469 | + "rowData": { // row 3 |
3470 | + "label": "me_nu3", |
3471 | + "sensitive": true, |
3472 | + "isSeparator": false, |
3473 | + "icon": "", |
3474 | + "type": "com.canonical.indicator.test", |
3475 | + "ext": {}, |
3476 | + "action": "dialer3", |
3477 | + "actionState": {}, |
3478 | + "isCheck": false, |
3479 | + "isRadio": false, |
3480 | + "isToggled": false, |
3481 | + "shortcut": "" |
3482 | + }, |
3483 | + "submenu": [{ |
3484 | + "rowData": { // 3.1 |
3485 | + "label": "menu3.1", |
3486 | + "sensitive": true, |
3487 | + "isSeparator": false, |
3488 | + "icon": "", |
3489 | + "type": "com.canonical.indicator.test", |
3490 | + "ext": {}, |
3491 | + "action": "menu3.1", |
3492 | + "actionState": {}, |
3493 | + "isCheck": false, |
3494 | + "isRadio": false, |
3495 | + "isToggled": false, |
3496 | + "shortcut": "" |
3497 | + }} |
3498 | + ]} |
3499 | + ] |
3500 | + |
3501 | + property var deepTestData: [{ |
3502 | + "rowData": { // 1 |
3503 | + "label": "_menu1", |
3504 | + "sensitive": true, |
3505 | + "isSeparator": false, |
3506 | + "icon": "", |
3507 | + "type": "com.canonical.indicator.test", |
3508 | + "ext": {}, |
3509 | + "action": "menu1", |
3510 | + "actionState": {}, |
3511 | + "isCheck": false, |
3512 | + "isRadio": false, |
3513 | + "isToggled": false, |
3514 | + "shortcut": "Alt+F" |
3515 | + }, |
3516 | + "submenu": [{ |
3517 | + "rowData": { // 1.1 |
3518 | + "label": "menu1.1", |
3519 | + "sensitive": true, |
3520 | + "isSeparator": false, |
3521 | + "icon": "", |
3522 | + "type": "com.canonical.indicator.test", |
3523 | + "ext": {}, |
3524 | + "action": "menu1.1", |
3525 | + "actionState": {}, |
3526 | + "isCheck": false, |
3527 | + "isRadio": false, |
3528 | + "isToggled": false, |
3529 | + "shortcut": "" |
3530 | + }, |
3531 | + "submenu": [{ |
3532 | + "rowData": { // 1.1.1 |
3533 | + "label": "menu1.1.1", |
3534 | + "sensitive": true, |
3535 | + "isSeparator": false, |
3536 | + "icon": "", |
3537 | + "type": "com.canonical.indicator.test", |
3538 | + "ext": {}, |
3539 | + "action": "menu1.1.1", |
3540 | + "actionState": {}, |
3541 | + "isCheck": false, |
3542 | + "isRadio": false, |
3543 | + "isToggled": false, |
3544 | + "shortcut": "" |
3545 | + }, |
3546 | + "submenu": [{ |
3547 | + "rowData": { // 1.1.1 |
3548 | + "label": "menu1.1.1.1", |
3549 | + "sensitive": true, |
3550 | + "isSeparator": false, |
3551 | + "icon": "", |
3552 | + "type": "com.canonical.indicator.test", |
3553 | + "ext": {}, |
3554 | + "action": "menu1.1.1.1", |
3555 | + "actionState": {}, |
3556 | + "isCheck": false, |
3557 | + "isRadio": false, |
3558 | + "isToggled": false, |
3559 | + "shortcut": "" |
3560 | + }, |
3561 | + "submenu": [{ |
3562 | + "rowData": { // 1.1.1.1 |
3563 | + "label": "menu1.1.1.1.1", |
3564 | + "sensitive": true, |
3565 | + "isSeparator": false, |
3566 | + "icon": "", |
3567 | + "type": "com.canonical.indicator.test", |
3568 | + "ext": {}, |
3569 | + "action": "menu1.1.1.1.1", |
3570 | + "actionState": {}, |
3571 | + "isCheck": false, |
3572 | + "isRadio": false, |
3573 | + "isToggled": false, |
3574 | + "shortcut": "" |
3575 | + }} |
3576 | + ]} |
3577 | + ]} |
3578 | + ]} |
3579 | + ]} |
3580 | + ] |
3581 | + |
3582 | + property var singleCheckable: [{ |
3583 | + "rowData": { // 1 |
3584 | + "label": "checkable1", |
3585 | + "sensitive": true, |
3586 | + "isSeparator": false, |
3587 | + "icon": "", |
3588 | + "type": "com.canonical.indicator.test", |
3589 | + "ext": {}, |
3590 | + "action": "checkable1", |
3591 | + "actionState": {}, |
3592 | + "isCheck": true, |
3593 | + "isRadio": false, |
3594 | + "isToggled": false, |
3595 | + "shortcut": "Alt+F" |
3596 | + } |
3597 | + }] |
3598 | + |
3599 | + function generateTestData(length, depth, separatorInterval, prefix) { |
3600 | + var data = []; |
3601 | + |
3602 | + if (prefix === undefined) prefix = "menu" |
3603 | + |
3604 | + for (var i = 0; i < length; i++) { |
3605 | + |
3606 | + var menuCode = String.fromCharCode(i+65); |
3607 | + |
3608 | + var isSeparator = separatorInterval > 0 && ((i+1) % separatorInterval == 0); |
3609 | + var row = { |
3610 | + "rowData": { // 1 |
3611 | + "label": prefix + "&" + menuCode, |
3612 | + "sensitive": true, |
3613 | + "isSeparator": isSeparator, |
3614 | + "icon": "", |
3615 | + "type": "com.canonical.indicator.test", |
3616 | + "ext": {}, |
3617 | + "action": prefix + menuCode, |
3618 | + "actionState": {}, |
3619 | + "isCheck": false, |
3620 | + "isRadio": false, |
3621 | + "isToggled": false, |
3622 | + "shortcut": "" |
3623 | + } |
3624 | + } |
3625 | + if (!isSeparator && depth > 1) { |
3626 | + var submenu = generateTestData(length, depth-1, separatorInterval, prefix + menuCode + "."); |
3627 | + row["submenu"] = submenu; |
3628 | + } |
3629 | + data[i] = row; |
3630 | + } |
3631 | + return data; |
3632 | + } |
3633 | +} |
3634 | + |
3635 | |
3636 | === modified file 'tests/qmltests/Stages/tst_DesktopStage.qml' |
3637 | --- tests/qmltests/Stages/tst_DesktopStage.qml 2016-01-07 14:00:21 +0000 |
3638 | +++ tests/qmltests/Stages/tst_DesktopStage.qml 2016-01-15 13:11:04 +0000 |
3639 | @@ -19,8 +19,10 @@ |
3640 | import Ubuntu.Components 1.3 |
3641 | import Ubuntu.Components.ListItems 1.3 |
3642 | import Unity.Application 0.1 |
3643 | +import Unity.Indicators 0.1 as Indicators |
3644 | import Unity.Test 0.1 |
3645 | import Utils 0.1 |
3646 | +import QMenuModel 0.1 |
3647 | |
3648 | import ".." // For EdgeBarrierControls |
3649 | import "../../../qml/Stages" |
3650 | @@ -40,7 +42,17 @@ |
3651 | value: false |
3652 | } |
3653 | |
3654 | - Component.onCompleted: resetGeometry() |
3655 | + DesktopMenuData { |
3656 | + id: appMenuData |
3657 | + } |
3658 | + |
3659 | + Component.onCompleted: { |
3660 | + resetGeometry(); |
3661 | + |
3662 | + Indicators.UnityMenuModelCache.setCachedModelData("/dialer-app", appMenuData.dialerData); |
3663 | + Indicators.UnityMenuModelCache.setCachedModelData("/camera-app", appMenuData.cameraData); |
3664 | + Indicators.UnityMenuModelCache.setCachedModelData("/gallery-app", appMenuData.galleryData); |
3665 | + } |
3666 | |
3667 | function resetGeometry() { |
3668 | // ensures apps which are tested decorations are in view. |
3669 | |
3670 | === added file 'tests/qmltests/Stages/tst_WindowDecoration.qml' |
3671 | --- tests/qmltests/Stages/tst_WindowDecoration.qml 1970-01-01 00:00:00 +0000 |
3672 | +++ tests/qmltests/Stages/tst_WindowDecoration.qml 2016-01-15 13:11:04 +0000 |
3673 | @@ -0,0 +1,163 @@ |
3674 | +/* |
3675 | + * Copyright (C) 2016 Canonical, Ltd. |
3676 | + * |
3677 | + * This program is free software; you can redistribute it and/or modify |
3678 | + * it under the terms of the GNU General Public License as published by |
3679 | + * the Free Software Foundation; version 3. |
3680 | + * |
3681 | + * This program is distributed in the hope that it will be useful, |
3682 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3683 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3684 | + * GNU General Public License for more details. |
3685 | + * |
3686 | + * You should have received a copy of the GNU General Public License |
3687 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3688 | + */ |
3689 | + |
3690 | +import QtQuick 2.4 |
3691 | +import QtTest 1.0 |
3692 | +import QtQuick.Layouts 1.1 |
3693 | +import Ubuntu.Components 1.3 |
3694 | +import Ubuntu.Components.ListItems 1.3 |
3695 | +import Unity.Application 0.1 |
3696 | +import Unity.Test 0.1 |
3697 | +import Utils 0.1 |
3698 | +import QMenuModel 0.1 |
3699 | + |
3700 | +import "../../../qml/Stages" |
3701 | + |
3702 | +Item { |
3703 | + id: root |
3704 | + width: units.gu(70) |
3705 | + height: units.gu(50) |
3706 | + |
3707 | + Binding { |
3708 | + target: MouseTouchAdaptor |
3709 | + property: "enabled" |
3710 | + value: false |
3711 | + } |
3712 | + |
3713 | + DesktopMenuData { id: desktopMenuData } |
3714 | + |
3715 | + Rectangle { |
3716 | + anchors { |
3717 | + left: parent.left |
3718 | + right: parent.right |
3719 | + bottom: parent.bottom |
3720 | + margins: units.gu(1) |
3721 | + } |
3722 | + height: units.gu(20) |
3723 | + border.width: 1 |
3724 | + border.color: "black" |
3725 | + |
3726 | + TextArea { |
3727 | + id: log |
3728 | + anchors.fill: parent |
3729 | + readOnly: true |
3730 | + } |
3731 | + } |
3732 | + |
3733 | + WindowDecoration { |
3734 | + id: decoration |
3735 | + anchors { left: parent.left; right: parent.right; top: parent.top } |
3736 | + anchors.margins: units.gu(1) |
3737 | + title: "TestTitle - Doing something" |
3738 | + active: true |
3739 | + height: units.gu(3) |
3740 | + menu: menuBackend |
3741 | + UnityMenuModel { |
3742 | + id: menuBackend |
3743 | + modelData: desktopMenuData.testData |
3744 | + onActivated: log.text = "Activated " + action + "\n" + log.text |
3745 | + } |
3746 | + |
3747 | + onClose: log.text = "Close\n" + log.text |
3748 | + onMinimize: log.text = "Minimize\n" + log.text |
3749 | + onMaximize: log.text = "Maximize\n" + log.text |
3750 | + } |
3751 | + |
3752 | + SignalSpy { |
3753 | + id: signalSpy |
3754 | + target: decoration |
3755 | + } |
3756 | + |
3757 | + UnityTestCase { |
3758 | + id: testCase |
3759 | + name: "MenuPage" |
3760 | + when: windowShown |
3761 | + |
3762 | + function init() { |
3763 | + decoration.menu = menuBackend; |
3764 | + signalSpy.clear(); |
3765 | + } |
3766 | + |
3767 | + function test_windowControlButtons_data() { |
3768 | + return [ { tag: "close", controlName: "closeWindowButton", signal: "close"}, |
3769 | + { tag: "minimize", controlName: "minimizeWindowButton", signal: "minimize"}, |
3770 | + { tag: "maximize", controlName: "maximizeWindowButton", signal: "maximize"}]; |
3771 | + } |
3772 | + |
3773 | + function test_windowControlButtons(data) { |
3774 | + signalSpy.signalName = data.signal; |
3775 | + var controlButton = findChild(decoration, data.controlName); |
3776 | + verify(controlButton !== null); |
3777 | + |
3778 | + mouseClick(controlButton, controlButton.width/2, controlButton.height/2); |
3779 | + compare(signalSpy.count, 1); |
3780 | + } |
3781 | + |
3782 | + function test_titleRemainsWhenHoveringOnTitleBarWithNoMenu() { |
3783 | + decoration.menu = undefined; |
3784 | + |
3785 | + var menuLoader = findChild(decoration, "windowDecorationMenuBarLoader"); |
3786 | + verify(menuLoader); |
3787 | + mouseMove(menuLoader, menuLoader.width/2, menuLoader.height/2); |
3788 | + wait(200); |
3789 | + |
3790 | + var titleLabel = findChild(decoration, "windowDecorationTitle"); |
3791 | + verify(menuLoader); |
3792 | + |
3793 | + compare(menuLoader.opacity, 0, "Menu should not show when present") |
3794 | + compare(titleLabel.opacity, 1, "Title should always show when app menu not present") |
3795 | + } |
3796 | + |
3797 | + function test_menuShowsWhenHoveringOnTitleBar() { |
3798 | + var menuLoader = findChild(decoration, "windowDecorationMenuBarLoader"); |
3799 | + verify(menuLoader); |
3800 | + mouseMove(menuLoader, menuLoader.width/2, menuLoader.height/2) |
3801 | + |
3802 | + var titleLabel = findChild(decoration, "windowDecorationTitle"); |
3803 | + verify(menuLoader); |
3804 | + |
3805 | + tryCompare(menuLoader, "opacity", 1); |
3806 | + tryCompare(titleLabel, "opacity", 0); |
3807 | + |
3808 | + mouseMove(menuLoader, menuLoader.width/2, menuLoader.height * 2); |
3809 | + |
3810 | + tryCompare(menuLoader, "opacity", 0); |
3811 | + tryCompare(titleLabel, "opacity", 1); |
3812 | + } |
3813 | + |
3814 | + function test_showMenuBarWithShortcutsOnLongAltPress() { |
3815 | + var menuLoader = findChild(decoration, "windowDecorationMenuBarLoader"); |
3816 | + verify(menuLoader); |
3817 | + |
3818 | + var titleLabel = findChild(decoration, "windowDecorationTitle"); |
3819 | + verify(menuLoader); |
3820 | + |
3821 | + var menuBar = findChild(decoration, "menuBar"); |
3822 | + verify(menuBar); |
3823 | + verify(menuBar.enableMnemonic === false, "Menubar should not show shortcuts") |
3824 | + |
3825 | + keyPress(Qt.Key_Alt, Qt.NoModifier); |
3826 | + tryCompare(menuLoader, "opacity", 1); |
3827 | + tryCompare(titleLabel, "opacity", 0); |
3828 | + compare(menuBar.enableMnemonic, true, "Menubar should show shortcuts when long alt pressed"); |
3829 | + |
3830 | + keyRelease(Qt.Key_Alt, Qt.NoModifier); |
3831 | + tryCompare(menuLoader, "opacity", 0); |
3832 | + tryCompare(titleLabel, "opacity", 1); |
3833 | + compare(menuBar.enableMnemonic, false, "Menubar should not show shortcuts after long alt pressed"); |
3834 | + } |
3835 | + } |
3836 | +} |
3837 | |
3838 | === modified file 'tests/qmltests/tst_Shell.qml' |
3839 | --- tests/qmltests/tst_Shell.qml 2015-12-08 15:37:51 +0000 |
3840 | +++ tests/qmltests/tst_Shell.qml 2016-01-15 13:11:04 +0000 |
3841 | @@ -30,6 +30,7 @@ |
3842 | import Powerd 0.1 |
3843 | import Wizard 0.1 as Wizard |
3844 | import Utils 0.1 |
3845 | +import Unity.Indicators 0.1 as Indicators |
3846 | |
3847 | import "../../qml" |
3848 | import "../../qml/Components" |
3849 | @@ -47,6 +48,14 @@ |
3850 | LightDM.Greeter.mockMode = "single"; |
3851 | LightDM.Users.mockMode = "single"; |
3852 | shellLoader.active = true; |
3853 | + |
3854 | + Indicators.UnityMenuModelCache.setCachedModelData("/dialer-app", appMenuData.dialerData); |
3855 | + Indicators.UnityMenuModelCache.setCachedModelData("/camera-app", appMenuData.cameraData); |
3856 | + Indicators.UnityMenuModelCache.setCachedModelData("/gallery-app", appMenuData.galleryData); |
3857 | + } |
3858 | + |
3859 | + DesktopMenuData { |
3860 | + id: appMenuData |
3861 | } |
3862 | |
3863 | Item { |
3864 | @@ -1752,17 +1761,17 @@ |
3865 | var maximizeButton = findChild(appDelegate, "maximizeWindowButton"); |
3866 | |
3867 | tryCompare(appDelegate, "state", "normal"); |
3868 | - tryCompare(PanelState, "buttonsVisible", false) |
3869 | + tryCompare(PanelState, "decorationsVisible", false) |
3870 | |
3871 | mouseClick(maximizeButton, maximizeButton.width / 2, maximizeButton.height / 2); |
3872 | tryCompare(appDelegate, "state", "maximized"); |
3873 | - tryCompare(PanelState, "buttonsVisible", true) |
3874 | + tryCompare(PanelState, "decorationsVisible", true) |
3875 | |
3876 | ApplicationManager.stopApplication(appId); |
3877 | - tryCompare(PanelState, "buttonsVisible", false) |
3878 | + tryCompare(PanelState, "decorationsVisible", false) |
3879 | |
3880 | ApplicationManager.startApplication(appId); |
3881 | - tryCompare(PanelState, "buttonsVisible", true) |
3882 | + tryCompare(PanelState, "decorationsVisible", true) |
3883 | } |
3884 | |
3885 | function test_newAppHasValidGeometry() { |
FAILED: Continuous integration, rev:2109 /unity8- jenkins. ubuntu. com/job/ lp-unity8- 1-ci/90/
https:/
Executed test runs:
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- 1-ci/90/ rebuild
https:/