Merge lp:~nick-dedekind/unity8/menu.overflow into lp:unity8
- menu.overflow
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Albert Astals Cid |
Approved revision: | 2790 |
Merged at revision: | 2815 |
Proposed branch: | lp:~nick-dedekind/unity8/menu.overflow |
Merge into: | lp:unity8 |
Prerequisite: | lp:~aacid/unity8/eatHoverWhenMenuIsOpen |
Diff against target: |
978 lines (+503/-188) 12 files modified
plugins/Utils/CMakeLists.txt (+1/-0) plugins/Utils/expressionfiltermodel.cpp (+49/-0) plugins/Utils/expressionfiltermodel.h (+42/-0) plugins/Utils/plugin.cpp (+2/-0) qml/ApplicationMenus/MenuBar.qml (+317/-175) qml/ApplicationMenus/MenuItem.qml (+3/-1) qml/ApplicationMenus/MenuNavigator.qml (+23/-0) qml/ApplicationMenus/MenuPopup.qml (+26/-3) qml/Panel/Panel.qml (+3/-1) tests/mocks/Utils/CMakeLists.txt (+1/-0) tests/mocks/Utils/plugin.cpp (+2/-0) tests/qmltests/ApplicationMenus/tst_MenuBar.qml (+34/-8) |
To merge this branch: | bzr merge lp:~nick-dedekind/unity8/menu.overflow |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Albert Astals Cid (community) | Approve | ||
Unity8 CI Bot | continuous-integration | Approve | |
Lukáš Tinkl | Pending | ||
Review via email: mp+315925@code.launchpad.net |
This proposal supersedes a proposal from 2017-01-24.
Commit message
Added overflow support to application menus.
Hover timer for auto-scrolling menu popup overflow.
Description of the change
* Are there any related MPs required for this MP to build/function as expected? Please list.
N/A
* Did you perform an exploratory manual test run of your code change and any related functionality?
Yes
* If you changed the packaging (debian), did you subscribe the ubuntu-unity team to this MP?
N/A
* If you changed the UI, has there been a design review?
Yes
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
Lukáš Tinkl (lukas-kde) wrote : Posted in a previous version of this proposal | # |
/<<BUILDDIR>
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2778
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2779
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:2780
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2781
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Michał Sawicz (saviq) wrote : Posted in a previous version of this proposal | # |
MenuNavigator.qml: bad whitespace in line 83
Albert Astals Cid (aacid) wrote : Posted in a previous version of this proposal | # |
Text conflict in plugins/
Text conflict in plugins/
Text conflict in tests/mocks/
Text conflict in tests/mocks/
Text conflict in tests/qmltests/
5 conflicts encountered.
Nick Dedekind (nick-dedekind) wrote : Posted in a previous version of this proposal | # |
> Text conflict in plugins/
> Text conflict in plugins/
> Text conflict in tests/mocks/
> Text conflict in tests/mocks/
> Text conflict in tests/qmltests/
> 5 conflicts encountered.
merged
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2782
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
PASSED: Continuous integration, rev:2782
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2785
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : Posted in a previous version of this proposal | # |
FAILED: Continuous integration, rev:2785
https:/
Executed test runs:
SUCCESS: https:/
UNSTABLE: https:/
UNSTABLE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Albert Astals Cid (aacid) wrote : | # |
Seems you have broken the menu positioning when it collides with the bottom border.
Run make tryDesktopStage, move the window down so that the menu would collide with the window bottom border and open it, previously it would show inside the window just fine, now it shows just the Up/down arrows and that's it.
- 2786. By Nick Dedekind
-
removed height change
Albert Astals Cid (aacid) wrote : | # |
Seems you removed two tests from tst_MenuBar.qml by mistake?
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:2786
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 2787. By Nick Dedekind
-
moved shouldDisplay connection
Albert Astals Cid (aacid) wrote : | # |
Looking good :)
Some minor things
Readd test_menuActiva
Remove console.
Make readonly (if possible)
visualItem: property bool popupVisible
visualItem: property bool shouldDisplay
overflowButton: property bool popupVisible
overflowButton: property Item firstInvisibleItem
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2787
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 2788. By Nick Dedekind
-
include panel height in max height calc
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2788
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
- 2789. By Nick Dedekind
-
readd tests
- 2790. By Nick Dedekind
-
review comments
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2789
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:2790
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Albert Astals Cid (aacid) wrote : | # |
* Did you perform an exploratory manual test run of the code change and any related functionality?
Yes
* Did CI run pass? If not, please explain why.
Yes
- 2791. By Nick Dedekind
-
merged with parent
Preview Diff
1 | === modified file 'plugins/Utils/CMakeLists.txt' | |||
2 | --- plugins/Utils/CMakeLists.txt 2016-12-21 10:20:36 +0000 | |||
3 | +++ plugins/Utils/CMakeLists.txt 2017-02-07 16:54:54 +0000 | |||
4 | @@ -35,6 +35,7 @@ | |||
5 | 35 | globalfunctions.cpp | 35 | globalfunctions.cpp |
6 | 36 | URLDispatcher.cpp | 36 | URLDispatcher.cpp |
7 | 37 | tabfocusfence.cpp | 37 | tabfocusfence.cpp |
8 | 38 | expressionfiltermodel.cpp | ||
9 | 38 | plugin.cpp | 39 | plugin.cpp |
10 | 39 | ) | 40 | ) |
11 | 40 | 41 | ||
12 | 41 | 42 | ||
13 | === added file 'plugins/Utils/expressionfiltermodel.cpp' | |||
14 | --- plugins/Utils/expressionfiltermodel.cpp 1970-01-01 00:00:00 +0000 | |||
15 | +++ plugins/Utils/expressionfiltermodel.cpp 2017-02-07 16:54:54 +0000 | |||
16 | @@ -0,0 +1,49 @@ | |||
17 | 1 | /* | ||
18 | 2 | * Copyright (C) 2017 Canonical, Ltd. | ||
19 | 3 | * | ||
20 | 4 | * This program is free software; you can redistribute it and/or modify | ||
21 | 5 | * it under the terms of the GNU General Public License as published by | ||
22 | 6 | * the Free Software Foundation; version 3. | ||
23 | 7 | * | ||
24 | 8 | * This program is distributed in the hope that it will be useful, | ||
25 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
26 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
27 | 11 | * GNU General Public License for more details. | ||
28 | 12 | * | ||
29 | 13 | * You should have received a copy of the GNU General Public License | ||
30 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
31 | 15 | */ | ||
32 | 16 | |||
33 | 17 | #include "expressionfiltermodel.h" | ||
34 | 18 | |||
35 | 19 | ExpressionFilterModel::ExpressionFilterModel(QObject *parent) | ||
36 | 20 | : UnitySortFilterProxyModelQML(parent) | ||
37 | 21 | { | ||
38 | 22 | } | ||
39 | 23 | |||
40 | 24 | QJSValue ExpressionFilterModel::matchExpression() const | ||
41 | 25 | { | ||
42 | 26 | return m_matchExpression; | ||
43 | 27 | } | ||
44 | 28 | |||
45 | 29 | void ExpressionFilterModel::setMatchExpression(const QJSValue &value) | ||
46 | 30 | { | ||
47 | 31 | m_matchExpression = value; | ||
48 | 32 | invalidateFilter(); | ||
49 | 33 | } | ||
50 | 34 | |||
51 | 35 | bool | ||
52 | 36 | ExpressionFilterModel::filterAcceptsRow(int sourceRow, | ||
53 | 37 | const QModelIndex &sourceParent) const | ||
54 | 38 | { | ||
55 | 39 | if (m_matchExpression.isCallable()) { | ||
56 | 40 | QJSValueList args; | ||
57 | 41 | args << sourceRow; | ||
58 | 42 | QJSValue ret = m_matchExpression.call(args); | ||
59 | 43 | if (ret.isBool()) { | ||
60 | 44 | return ret.toBool(); | ||
61 | 45 | } | ||
62 | 46 | } | ||
63 | 47 | |||
64 | 48 | return UnitySortFilterProxyModelQML::filterAcceptsRow(sourceRow, sourceParent); | ||
65 | 49 | } | ||
66 | 0 | 50 | ||
67 | === added file 'plugins/Utils/expressionfiltermodel.h' | |||
68 | --- plugins/Utils/expressionfiltermodel.h 1970-01-01 00:00:00 +0000 | |||
69 | +++ plugins/Utils/expressionfiltermodel.h 2017-02-07 16:54:54 +0000 | |||
70 | @@ -0,0 +1,42 @@ | |||
71 | 1 | /* | ||
72 | 2 | * Copyright (C) 2017 Canonical, Ltd. | ||
73 | 3 | * | ||
74 | 4 | * This program is free software; you can redistribute it and/or modify | ||
75 | 5 | * it under the terms of the GNU General Public License as published by | ||
76 | 6 | * the Free Software Foundation; version 3. | ||
77 | 7 | * | ||
78 | 8 | * This program is distributed in the hope that it will be useful, | ||
79 | 9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
80 | 10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
81 | 11 | * GNU General Public License for more details. | ||
82 | 12 | * | ||
83 | 13 | * You should have received a copy of the GNU General Public License | ||
84 | 14 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
85 | 15 | */ | ||
86 | 16 | |||
87 | 17 | #ifndef EXPRESSIONFILTERMODEL_H | ||
88 | 18 | #define EXPRESSIONFILTERMODEL_H | ||
89 | 19 | |||
90 | 20 | #include "unitysortfilterproxymodelqml.h" | ||
91 | 21 | #include <QJSValue> | ||
92 | 22 | |||
93 | 23 | class ExpressionFilterModel : public UnitySortFilterProxyModelQML | ||
94 | 24 | { | ||
95 | 25 | Q_OBJECT | ||
96 | 26 | Q_PROPERTY(QJSValue matchExpression READ matchExpression WRITE setMatchExpression NOTIFY matchExpressionChanged) | ||
97 | 27 | public: | ||
98 | 28 | explicit ExpressionFilterModel(QObject *parent = 0); | ||
99 | 29 | |||
100 | 30 | bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; | ||
101 | 31 | |||
102 | 32 | QJSValue matchExpression() const; | ||
103 | 33 | void setMatchExpression(const QJSValue& value); | ||
104 | 34 | |||
105 | 35 | Q_SIGNALS: | ||
106 | 36 | void matchExpressionChanged(); | ||
107 | 37 | |||
108 | 38 | private: | ||
109 | 39 | mutable QJSValue m_matchExpression; | ||
110 | 40 | }; | ||
111 | 41 | |||
112 | 42 | #endif // EXPRESSIONFILTERMODEL_H | ||
113 | 0 | 43 | ||
114 | === modified file 'plugins/Utils/plugin.cpp' | |||
115 | --- plugins/Utils/plugin.cpp 2017-01-03 12:16:00 +0000 | |||
116 | +++ plugins/Utils/plugin.cpp 2017-02-07 16:54:54 +0000 | |||
117 | @@ -41,6 +41,7 @@ | |||
118 | 41 | #include "URLDispatcher.h" | 41 | #include "URLDispatcher.h" |
119 | 42 | #include "appdrawerproxymodel.h" | 42 | #include "appdrawerproxymodel.h" |
120 | 43 | #include "tabfocusfence.h" | 43 | #include "tabfocusfence.h" |
121 | 44 | #include "expressionfiltermodel.h" | ||
122 | 44 | 45 | ||
123 | 45 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) | 46 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
124 | 46 | { | 47 | { |
125 | @@ -86,4 +87,5 @@ | |||
126 | 86 | qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher"); | 87 | qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher"); |
127 | 87 | qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel"); | 88 | qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel"); |
128 | 88 | qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence"); | 89 | qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence"); |
129 | 90 | qmlRegisterType<ExpressionFilterModel>(uri, 0, 1, "ExpressionFilterModel"); | ||
130 | 89 | } | 91 | } |
131 | 90 | 92 | ||
132 | === modified file 'qml/ApplicationMenus/MenuBar.qml' | |||
133 | --- qml/ApplicationMenus/MenuBar.qml 2017-02-07 16:54:54 +0000 | |||
134 | +++ qml/ApplicationMenus/MenuBar.qml 2017-02-07 16:54:54 +0000 | |||
135 | @@ -24,12 +24,13 @@ | |||
136 | 24 | id: root | 24 | id: root |
137 | 25 | objectName: "menuBar" | 25 | objectName: "menuBar" |
138 | 26 | 26 | ||
139 | 27 | // set from outside | ||
140 | 27 | property alias unityMenuModel: rowRepeater.model | 28 | property alias unityMenuModel: rowRepeater.model |
141 | 29 | property bool enableKeyFilter: false | ||
142 | 30 | property real overflowWidth: width | ||
143 | 28 | 31 | ||
144 | 32 | // read from outside | ||
145 | 29 | readonly property bool valid: rowRepeater.count > 0 | 33 | readonly property bool valid: rowRepeater.count > 0 |
146 | 30 | |||
147 | 31 | property bool enableKeyFilter: false | ||
148 | 32 | |||
149 | 33 | readonly property bool showRequested: d.longAltPressed || d.currentItem != null | 34 | readonly property bool showRequested: d.longAltPressed || d.currentItem != null |
150 | 34 | 35 | ||
151 | 35 | implicitWidth: row.width | 36 | implicitWidth: row.width |
152 | @@ -75,197 +76,334 @@ | |||
153 | 75 | acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton | 76 | acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton |
154 | 76 | anchors.fill: parent | 77 | anchors.fill: parent |
155 | 77 | enabled: d.currentItem != null | 78 | enabled: d.currentItem != null |
157 | 78 | hoverEnabled: enabled && d.currentItem.__popup != null | 79 | hoverEnabled: enabled && d.currentItem && d.currentItem.__popup != null |
158 | 79 | onPressed: d.dismissAll() | 80 | onPressed: d.dismissAll() |
159 | 80 | } | 81 | } |
160 | 81 | 82 | ||
298 | 82 | Item { | 83 | Row { |
299 | 83 | id: clippingItem | 84 | id: row |
300 | 84 | 85 | spacing: units.gu(2) | |
301 | 85 | height: root.height | 86 | height: parent.height |
302 | 86 | width: root.width | 87 | |
303 | 87 | clip: true | 88 | ActionContext { |
304 | 88 | 89 | id: menuBarContext | |
305 | 89 | Row { | 90 | objectName: "barContext" |
306 | 90 | id: row | 91 | active: !d.currentItem && enableKeyFilter |
307 | 91 | spacing: units.gu(2) | 92 | } |
308 | 92 | height: parent.height | 93 | |
309 | 93 | 94 | Connections { | |
310 | 94 | ActionContext { | 95 | target: root.unityMenuModel |
311 | 95 | id: menuBarContext | 96 | onModelReset: d.firstInvisibleIndex = undefined |
312 | 96 | objectName: "barContext" | 97 | } |
313 | 97 | active: !d.currentItem && enableKeyFilter | 98 | |
314 | 98 | } | 99 | Repeater { |
315 | 99 | 100 | id: rowRepeater | |
316 | 100 | Repeater { | 101 | |
317 | 101 | id: rowRepeater | 102 | onItemAdded: d.recalcFirstInvisibleIndexAdded(index, item) |
318 | 102 | 103 | onCountChanged: d.recalcFirstInvisibleIndex() | |
319 | 103 | Item { | 104 | |
320 | 104 | id: visualItem | 105 | Item { |
321 | 105 | objectName: root.objectName + "-item" + __ownIndex | 106 | id: visualItem |
322 | 106 | 107 | objectName: root.objectName + "-item" + __ownIndex | |
323 | 107 | readonly property int __ownIndex: index | 108 | |
324 | 108 | property Item __popup: null; | 109 | readonly property int __ownIndex: index |
325 | 109 | property bool popupVisible: __popup && __popup.visible | 110 | property Item __popup: null; |
326 | 110 | 111 | readonly property bool popupVisible: __popup && __popup.visible | |
327 | 111 | implicitWidth: column.implicitWidth | 112 | readonly property bool shouldDisplay: x + width + ((__ownIndex < rowRepeater.count-1) ? units.gu(2) : 0) < |
328 | 112 | implicitHeight: row.height | 113 | root.overflowWidth - ((__ownIndex < rowRepeater.count-1) ? overflowButton.width : 0) |
329 | 113 | enabled: model.sensitive | 114 | |
330 | 114 | 115 | implicitWidth: column.implicitWidth | |
331 | 115 | function show() { | 116 | implicitHeight: row.height |
332 | 116 | if (!__popup) { | 117 | enabled: model.sensitive && shouldDisplay |
333 | 117 | __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" }); | 118 | opacity: shouldDisplay ? 1 : 0 |
334 | 118 | __popup.childActivated.connect(dismiss); | 119 | |
335 | 119 | // force the current item to be the newly popped up menu | 120 | function show() { |
336 | 120 | } else { | 121 | if (!__popup) { |
337 | 121 | __popup.show(); | 122 | __popup = menuComponent.createObject(root, { objectName: visualItem.objectName + "-menu" }); |
338 | 122 | } | 123 | __popup.childActivated.connect(dismiss); |
339 | 123 | d.currentItem = visualItem; | 124 | // force the current item to be the newly popped up menu |
340 | 124 | } | 125 | } else { |
341 | 125 | function hide() { | 126 | __popup.show(); |
342 | 126 | if (__popup) { | 127 | } |
343 | 127 | __popup.hide(); | 128 | d.currentItem = visualItem; |
344 | 128 | 129 | } | |
345 | 129 | if (d.currentItem === visualItem) { | 130 | function hide() { |
346 | 130 | d.currentItem = null; | 131 | if (__popup) { |
347 | 131 | } | 132 | __popup.hide(); |
348 | 132 | } | 133 | |
349 | 133 | } | 134 | if (d.currentItem === visualItem) { |
350 | 134 | function dismiss() { | 135 | d.currentItem = null; |
351 | 135 | if (__popup) { | 136 | } |
352 | 136 | __popup.destroy(); | 137 | } |
353 | 137 | __popup = null; | 138 | } |
354 | 138 | 139 | function dismiss() { | |
355 | 139 | if (d.currentItem === visualItem) { | 140 | if (__popup) { |
356 | 140 | d.currentItem = null; | 141 | __popup.destroy(); |
357 | 141 | } | 142 | __popup = null; |
358 | 142 | } | 143 | |
359 | 143 | } | 144 | if (d.currentItem === visualItem) { |
360 | 144 | 145 | d.currentItem = null; | |
361 | 145 | Connections { | 146 | } |
362 | 146 | target: d | 147 | } |
363 | 147 | onDismissAll: visualItem.dismiss() | 148 | } |
364 | 148 | } | 149 | |
365 | 149 | 150 | onVisibleChanged: { | |
366 | 150 | Component { | 151 | if (!visible && __popup) dismiss(); |
367 | 151 | id: menuComponent | 152 | } |
368 | 152 | MenuPopup { | 153 | |
369 | 153 | desiredX: visualItem.x - units.gu(1) | 154 | onShouldDisplayChanged: { |
370 | 154 | desiredY: parent.height | 155 | if ((!shouldDisplay && d.firstInvisibleIndex == undefined) || __ownIndex <= d.firstInvisibleIndex) { |
371 | 155 | unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex) | 156 | d.recalcFirstInvisibleIndex(); |
372 | 156 | 157 | } | |
373 | 157 | Component.onCompleted: reset(); | 158 | } |
374 | 158 | } | 159 | |
375 | 159 | } | 160 | Connections { |
376 | 160 | 161 | target: d | |
377 | 161 | RowLayout { | 162 | onDismissAll: visualItem.dismiss() |
378 | 162 | id: column | 163 | } |
379 | 163 | spacing: units.gu(1) | 164 | |
380 | 164 | anchors { | 165 | Component { |
381 | 165 | centerIn: parent | 166 | id: menuComponent |
382 | 166 | } | 167 | MenuPopup { |
383 | 167 | 168 | desiredX: visualItem.x - units.gu(1) | |
384 | 168 | Icon { | 169 | desiredY: parent.height |
385 | 169 | Layout.preferredWidth: units.gu(2) | 170 | unityMenuModel: root.unityMenuModel.submenu(visualItem.__ownIndex) |
386 | 170 | Layout.preferredHeight: units.gu(2) | 171 | |
387 | 171 | Layout.alignment: Qt.AlignVCenter | 172 | Component.onCompleted: reset(); |
388 | 172 | 173 | } | |
389 | 173 | visible: model.icon || false | 174 | } |
390 | 174 | source: model.icon || "" | 175 | |
391 | 175 | } | 176 | RowLayout { |
392 | 176 | 177 | id: column | |
393 | 177 | ActionItem { | 178 | spacing: units.gu(1) |
394 | 178 | id: actionItem | 179 | anchors { |
395 | 179 | width: _title.width | 180 | centerIn: parent |
396 | 180 | height: _title.height | 181 | } |
397 | 181 | 182 | ||
398 | 182 | action: Action { | 183 | Icon { |
399 | 183 | // FIXME - SDK Action:text modifies menu text with html underline for mnemonic | 184 | Layout.preferredWidth: units.gu(2) |
400 | 184 | text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") | 185 | Layout.preferredHeight: units.gu(2) |
401 | 185 | 186 | Layout.alignment: Qt.AlignVCenter | |
402 | 186 | onTriggered: { | 187 | |
403 | 187 | visualItem.show(); | 188 | visible: model.icon || false |
404 | 188 | } | 189 | source: model.icon || "" |
405 | 189 | } | 190 | } |
406 | 190 | 191 | ||
407 | 191 | Label { | 192 | ActionItem { |
408 | 192 | id: _title | 193 | id: actionItem |
409 | 193 | text: actionItem.text | 194 | width: _title.width |
410 | 194 | horizontalAlignment: Text.AlignLeft | 195 | height: _title.height |
411 | 195 | color: enabled ? "white" : "#5d5d5d" | 196 | |
412 | 196 | } | 197 | action: Action { |
413 | 197 | } | 198 | enabled: visualItem.enabled |
414 | 198 | } | 199 | // FIXME - SDK Action:text modifies menu text with html underline for mnemonic |
415 | 199 | } // Item ( delegate ) | 200 | text: model.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") |
416 | 200 | } // Repeater | 201 | |
417 | 201 | } // Row | 202 | onTriggered: { |
418 | 202 | 203 | visualItem.show(); | |
419 | 203 | MouseArea { | 204 | } |
420 | 204 | anchors.fill: parent | 205 | } |
421 | 205 | hoverEnabled: d.currentItem | 206 | |
422 | 206 | 207 | Label { | |
423 | 207 | onEntered: { | 208 | id: _title |
424 | 208 | if (d.currentItem) { | 209 | text: actionItem.text |
425 | 209 | updateCurrentItemFromPosition(Qt.point(mouseX, mouseY)) | 210 | horizontalAlignment: Text.AlignLeft |
426 | 210 | } | 211 | color: enabled ? "white" : "#5d5d5d" |
427 | 211 | } | 212 | } |
428 | 212 | onPositionChanged: { | 213 | } |
429 | 213 | if (d.currentItem) { | 214 | } |
430 | 214 | updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) | 215 | } // Item ( delegate ) |
431 | 215 | } | 216 | } // Repeater |
432 | 216 | } | 217 | } // Row |
433 | 217 | onClicked: { | 218 | |
434 | 218 | var prevItem = d.currentItem; | 219 | MouseArea { |
435 | 220 | anchors.fill: parent | ||
436 | 221 | hoverEnabled: d.currentItem | ||
437 | 222 | |||
438 | 223 | onEntered: { | ||
439 | 224 | if (d.currentItem) { | ||
440 | 225 | updateCurrentItemFromPosition(Qt.point(mouseX, mouseY)) | ||
441 | 226 | } | ||
442 | 227 | } | ||
443 | 228 | onPositionChanged: { | ||
444 | 229 | if (d.currentItem) { | ||
445 | 219 | updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) | 230 | updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) |
477 | 220 | if (prevItem && d.currentItem == prevItem) { | 231 | } |
478 | 221 | prevItem.hide(); | 232 | } |
479 | 222 | } | 233 | onClicked: { |
480 | 223 | } | 234 | var prevItem = d.currentItem; |
481 | 224 | 235 | updateCurrentItemFromPosition(Qt.point(mouse.x, mouse.y)) | |
482 | 225 | function updateCurrentItemFromPosition(point) { | 236 | if (prevItem && d.currentItem == prevItem) { |
483 | 226 | var pos = mapToItem(row, point.x, point.y); | 237 | prevItem.hide(); |
484 | 227 | 238 | } | |
485 | 228 | if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) { | 239 | } |
486 | 229 | d.hoveredItem = row.childAt(pos.x, pos.y); | 240 | |
487 | 230 | if (!d.hoveredItem || !d.hoveredItem.enabled) | 241 | function updateCurrentItemFromPosition(point) { |
488 | 231 | return false; | 242 | var pos = mapToItem(row, point.x, point.y); |
489 | 232 | if (d.currentItem != d.hoveredItem) { | 243 | |
490 | 233 | d.currentItem = d.hoveredItem; | 244 | if (!d.hoveredItem || !d.currentItem || !d.hoveredItem.contains(Qt.point(pos.x - d.currentItem.x, pos.y - d.currentItem.y))) { |
491 | 234 | } | 245 | d.hoveredItem = row.childAt(pos.x, pos.y); |
492 | 235 | } | 246 | if (!d.hoveredItem || !d.hoveredItem.enabled) |
493 | 236 | return true; | 247 | return; |
494 | 237 | } | 248 | if (d.currentItem != d.hoveredItem) { |
495 | 238 | } | 249 | d.currentItem = d.hoveredItem; |
496 | 239 | 250 | } | |
497 | 240 | Rectangle { | 251 | } |
498 | 241 | id: underline | 252 | } |
499 | 242 | anchors { | 253 | } |
500 | 243 | bottom: row.bottom | 254 | |
501 | 244 | } | 255 | MouseArea { |
502 | 245 | x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0 | 256 | id: overflowButton |
503 | 246 | width: d.currentItem ? d.currentItem.width + units.gu(2) : 0 | 257 | objectName: "overflow" |
504 | 247 | height: units.dp(4) | 258 | |
505 | 248 | color: UbuntuColors.orange | 259 | hoverEnabled: d.currentItem |
506 | 249 | visible: d.currentItem | 260 | onEntered: d.currentItem = this |
507 | 250 | } | 261 | onPositionChanged: d.currentItem = this |
508 | 262 | onClicked: d.currentItem = this | ||
509 | 263 | |||
510 | 264 | property Item __popup: null; | ||
511 | 265 | readonly property bool popupVisible: __popup && __popup.visible | ||
512 | 266 | readonly property Item firstInvisibleItem: d.firstInvisibleIndex !== undefined ? rowRepeater.itemAt(d.firstInvisibleIndex) : null | ||
513 | 267 | |||
514 | 268 | visible: d.firstInvisibleIndex != undefined | ||
515 | 269 | x: firstInvisibleItem ? firstInvisibleItem.x : 0 | ||
516 | 270 | |||
517 | 271 | height: parent.height | ||
518 | 272 | width: units.gu(4) | ||
519 | 273 | |||
520 | 274 | onVisibleChanged: { | ||
521 | 275 | if (!visible && __popup) dismiss(); | ||
522 | 276 | } | ||
523 | 277 | |||
524 | 278 | Icon { | ||
525 | 279 | id: icon | ||
526 | 280 | width: units.gu(2) | ||
527 | 281 | height: units.gu(2) | ||
528 | 282 | anchors.centerIn: parent | ||
529 | 283 | color: theme.palette.normal.overlayText | ||
530 | 284 | name: "toolkit_chevron-down_2gu" | ||
531 | 285 | } | ||
532 | 286 | |||
533 | 287 | function show() { | ||
534 | 288 | if (!__popup) { | ||
535 | 289 | __popup = overflowComponent.createObject(root, { objectName: overflowButton.objectName + "-menu" }); | ||
536 | 290 | __popup.childActivated.connect(dismiss); | ||
537 | 291 | // force the current item to be the newly popped up menu | ||
538 | 292 | } else { | ||
539 | 293 | __popup.show(); | ||
540 | 294 | } | ||
541 | 295 | d.currentItem = overflowButton; | ||
542 | 296 | } | ||
543 | 297 | function hide() { | ||
544 | 298 | if (__popup) { | ||
545 | 299 | __popup.hide(); | ||
546 | 300 | |||
547 | 301 | if (d.currentItem === overflowButton) { | ||
548 | 302 | d.currentItem = null; | ||
549 | 303 | } | ||
550 | 304 | } | ||
551 | 305 | } | ||
552 | 306 | function dismiss() { | ||
553 | 307 | if (__popup) { | ||
554 | 308 | __popup.destroy(); | ||
555 | 309 | __popup = null; | ||
556 | 310 | |||
557 | 311 | if (d.currentItem === overflowButton) { | ||
558 | 312 | d.currentItem = null; | ||
559 | 313 | } | ||
560 | 314 | } | ||
561 | 315 | } | ||
562 | 316 | |||
563 | 317 | Connections { | ||
564 | 318 | target: d | ||
565 | 319 | onDismissAll: overflowButton.dismiss() | ||
566 | 320 | } | ||
567 | 321 | |||
568 | 322 | Component { | ||
569 | 323 | id: overflowComponent | ||
570 | 324 | MenuPopup { | ||
571 | 325 | id: overflowPopup | ||
572 | 326 | desiredX: overflowButton.x - units.gu(1) | ||
573 | 327 | desiredY: parent.height | ||
574 | 328 | unityMenuModel: overflowModel | ||
575 | 329 | |||
576 | 330 | ExpressionFilterModel { | ||
577 | 331 | id: overflowModel | ||
578 | 332 | sourceModel: root.unityMenuModel | ||
579 | 333 | matchExpression: function(index) { | ||
580 | 334 | if (d.firstInvisibleIndex === undefined) return false; | ||
581 | 335 | return index >= d.firstInvisibleIndex; | ||
582 | 336 | } | ||
583 | 337 | |||
584 | 338 | function submenu(index) { | ||
585 | 339 | return sourceModel.submenu(mapRowToSource(index)); | ||
586 | 340 | } | ||
587 | 341 | function activate(index) { | ||
588 | 342 | return sourceModel.activate(mapRowToSource(index)); | ||
589 | 343 | } | ||
590 | 344 | } | ||
591 | 345 | |||
592 | 346 | Connections { | ||
593 | 347 | target: d | ||
594 | 348 | onFirstInvisibleIndexChanged: overflowModel.invalidate() | ||
595 | 349 | } | ||
596 | 350 | } | ||
597 | 351 | } | ||
598 | 352 | } | ||
599 | 353 | |||
600 | 354 | Rectangle { | ||
601 | 355 | id: underline | ||
602 | 356 | anchors { | ||
603 | 357 | bottom: row.bottom | ||
604 | 358 | } | ||
605 | 359 | x: d.currentItem ? row.x + d.currentItem.x - units.gu(1) : 0 | ||
606 | 360 | width: d.currentItem ? d.currentItem.width + units.gu(2) : 0 | ||
607 | 361 | height: units.dp(4) | ||
608 | 362 | color: UbuntuColors.orange | ||
609 | 363 | visible: d.currentItem | ||
610 | 251 | } | 364 | } |
611 | 252 | 365 | ||
612 | 253 | MenuNavigator { | 366 | MenuNavigator { |
613 | 254 | id: d | 367 | id: d |
614 | 255 | objectName: "d" | 368 | objectName: "d" |
615 | 256 | itemView: rowRepeater | 369 | itemView: rowRepeater |
616 | 370 | hasOverflow: overflowButton.visible | ||
617 | 257 | 371 | ||
618 | 258 | property Item currentItem: null | 372 | property Item currentItem: null |
619 | 259 | property Item hoveredItem: null | 373 | property Item hoveredItem: null |
620 | 260 | property Item prevCurrentItem: null | 374 | property Item prevCurrentItem: null |
621 | 261 | |||
622 | 262 | readonly property int currentIndex: currentItem ? currentItem.__ownIndex : -1 | ||
623 | 263 | |||
624 | 264 | property bool altPressed: false | 375 | property bool altPressed: false |
625 | 265 | property bool longAltPressed: false | 376 | property bool longAltPressed: false |
626 | 377 | property var firstInvisibleIndex: undefined | ||
627 | 378 | |||
628 | 379 | readonly property int currentIndex: currentItem && currentItem.hasOwnProperty("__ownIndex") ? currentItem.__ownIndex : -1 | ||
629 | 266 | 380 | ||
630 | 267 | signal dismissAll() | 381 | signal dismissAll() |
631 | 268 | 382 | ||
632 | 383 | function recalcFirstInvisibleIndexAdded(index, item) { | ||
633 | 384 | if (firstInvisibleIndex === undefined) { | ||
634 | 385 | if (!item.shouldDisplay) { | ||
635 | 386 | firstInvisibleIndex = index; | ||
636 | 387 | } | ||
637 | 388 | } else if (index <= firstInvisibleIndex) { | ||
638 | 389 | if (!item.shouldDisplay) { | ||
639 | 390 | firstInvisibleIndex = index; | ||
640 | 391 | } else { | ||
641 | 392 | firstInvisibleIndex++; | ||
642 | 393 | } | ||
643 | 394 | } | ||
644 | 395 | } | ||
645 | 396 | |||
646 | 397 | function recalcFirstInvisibleIndex() { | ||
647 | 398 | for (var i = 0; i < rowRepeater.count; i++) { | ||
648 | 399 | if (!rowRepeater.itemAt(i).shouldDisplay) { | ||
649 | 400 | firstInvisibleIndex = i; | ||
650 | 401 | return; | ||
651 | 402 | } | ||
652 | 403 | } | ||
653 | 404 | firstInvisibleIndex = undefined; | ||
654 | 405 | } | ||
655 | 406 | |||
656 | 269 | onSelect: { | 407 | onSelect: { |
657 | 270 | var delegate = rowRepeater.itemAt(index); | 408 | var delegate = rowRepeater.itemAt(index); |
658 | 271 | if (delegate) { | 409 | if (delegate) { |
659 | @@ -273,6 +411,10 @@ | |||
660 | 273 | } | 411 | } |
661 | 274 | } | 412 | } |
662 | 275 | 413 | ||
663 | 414 | onOverflow: { | ||
664 | 415 | d.currentItem = overflowButton; | ||
665 | 416 | } | ||
666 | 417 | |||
667 | 276 | onCurrentItemChanged: { | 418 | onCurrentItemChanged: { |
668 | 277 | if (prevCurrentItem && prevCurrentItem != currentItem) { | 419 | if (prevCurrentItem && prevCurrentItem != currentItem) { |
669 | 278 | if (currentItem) { | 420 | if (currentItem) { |
670 | 279 | 421 | ||
671 | === modified file 'qml/ApplicationMenus/MenuItem.qml' | |||
672 | --- qml/ApplicationMenus/MenuItem.qml 2017-02-07 16:54:54 +0000 | |||
673 | +++ qml/ApplicationMenus/MenuItem.qml 2017-02-07 16:54:54 +0000 | |||
674 | @@ -46,6 +46,8 @@ | |||
675 | 46 | enabled: menuData ? menuData.sensitive : false | 46 | enabled: menuData ? menuData.sensitive : false |
676 | 47 | 47 | ||
677 | 48 | action: Action { | 48 | action: Action { |
678 | 49 | enabled: root.enabled | ||
679 | 50 | |||
680 | 49 | // FIXME - SDK Action:text modifies menu text with html underline for mnemonic | 51 | // FIXME - SDK Action:text modifies menu text with html underline for mnemonic |
681 | 50 | text: menuData.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") | 52 | text: menuData.label.replace("_", "&").replace("<u>", "&").replace("</u>", "") |
682 | 51 | checkable: menuData.isCheck || menuData.isRadio | 53 | checkable: menuData.isCheck || menuData.isRadio |
683 | @@ -137,7 +139,7 @@ | |||
684 | 137 | theme.palette.disabled.backgroundSecondaryText | 139 | theme.palette.disabled.backgroundSecondaryText |
685 | 138 | 140 | ||
686 | 139 | visible: root.hasSubmenu | 141 | visible: root.hasSubmenu |
688 | 140 | name: "chevron" | 142 | name: "toolkit_chevron-ltr_2gu" |
689 | 141 | } | 143 | } |
690 | 142 | } | 144 | } |
691 | 143 | } | 145 | } |
692 | 144 | 146 | ||
693 | === modified file 'qml/ApplicationMenus/MenuNavigator.qml' | |||
694 | --- qml/ApplicationMenus/MenuNavigator.qml 2016-11-28 13:44:30 +0000 | |||
695 | +++ qml/ApplicationMenus/MenuNavigator.qml 2017-02-07 16:54:54 +0000 | |||
696 | @@ -18,8 +18,10 @@ | |||
697 | 18 | 18 | ||
698 | 19 | QtObject { | 19 | QtObject { |
699 | 20 | property Item itemView: null | 20 | property Item itemView: null |
700 | 21 | property bool hasOverflow: false | ||
701 | 21 | 22 | ||
702 | 22 | signal select(int index) | 23 | signal select(int index) |
703 | 24 | signal overflow() | ||
704 | 23 | 25 | ||
705 | 24 | function selectNext(currentIndex) { | 26 | function selectNext(currentIndex) { |
706 | 25 | var menu; | 27 | var menu; |
707 | @@ -32,6 +34,11 @@ | |||
708 | 32 | break; | 34 | break; |
709 | 33 | } | 35 | } |
710 | 34 | newIndex++; | 36 | newIndex++; |
711 | 37 | |||
712 | 38 | if (hasOverflow && newIndex === itemView.count) { | ||
713 | 39 | overflow() | ||
714 | 40 | break; | ||
715 | 41 | } | ||
716 | 35 | } | 42 | } |
717 | 36 | } else if (currentIndex !== -1 && itemView.count > 1) { | 43 | } else if (currentIndex !== -1 && itemView.count > 1) { |
718 | 37 | var startIndex = (currentIndex + 1) % itemView.count; | 44 | var startIndex = (currentIndex + 1) % itemView.count; |
719 | @@ -42,6 +49,12 @@ | |||
720 | 42 | select(newIndex); | 49 | select(newIndex); |
721 | 43 | break; | 50 | break; |
722 | 44 | } | 51 | } |
723 | 52 | |||
724 | 53 | if (hasOverflow && newIndex + 1 === itemView.count) { | ||
725 | 54 | overflow() | ||
726 | 55 | break; | ||
727 | 56 | } | ||
728 | 57 | |||
729 | 45 | newIndex = (newIndex + 1) % itemView.count; | 58 | newIndex = (newIndex + 1) % itemView.count; |
730 | 46 | } while (newIndex !== startIndex) | 59 | } while (newIndex !== startIndex) |
731 | 47 | } | 60 | } |
732 | @@ -58,12 +71,21 @@ | |||
733 | 58 | break; | 71 | break; |
734 | 59 | } | 72 | } |
735 | 60 | newIndex--; | 73 | newIndex--; |
736 | 74 | |||
737 | 75 | if (hasOverflow && newIndex < 0 ) { | ||
738 | 76 | overflow(); | ||
739 | 77 | break; | ||
740 | 78 | } | ||
741 | 61 | } | 79 | } |
742 | 62 | } else if (currentIndex !== -1 && itemView.count > 1) { | 80 | } else if (currentIndex !== -1 && itemView.count > 1) { |
743 | 63 | var startIndex = currentIndex - 1; | 81 | var startIndex = currentIndex - 1; |
744 | 64 | newIndex = startIndex; | 82 | newIndex = startIndex; |
745 | 65 | do { | 83 | do { |
746 | 66 | if (newIndex < 0) { | 84 | if (newIndex < 0) { |
747 | 85 | if (hasOverflow) { | ||
748 | 86 | overflow(); | ||
749 | 87 | break; | ||
750 | 88 | } | ||
751 | 67 | newIndex = itemView.count - 1; | 89 | newIndex = itemView.count - 1; |
752 | 68 | } | 90 | } |
753 | 69 | menu = itemView.itemAt(newIndex); | 91 | menu = itemView.itemAt(newIndex); |
754 | @@ -72,6 +94,7 @@ | |||
755 | 72 | break; | 94 | break; |
756 | 73 | } | 95 | } |
757 | 74 | newIndex--; | 96 | newIndex--; |
758 | 97 | |||
759 | 75 | } while (newIndex !== startIndex) | 98 | } while (newIndex !== startIndex) |
760 | 76 | } | 99 | } |
761 | 77 | } | 100 | } |
762 | 78 | 101 | ||
763 | === modified file 'qml/ApplicationMenus/MenuPopup.qml' | |||
764 | --- qml/ApplicationMenus/MenuPopup.qml 2017-02-07 16:54:54 +0000 | |||
765 | +++ qml/ApplicationMenus/MenuPopup.qml 2017-02-07 16:54:54 +0000 | |||
766 | @@ -19,6 +19,7 @@ | |||
767 | 19 | import Ubuntu.Components 1.3 | 19 | import Ubuntu.Components 1.3 |
768 | 20 | import Ubuntu.Components.ListItems 1.3 as ListItems | 20 | import Ubuntu.Components.ListItems 1.3 as ListItems |
769 | 21 | import "../Components" | 21 | import "../Components" |
770 | 22 | import "../Components/PanelState" | ||
771 | 22 | import "." | 23 | import "." |
772 | 23 | 24 | ||
773 | 24 | UbuntuShape { | 25 | UbuntuShape { |
774 | @@ -100,7 +101,7 @@ | |||
775 | 100 | property real __minimumWidth: units.gu(20) | 101 | property real __minimumWidth: units.gu(20) |
776 | 101 | property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7 | 102 | property real __maximumWidth: ApplicationMenusLimits.screenWidth * 0.7 |
777 | 102 | property real __minimumHeight: units.gu(2) | 103 | property real __minimumHeight: units.gu(2) |
779 | 103 | property real __maximumHeight: ApplicationMenusLimits.screenHeight * 0.7 | 104 | property real __maximumHeight: ApplicationMenusLimits.screenHeight - PanelState.panelHeight |
780 | 104 | 105 | ||
781 | 105 | signal dismissAll() | 106 | signal dismissAll() |
782 | 106 | 107 | ||
783 | @@ -183,8 +184,19 @@ | |||
784 | 183 | } | 184 | } |
785 | 184 | 185 | ||
786 | 185 | MouseArea { | 186 | MouseArea { |
787 | 187 | id: previousMA | ||
788 | 186 | anchors.fill: parent | 188 | anchors.fill: parent |
790 | 187 | onPressed: { | 189 | hoverEnabled: enabled |
791 | 190 | onPressed: progress() | ||
792 | 191 | |||
793 | 192 | Timer { | ||
794 | 193 | running: previousMA.containsMouse && !listView.atYBeginning | ||
795 | 194 | interval: 1000 | ||
796 | 195 | repeat: true | ||
797 | 196 | onTriggered: previousMA.progress() | ||
798 | 197 | } | ||
799 | 198 | |||
800 | 199 | function progress() { | ||
801 | 188 | var item = menuColumn.childAt(0, listView.contentY); | 200 | var item = menuColumn.childAt(0, listView.contentY); |
802 | 189 | if (item) { | 201 | if (item) { |
803 | 190 | var previousItem = item; | 202 | var previousItem = item; |
804 | @@ -412,8 +424,19 @@ | |||
805 | 412 | } | 424 | } |
806 | 413 | 425 | ||
807 | 414 | MouseArea { | 426 | MouseArea { |
808 | 427 | id: nextMA | ||
809 | 415 | anchors.fill: parent | 428 | anchors.fill: parent |
811 | 416 | onPressed: { | 429 | hoverEnabled: enabled |
812 | 430 | onPressed: progress() | ||
813 | 431 | |||
814 | 432 | Timer { | ||
815 | 433 | running: nextMA.containsMouse && !listView.atYEnd | ||
816 | 434 | interval: 1000 | ||
817 | 435 | repeat: true | ||
818 | 436 | onTriggered: nextMA.progress() | ||
819 | 437 | } | ||
820 | 438 | |||
821 | 439 | function progress() { | ||
822 | 417 | var item = menuColumn.childAt(0, listView.contentY + listView.height); | 440 | var item = menuColumn.childAt(0, listView.contentY + listView.height); |
823 | 418 | if (item) { | 441 | if (item) { |
824 | 419 | var nextItem = item; | 442 | var nextItem = item; |
825 | 420 | 443 | ||
826 | === modified file 'qml/Panel/Panel.qml' | |||
827 | --- qml/Panel/Panel.qml 2017-01-09 14:10:17 +0000 | |||
828 | +++ qml/Panel/Panel.qml 2017-02-07 16:54:54 +0000 | |||
829 | @@ -209,6 +209,8 @@ | |||
830 | 209 | Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } | 209 | Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
831 | 210 | active: __applicationMenus.model | 210 | active: __applicationMenus.model |
832 | 211 | 211 | ||
833 | 212 | width: parent.width - windowControlButtons.width - units.gu(2) - __indicators.barWidth | ||
834 | 213 | |||
835 | 212 | property bool menusRequested: menuBarLoader.item ? menuBarLoader.item.showRequested : false | 214 | property bool menusRequested: menuBarLoader.item ? menuBarLoader.item.showRequested : false |
836 | 213 | 215 | ||
837 | 214 | sourceComponent: MenuBar { | 216 | sourceComponent: MenuBar { |
838 | @@ -385,7 +387,7 @@ | |||
839 | 385 | } | 387 | } |
840 | 386 | 388 | ||
841 | 387 | enabled: !applicationMenus.expanded | 389 | enabled: !applicationMenus.expanded |
843 | 388 | opacity: !applicationMenus.expanded ? 1 : 0 | 390 | opacity: !callHint.visible && !applicationMenus.expanded ? 1 : 0 |
844 | 389 | Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } | 391 | Behavior on opacity { UbuntuNumberAnimation { duration: UbuntuAnimation.SnapDuration } } |
845 | 390 | 392 | ||
846 | 391 | onEnabledChanged: { | 393 | onEnabledChanged: { |
847 | 392 | 394 | ||
848 | === modified file 'tests/mocks/Utils/CMakeLists.txt' | |||
849 | --- tests/mocks/Utils/CMakeLists.txt 2016-12-21 10:20:36 +0000 | |||
850 | +++ tests/mocks/Utils/CMakeLists.txt 2017-02-07 16:54:54 +0000 | |||
851 | @@ -26,6 +26,7 @@ | |||
852 | 26 | ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp | 26 | ${CMAKE_SOURCE_DIR}/plugins/Utils/globalfunctions.cpp |
853 | 27 | ${CMAKE_SOURCE_DIR}/plugins/Utils/appdrawerproxymodel.cpp | 27 | ${CMAKE_SOURCE_DIR}/plugins/Utils/appdrawerproxymodel.cpp |
854 | 28 | ${CMAKE_SOURCE_DIR}/plugins/Utils/tabfocusfence.cpp | 28 | ${CMAKE_SOURCE_DIR}/plugins/Utils/tabfocusfence.cpp |
855 | 29 | ${CMAKE_SOURCE_DIR}/plugins/Utils/expressionfiltermodel.cpp | ||
856 | 29 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h | 30 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationManagerInterface.h |
857 | 30 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h | 31 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/ApplicationInfoInterface.h |
858 | 31 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h | 32 | ${APPLICATION_API_INCLUDEDIR}/unity/shell/application/MirSurfaceInterface.h |
859 | 32 | 33 | ||
860 | === modified file 'tests/mocks/Utils/plugin.cpp' | |||
861 | --- tests/mocks/Utils/plugin.cpp 2017-01-03 12:16:00 +0000 | |||
862 | +++ tests/mocks/Utils/plugin.cpp 2017-02-07 16:54:54 +0000 | |||
863 | @@ -42,6 +42,7 @@ | |||
864 | 42 | #include <globalfunctions.h> | 42 | #include <globalfunctions.h> |
865 | 43 | #include <appdrawerproxymodel.h> | 43 | #include <appdrawerproxymodel.h> |
866 | 44 | #include <tabfocusfence.h> | 44 | #include <tabfocusfence.h> |
867 | 45 | #include <expressionfiltermodel.h> | ||
868 | 45 | 46 | ||
869 | 46 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) | 47 | static QObject *createWindowStateStorage(QQmlEngine *engine, QJSEngine *scriptEngine) |
870 | 47 | { | 48 | { |
871 | @@ -86,4 +87,5 @@ | |||
872 | 86 | qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher"); | 87 | qmlRegisterType<URLDispatcher>(uri, 0, 1, "URLDispatcher"); |
873 | 87 | qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel"); | 88 | qmlRegisterType<AppDrawerProxyModel>(uri, 0, 1, "AppDrawerProxyModel"); |
874 | 88 | qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence"); | 89 | qmlRegisterType<TabFocusFenceItem>(uri, 0, 1, "TabFocusFence"); |
875 | 90 | qmlRegisterType<ExpressionFilterModel>(uri, 0, 1, "ExpressionFilterModel"); | ||
876 | 89 | } | 91 | } |
877 | 90 | 92 | ||
878 | === modified file 'tests/qmltests/ApplicationMenus/tst_MenuBar.qml' | |||
879 | --- tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-02-07 16:54:54 +0000 | |||
880 | +++ tests/qmltests/ApplicationMenus/tst_MenuBar.qml 2017-02-07 16:54:54 +0000 | |||
881 | @@ -28,8 +28,8 @@ | |||
882 | 28 | 28 | ||
883 | 29 | Item { | 29 | Item { |
884 | 30 | id: root | 30 | id: root |
887 | 31 | width: units.gu(100) | 31 | width: units.gu(120) |
888 | 32 | height: units.gu(50) | 32 | height: units.gu(70) |
889 | 33 | 33 | ||
890 | 34 | Component.onCompleted: { | 34 | Component.onCompleted: { |
891 | 35 | QuickUtils.keyboardAttached = true; | 35 | QuickUtils.keyboardAttached = true; |
892 | @@ -51,11 +51,11 @@ | |||
893 | 51 | Rectangle { | 51 | Rectangle { |
894 | 52 | anchors { | 52 | anchors { |
895 | 53 | left: parent.left | 53 | left: parent.left |
896 | 54 | right: parent.right | ||
897 | 55 | top: parent.top | 54 | top: parent.top |
898 | 56 | margins: units.gu(1) | 55 | margins: units.gu(1) |
899 | 57 | } | 56 | } |
900 | 58 | height: units.gu(3) | 57 | height: units.gu(3) |
901 | 58 | width: parent.width * 2/3 | ||
902 | 59 | color: "grey" | 59 | color: "grey" |
903 | 60 | 60 | ||
904 | 61 | MenuBar { | 61 | MenuBar { |
905 | @@ -65,7 +65,7 @@ | |||
906 | 65 | 65 | ||
907 | 66 | unityMenuModel: UnityMenuModel { | 66 | unityMenuModel: UnityMenuModel { |
908 | 67 | id: menuBackend | 67 | id: menuBackend |
910 | 68 | modelData: appMenuData.generateTestData(17,5,2,3) | 68 | modelData: appMenuData.generateTestData(10,5,2,3) |
911 | 69 | } | 69 | } |
912 | 70 | } | 70 | } |
913 | 71 | } | 71 | } |
914 | @@ -83,12 +83,12 @@ | |||
915 | 83 | 83 | ||
916 | 84 | function init() { | 84 | function init() { |
917 | 85 | menuBar.dismiss(); | 85 | menuBar.dismiss(); |
919 | 86 | menuBackend.modelData = appMenuData.generateTestData(5,5,2,3) | 86 | menuBackend.modelData = appMenuData.generateTestData(5,5,2,3, "menu") |
920 | 87 | activatedSpy.clear(); | 87 | activatedSpy.clear(); |
921 | 88 | } | 88 | } |
922 | 89 | 89 | ||
923 | 90 | function test_mouseNavigation() { | 90 | function test_mouseNavigation() { |
925 | 91 | menuBackend.modelData = appMenuData.generateTestData(3,3,0,0); | 91 | menuBackend.modelData = appMenuData.generateTestData(3,3,0,0, "menu"); |
926 | 92 | wait(50) // wait for row to build | 92 | wait(50) // wait for row to build |
927 | 93 | var priv = findInvisibleChild(menuBar, "d"); | 93 | var priv = findInvisibleChild(menuBar, "d"); |
928 | 94 | 94 | ||
929 | @@ -114,7 +114,7 @@ | |||
930 | 114 | } | 114 | } |
931 | 115 | 115 | ||
932 | 116 | function test_keyboardNavigation_RightKeySelectsNextMenuItem(data) { | 116 | function test_keyboardNavigation_RightKeySelectsNextMenuItem(data) { |
934 | 117 | menuBackend.modelData = appMenuData.generateTestData(3,3,0,0); | 117 | menuBackend.modelData = appMenuData.generateTestData(3,3,0,0, "menu"); |
935 | 118 | var priv = findInvisibleChild(menuBar, "d"); | 118 | var priv = findInvisibleChild(menuBar, "d"); |
936 | 119 | 119 | ||
937 | 120 | var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); | 120 | var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); |
938 | @@ -139,7 +139,7 @@ | |||
939 | 139 | } | 139 | } |
940 | 140 | 140 | ||
941 | 141 | function test_keyboardNavigation_LeftKeySelectsPreviousMenuItem(data) { | 141 | function test_keyboardNavigation_LeftKeySelectsPreviousMenuItem(data) { |
943 | 142 | menuBackend.modelData = appMenuData.generateTestData(3,3,0,0); | 142 | menuBackend.modelData = appMenuData.generateTestData(3,3,0,0, "menu"); |
944 | 143 | var priv = findInvisibleChild(menuBar, "d"); | 143 | var priv = findInvisibleChild(menuBar, "d"); |
945 | 144 | 144 | ||
946 | 145 | var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); | 145 | var menuItem0 = findChild(menuBar, "menuBar-item0"); verify(menuItem0); |
947 | @@ -242,5 +242,31 @@ | |||
948 | 242 | mouseClick(menuItem); | 242 | mouseClick(menuItem); |
949 | 243 | compare(priv.currentItem, null, "CurrentItem should be null"); | 243 | compare(priv.currentItem, null, "CurrentItem should be null"); |
950 | 244 | } | 244 | } |
951 | 245 | |||
952 | 246 | function test_overfow() { | ||
953 | 247 | menuBackend.modelData = appMenuData.generateTestData(5,2,0,0,"menu"); | ||
954 | 248 | |||
955 | 249 | var overflow = findChild(menuBar, "overflow"); | ||
956 | 250 | compare(overflow.visible, false, "Overflow should not be visible"); | ||
957 | 251 | |||
958 | 252 | var menu = { "rowData": { "label": "Short" } }; | ||
959 | 253 | tryCompareFunction(function() { | ||
960 | 254 | menuBackend.insertRow(0, menu); | ||
961 | 255 | wait(1); | ||
962 | 256 | if (overflow.visible) { | ||
963 | 257 | return true; | ||
964 | 258 | } | ||
965 | 259 | return false; | ||
966 | 260 | }, true); | ||
967 | 261 | |||
968 | 262 | tryCompareFunction(function() { | ||
969 | 263 | menuBackend.removeRow(0); | ||
970 | 264 | wait(1); | ||
971 | 265 | if (!overflow.visible) { | ||
972 | 266 | return true; | ||
973 | 267 | } | ||
974 | 268 | return false; | ||
975 | 269 | }, true); | ||
976 | 270 | } | ||
977 | 245 | } | 271 | } |
978 | 246 | } | 272 | } |
FAILED: Continuous integration, rev:2777 /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/3007/ /unity8- jenkins. ubuntu. com/job/ build/3912/ console /unity8- jenkins. ubuntu. com/job/ build-0- fetch/3940 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 3785/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= zesty/3785/ console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 3785/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= zesty/3785/ console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 3785/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= zesty/3785/ console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-unity8- ci/3007/ rebuild
https:/