Merge lp:~bzoltan/ubuntu-ui-toolkit/more_1618581 into lp:ubuntu-ui-toolkit
- more_1618581
- Merge into trunk
Proposed by
Zoltan Balogh
Status: | Superseded |
---|---|
Proposed branch: | lp:~bzoltan/ubuntu-ui-toolkit/more_1618581 |
Merge into: | lp:ubuntu-ui-toolkit |
Diff against target: |
7457 lines (+5769/-506) 68 files modified
app-launch-profiler/app-launch-tracepoints.c (+2/-2) components.api (+14/-3) debian/control (+1/-0) debian/control.gles (+1/-0) debian/libubuntugestures5-dev.install (+0/-1) debian/libubuntumetrics5-dev.install (+0/-1) debian/libubuntutoolkit5-dev.install (+0/-1) debian/rules (+1/-0) documentation/overview.qdoc (+16/-0) src/Ubuntu/Components/1.2/ActivityIndicator.qml (+5/-0) src/Ubuntu/Components/1.3/ActionList.qml (+0/-47) src/Ubuntu/Components/1.3/ActivityIndicator.qml (+5/-0) src/Ubuntu/Components/ComponentModule.pro (+0/-1) src/Ubuntu/Components/Popups/1.3/Popover.qml (+30/-25) src/Ubuntu/Components/Popups/1.3/popupUtils.js (+5/-0) src/Ubuntu/Components/Themes/Ambiance/1.2/ActivityIndicatorStyle.qml (+0/-2) src/Ubuntu/Components/Themes/Ambiance/1.3/ActivityIndicatorStyle.qml (+0/-2) src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollbarStyle.qml (+49/-28) src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml (+203/-0) src/Ubuntu/Components/Themes/Ambiance/Ambiance.pro (+1/-0) src/Ubuntu/Components/Themes/Ambiance/qmldir (+1/-0) src/Ubuntu/Components/qmldir (+0/-1) src/Ubuntu/Test/UbuntuTestCase13.qml (+1/-1) src/Ubuntu/UbuntuMetrics/applicationmonitor.cpp (+3/-1) src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro (+22/-4) src/Ubuntu/UbuntuToolkit/actionlist.cpp (+158/-0) src/Ubuntu/UbuntuToolkit/actionlist_p.h (+58/-0) src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp (+168/-0) src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h (+62/-0) src/Ubuntu/UbuntuToolkit/menu.cpp (+671/-0) src/Ubuntu/UbuntuToolkit/menu_p.h (+75/-0) src/Ubuntu/UbuntuToolkit/menu_p_p.h (+95/-0) src/Ubuntu/UbuntuToolkit/menubar.cpp (+340/-0) src/Ubuntu/UbuntuToolkit/menubar_p.h (+65/-0) src/Ubuntu/UbuntuToolkit/menubar_p_p.h (+78/-0) src/Ubuntu/UbuntuToolkit/menugroup.cpp (+164/-0) src/Ubuntu/UbuntuToolkit/menugroup_p.h (+60/-0) src/Ubuntu/UbuntuToolkit/privates/splitviewhandler.cpp (+137/-0) src/Ubuntu/UbuntuToolkit/privates/splitviewhandler_p_p.h (+52/-0) src/Ubuntu/UbuntuToolkit/quickutils.cpp (+4/-2) src/Ubuntu/UbuntuToolkit/quickutils_p.h (+1/-0) src/Ubuntu/UbuntuToolkit/splitview.cpp (+675/-0) src/Ubuntu/UbuntuToolkit/splitview_p.h (+160/-0) src/Ubuntu/UbuntuToolkit/splitview_p_p.h (+154/-0) src/Ubuntu/UbuntuToolkit/splitviewlayout.cpp (+252/-0) src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp (+18/-4) src/Ubuntu/UbuntuToolkit/ucaction.cpp (+186/-37) src/Ubuntu/UbuntuToolkit/ucaction_p.h (+33/-7) src/Ubuntu/UbuntuToolkit/uclistitem.cpp (+46/-22) src/Ubuntu/UbuntuToolkit/ucmathutils_p.h (+4/-4) tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_actionbar.py (+43/-11) tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_flickable.py (+14/-14) tests/autopilot/ubuntuuitoolkit/_custom_proxy_objects/_header.py (+2/-1) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/MyDialog.qml (+36/-0) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_actionbar.ActionBarTestCase.qml (+13/-1) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_actionbar.ScrollingActionBarTestCase.qml (+79/-0) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_actionbar.py (+18/-8) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_popups.WindowTestCase.qml (+36/-0) tests/autopilot/ubuntuuitoolkit/tests/custom_proxy_objects/test_popups.py (+19/-0) tests/unit/components/tst_action.qml (+107/-1) tests/unit/components/tst_activityindicator.qml (+7/-0) tests/unit/components/tst_menu.qml (+137/-0) tests/unit/visual/ScrollbarTestCase13.qml (+3/-0) tests/unit/visual/tst_actionbar.13.qml (+299/-148) tests/unit/visual/tst_scrollbar.13.qml (+238/-126) tests/unit/visual/tst_splitview.13.qml (+383/-0) tests/unit/visual/tst_splitview_page.13.qml (+143/-0) tests/unit/visual/tst_splitview_repeater.13.qml (+116/-0) |
To merge this branch: | bzr merge lp:~bzoltan/ubuntu-ui-toolkit/more_1618581 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu SDK team | Pending | ||
Review via email: mp+304454@code.launchpad.net |
This proposal has been superseded by a proposal from 2016-08-31.
Commit message
Fix the header tests. Fixes LP: #1618581
Description of the change
Fix the header tests. Fixes LP: #1618581
To post a comment you must log in.
Unmerged revisions
- 2089. By Zoltan Balogh
-
Fix the header tests. Fixes LP: #Bug #1618581
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'app-launch-profiler/app-launch-tracepoints.c' |
2 | --- app-launch-profiler/app-launch-tracepoints.c 2016-08-16 08:43:28 +0000 |
3 | +++ app-launch-profiler/app-launch-tracepoints.c 2016-08-31 08:57:26 +0000 |
4 | @@ -28,8 +28,8 @@ |
5 | |
6 | int main (int argc, char* argv[]) |
7 | { |
8 | - #pragma unused(argc) |
9 | - #pragma unused(argv) |
10 | + (void)argc; |
11 | + (void)argv; |
12 | tracepoint(app,invokeApplauncher); |
13 | return 0; |
14 | } |
15 | |
16 | === modified file 'components.api' |
17 | --- components.api 2016-08-09 03:48:53 +0000 |
18 | +++ components.api 2016-08-31 08:57:26 +0000 |
19 | @@ -10,13 +10,17 @@ |
20 | readonly property bool pressed |
21 | readonly property UCMargins sensingMargins |
22 | Ubuntu.Components.Action 1.3 1.0 0.1 UCAction: QtObject |
23 | + property bool checkable 1.3 |
24 | + property bool checked 1.3 |
25 | property string description |
26 | property bool enabled |
27 | + property ExclusiveGroup exclusiveGroup 1.3 |
28 | property string iconName |
29 | property url iconSource |
30 | property Component itemHint |
31 | property string keywords |
32 | signal triggered(var value) |
33 | + signal toggled(bool value) 1.3 |
34 | function trigger(var value) |
35 | function trigger() |
36 | property string name |
37 | @@ -59,9 +63,12 @@ |
38 | Ubuntu.Components.ActionList 1.0 0.1: QtObject |
39 | property list<Action> actions |
40 | default property list<Action> children |
41 | -Ubuntu.Components.ActionList 1.3: QtObject |
42 | - property list<Action> actions |
43 | - default property list<Action> children |
44 | +Ubuntu.Components.ActionList 1.3 ActionList: QtObject |
45 | + default property list<Action> actions |
46 | + signal added(Action action) |
47 | + signal removed(Action action) |
48 | + function addAction(Action action) |
49 | + function removeAction(Action action) |
50 | Ubuntu.Components.ActionManager 1.0 0.1 UCActionManager: QtObject |
51 | default property list<Action> actions |
52 | readonly property ActionContext globalContext |
53 | @@ -458,6 +465,10 @@ |
54 | OperationPending |
55 | Ubuntu.Metrics.Event: Enum |
56 | UserInterfaceReady |
57 | +Ubuntu.Components.ExclusiveGroup 1.3 ExclusiveGroup: ActionList |
58 | + readonly property QtObject current |
59 | + function bindCheckable(QtObject object) |
60 | + function unbindCheckable(QtObject object) |
61 | Ubuntu.Components.ListItems.Expandable 1.0 0.1: Empty |
62 | property bool collapseOnClick |
63 | property double collapsedHeight |
64 | |
65 | === modified file 'debian/control' |
66 | --- debian/control 2016-08-18 16:51:24 +0000 |
67 | +++ debian/control 2016-08-31 08:57:26 +0000 |
68 | @@ -77,6 +77,7 @@ |
69 | qml-module-qtquick-layouts, |
70 | qml-module-qtquick-window2, |
71 | qml-module-qtquick2, |
72 | + qml-module-ubuntu-components-labs, |
73 | qml-module-ubuntu-performancemetrics, |
74 | qtdeclarative5-unity-action-plugin (>= 1.1.0), |
75 | suru-icon-theme, |
76 | |
77 | === modified file 'debian/control.gles' |
78 | --- debian/control.gles 2016-07-28 10:54:13 +0000 |
79 | +++ debian/control.gles 2016-08-31 08:57:26 +0000 |
80 | @@ -103,6 +103,7 @@ |
81 | qml-module-qtquick-layouts, |
82 | qml-module-qtquick-window2, |
83 | qml-module-qtquick2, |
84 | + qml-module-ubuntu-components-labs, |
85 | qml-module-ubuntu-performancemetrics-gles, |
86 | qtdeclarative5-unity-action-plugin (>= 1.1.0), |
87 | suru-icon-theme, |
88 | |
89 | === modified file 'debian/libubuntugestures5-dev.install' |
90 | --- debian/libubuntugestures5-dev.install 2016-07-06 11:27:11 +0000 |
91 | +++ debian/libubuntugestures5-dev.install 2016-08-31 08:57:26 +0000 |
92 | @@ -5,7 +5,6 @@ |
93 | usr/include/*/qt5/UbuntuGestures/ubuntugesturesglobal.h |
94 | usr/include/*/qt5/UbuntuGestures/ubuntugesturesmodule.h |
95 | usr/include/*/qt5/UbuntuGestures/ubuntugesturesversion.h |
96 | -usr/lib/*/libUbuntuGestures.la |
97 | usr/lib/*/libUbuntuGestures.prl |
98 | usr/lib/*/libUbuntuGestures.so |
99 | usr/lib/*/pkgconfig/UbuntuGestures.pc |
100 | |
101 | === modified file 'debian/libubuntumetrics5-dev.install' |
102 | --- debian/libubuntumetrics5-dev.install 2016-07-26 16:33:27 +0000 |
103 | +++ debian/libubuntumetrics5-dev.install 2016-08-31 08:57:26 +0000 |
104 | @@ -6,7 +6,6 @@ |
105 | usr/include/*/qt5/UbuntuMetrics/logger.h |
106 | usr/include/*/qt5/UbuntuMetrics/ubuntumetricsglobal.h |
107 | usr/include/*/qt5/UbuntuMetrics/ubuntumetricsversion.h |
108 | -usr/lib/*/libUbuntuMetrics.la |
109 | usr/lib/*/libUbuntuMetrics.prl |
110 | usr/lib/*/libUbuntuMetrics.so |
111 | usr/lib/*/pkgconfig/UbuntuMetrics.pc |
112 | |
113 | === modified file 'debian/libubuntutoolkit5-dev.install' |
114 | --- debian/libubuntutoolkit5-dev.install 2016-07-07 07:21:48 +0000 |
115 | +++ debian/libubuntutoolkit5-dev.install 2016-08-31 08:57:26 +0000 |
116 | @@ -5,7 +5,6 @@ |
117 | usr/include/*/qt5/UbuntuToolkit/ubuntutoolkitglobal.h |
118 | usr/include/*/qt5/UbuntuToolkit/ubuntutoolkitmodule.h |
119 | usr/include/*/qt5/UbuntuToolkit/ubuntutoolkitversion.h |
120 | -usr/lib/*/libUbuntuToolkit.la |
121 | usr/lib/*/libUbuntuToolkit.prl |
122 | usr/lib/*/libUbuntuToolkit.so |
123 | usr/lib/*/pkgconfig/UbuntuToolkit.pc |
124 | |
125 | === modified file 'debian/rules' |
126 | --- debian/rules 2016-07-07 18:47:22 +0000 |
127 | +++ debian/rules 2016-08-31 08:57:26 +0000 |
128 | @@ -43,6 +43,7 @@ |
129 | mkdir -p debian/tmp/`qmake -query QT_INSTALL_DOCS`/qch |
130 | mkdir -p debian/tmp/usr/share/ubuntu-ui-toolkit/doc |
131 | cp -r $(CURDIR)/documentation/*qch debian/tmp/`qmake -query QT_INSTALL_DOCS`/qch |
132 | + rm -f debian/tmp/usr/lib/$(DEB_HOST_MULTIARCH)/*.la |
133 | # FIXME: Due to autopilot not being in the archive we ship docs for now |
134 | # sphinx-build -b singlehtml documentation/autopilot-helpers documentation/autopilot-helpers/_build/html |
135 | # sphinx-build -b json documentation/autopilot-helpers documentation/autopilot-helpers/_build/json |
136 | |
137 | === modified file 'documentation/overview.qdoc' |
138 | --- documentation/overview.qdoc 2016-07-27 15:40:52 +0000 |
139 | +++ documentation/overview.qdoc 2016-08-31 08:57:26 +0000 |
140 | @@ -129,6 +129,22 @@ |
141 | \endcode |
142 | \annotatedlist ubuntu-services |
143 | |
144 | + \part Performance Metrics |
145 | + Available through: |
146 | + \code |
147 | + import Ubuntu.PerformanceMetrics 1.0 |
148 | + \endcode |
149 | + \annotatedlist ubuntu-performance-metrics |
150 | + |
151 | + \part Labs |
152 | + The Labs module contains a set of components which have unstable API. Those |
153 | + should not be used in applications as their interface may change any time. |
154 | + Available through: |
155 | + \code |
156 | + import Ubuntu.Components.Labs 1.0 |
157 | + \endcode |
158 | + \annotatedlist ubuntu-labs |
159 | + |
160 | \part Test extensions |
161 | Available through: |
162 | \code |
163 | |
164 | === modified file 'src/Ubuntu/Components/1.2/ActivityIndicator.qml' |
165 | --- src/Ubuntu/Components/1.2/ActivityIndicator.qml 2016-08-17 14:42:42 +0000 |
166 | +++ src/Ubuntu/Components/1.2/ActivityIndicator.qml 2016-08-31 08:57:26 +0000 |
167 | @@ -52,5 +52,10 @@ |
168 | */ |
169 | property bool running: false |
170 | |
171 | + implicitWidth: units.gu(3) |
172 | + implicitHeight: units.gu(3) |
173 | + width: units.gu(3) |
174 | + height: units.gu(3) |
175 | + |
176 | style: Theme.createStyleComponent("ActivityIndicatorStyle.qml", indicator) |
177 | } |
178 | |
179 | === removed file 'src/Ubuntu/Components/1.3/ActionList.qml' |
180 | --- src/Ubuntu/Components/1.3/ActionList.qml 2016-05-25 12:48:10 +0000 |
181 | +++ src/Ubuntu/Components/1.3/ActionList.qml 1970-01-01 00:00:00 +0000 |
182 | @@ -1,47 +0,0 @@ |
183 | -/* |
184 | - * Copyright 2012 Canonical Ltd. |
185 | - * |
186 | - * This program is free software; you can redistribute it and/or modify |
187 | - * it under the terms of the GNU Lesser General Public License as published by |
188 | - * the Free Software Foundation; version 3. |
189 | - * |
190 | - * This program is distributed in the hope that it will be useful, |
191 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
192 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
193 | - * GNU Lesser General Public License for more details. |
194 | - * |
195 | - * You should have received a copy of the GNU Lesser General Public License |
196 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
197 | - */ |
198 | - |
199 | -import QtQuick 2.4 |
200 | -import Ubuntu.Components 1.3 |
201 | - |
202 | -/*! |
203 | - \qmltype ActionList |
204 | - \inqmlmodule Ubuntu.Components |
205 | - \ingroup ubuntu |
206 | - \brief List of \l Action items |
207 | -*/ |
208 | - |
209 | -QtObject { |
210 | - id: list |
211 | - // internal objects using nested elements, |
212 | - // which isn't allowed by QtObject; this fix makes this possible |
213 | - /*! |
214 | - Default property to allow adding of children. |
215 | - \qmlproperty list<Action> children |
216 | - \default |
217 | - */ |
218 | - default property alias children: list.actions |
219 | - |
220 | - /*! |
221 | - List of already defined actions when not defining them as children of the ActionList. |
222 | - Note that when you set this property, the children of the ActionList will be ignored, |
223 | - so do not set the list and define children. |
224 | - |
225 | - The advantage of setting actions over using the children is that the same |
226 | - \l Action items can be used in several sets of actions. |
227 | - */ |
228 | - property list<Action> actions |
229 | -} |
230 | |
231 | === modified file 'src/Ubuntu/Components/1.3/ActivityIndicator.qml' |
232 | --- src/Ubuntu/Components/1.3/ActivityIndicator.qml 2016-08-17 14:42:42 +0000 |
233 | +++ src/Ubuntu/Components/1.3/ActivityIndicator.qml 2016-08-31 08:57:26 +0000 |
234 | @@ -52,5 +52,10 @@ |
235 | */ |
236 | property bool running: false |
237 | |
238 | + implicitWidth: units.gu(3) |
239 | + implicitHeight: units.gu(3) |
240 | + width: units.gu(3) |
241 | + height: units.gu(3) |
242 | + |
243 | styleName: "ActivityIndicatorStyle" |
244 | } |
245 | |
246 | === modified file 'src/Ubuntu/Components/ComponentModule.pro' |
247 | --- src/Ubuntu/Components/ComponentModule.pro 2016-07-29 13:21:05 +0000 |
248 | +++ src/Ubuntu/Components/ComponentModule.pro 2016-08-31 08:57:26 +0000 |
249 | @@ -78,7 +78,6 @@ |
250 | |
251 | #1.3 |
252 | QML_FILES += 1.3/ActionBar.qml \ |
253 | - 1.3/ActionList.qml \ |
254 | 1.3/ActivityIndicator.qml \ |
255 | 1.3/AdaptivePageLayout.qml \ |
256 | 1.3/AnimatedItem.qml \ |
257 | |
258 | === modified file 'src/Ubuntu/Components/Popups/1.3/Popover.qml' |
259 | --- src/Ubuntu/Components/Popups/1.3/Popover.qml 2016-03-17 17:40:51 +0000 |
260 | +++ src/Ubuntu/Components/Popups/1.3/Popover.qml 2016-08-31 08:57:26 +0000 |
261 | @@ -33,9 +33,8 @@ |
262 | \qml |
263 | import QtQuick 2.4 |
264 | import Ubuntu.Components 1.3 |
265 | - import Ubuntu.Components.ListItems 1.3 as ListItem |
266 | import Ubuntu.Components.Popups 1.3 |
267 | - |
268 | + import Ubuntu.Components.ListItems 1.3 as Old_ListItem |
269 | Rectangle { |
270 | color: theme.palette.normal.background |
271 | width: units.gu(80) |
272 | @@ -52,29 +51,35 @@ |
273 | top: parent.top |
274 | right: parent.right |
275 | } |
276 | - ListItem.Header { text: "Standard list items" } |
277 | - ListItem.Standard { text: "Do something" } |
278 | - ListItem.Standard { text: "Do something else" } |
279 | - ListItem.Header { text: "Buttons" } |
280 | - ListItem.SingleControl { |
281 | - highlightWhenPressed: false |
282 | - control: Button { |
283 | - text: "Do nothing" |
284 | - anchors { |
285 | - fill: parent |
286 | - margins: units.gu(1) |
287 | - } |
288 | - } |
289 | - } |
290 | - ListItem.SingleControl { |
291 | - highlightWhenPressed: false |
292 | - control: Button { |
293 | - text: "Close" |
294 | - anchors { |
295 | - fill: parent |
296 | - margins: units.gu(1) |
297 | - } |
298 | - onClicked: PopupUtils.close(popover) |
299 | + |
300 | + // there is no equivalent yet to ListItem.Header |
301 | + Old_ListItem.Header { text: "Standard list items" } |
302 | + |
303 | + ListItem { |
304 | + // shall specify the height when Using ListItemLayout inside ListItem |
305 | + height: somethingLayout.height + (divider.visible ? divider.height : 0) |
306 | + ListItemLayout { |
307 | + id: somethingLayout |
308 | + title.text: "Do somethings" |
309 | + } |
310 | + onClicked: console.log("clicked on ListItem with onClicked implemented") |
311 | + } |
312 | + ListItem { |
313 | + // shall specify the height when Using ListItemLayout inside ListItem |
314 | + height: somethingElseLayout.height + (divider.visible ? divider.height : 0) |
315 | + ListItemLayout { |
316 | + id: somethingElseLayout |
317 | + title.text: "Do somethings" |
318 | + subtitle.text: "else" |
319 | + } |
320 | + } |
321 | + ListItem { |
322 | + // shall specify the height when Using ListItemLayout inside ListItem |
323 | + height: closeBtn.height + (divider.visible ? divider.height : 0) |
324 | + Button { |
325 | + id: closeBtn |
326 | + text: "Close button" |
327 | + onClicked: PopupUtils.close(popover); |
328 | } |
329 | } |
330 | } |
331 | |
332 | === modified file 'src/Ubuntu/Components/Popups/1.3/popupUtils.js' |
333 | --- src/Ubuntu/Components/Popups/1.3/popupUtils.js 2016-04-13 19:32:20 +0000 |
334 | +++ src/Ubuntu/Components/Popups/1.3/popupUtils.js 2016-08-31 08:57:26 +0000 |
335 | @@ -60,6 +60,11 @@ |
336 | return null; |
337 | } |
338 | |
339 | + if (!rootObject) { |
340 | + print("PopupUtils.open(): Failed to get the root object."); |
341 | + return null; |
342 | + } |
343 | + |
344 | var popupObject; |
345 | if (params !== undefined) { |
346 | popupObject = popupComponent.createObject(rootObject, params); |
347 | |
348 | === modified file 'src/Ubuntu/Components/Themes/Ambiance/1.2/ActivityIndicatorStyle.qml' |
349 | --- src/Ubuntu/Components/Themes/Ambiance/1.2/ActivityIndicatorStyle.qml 2016-08-17 14:42:42 +0000 |
350 | +++ src/Ubuntu/Components/Themes/Ambiance/1.2/ActivityIndicatorStyle.qml 2016-08-31 08:57:26 +0000 |
351 | @@ -20,8 +20,6 @@ |
352 | Image { |
353 | id: container |
354 | |
355 | - implicitWidth: units.gu(3) |
356 | - implicitHeight: units.gu(3) |
357 | smooth: true |
358 | visible: styledItem.running |
359 | fillMode: Image.PreserveAspectFit |
360 | |
361 | === modified file 'src/Ubuntu/Components/Themes/Ambiance/1.3/ActivityIndicatorStyle.qml' |
362 | --- src/Ubuntu/Components/Themes/Ambiance/1.3/ActivityIndicatorStyle.qml 2016-08-17 14:42:42 +0000 |
363 | +++ src/Ubuntu/Components/Themes/Ambiance/1.3/ActivityIndicatorStyle.qml 2016-08-31 08:57:26 +0000 |
364 | @@ -20,8 +20,6 @@ |
365 | Image { |
366 | id: container |
367 | |
368 | - implicitWidth: units.gu(3) |
369 | - implicitHeight: units.gu(3) |
370 | smooth: true |
371 | visible: styledItem.running && styledItem.visible |
372 | fillMode: Image.PreserveAspectFit |
373 | |
374 | === modified file 'src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollbarStyle.qml' |
375 | --- src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollbarStyle.qml 2016-08-02 11:18:34 +0000 |
376 | +++ src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollbarStyle.qml 2016-08-31 08:57:26 +0000 |
377 | @@ -316,7 +316,9 @@ |
378 | property string propPosRatio: (isVertical) ? "yPosition" : "xPosition" |
379 | property string propSizeRatio: (isVertical) ? "heightRatio" : "widthRatio" |
380 | property string propCoordinate: (isVertical) ? "y" : "x" |
381 | + property string otherPropCoordinate: (isVertical) ? "x" : "y" |
382 | property string propSize: (isVertical) ? "height" : "width" |
383 | + property string otherPropSize: (isVertical) ? "width" : "height" |
384 | property string propAtBeginning: (isVertical) ? "atYBeginning" : "atXBeginning" |
385 | property string propAtEnd: (isVertical) ? "atYEnd" : "atXEnd" |
386 | |
387 | @@ -622,8 +624,6 @@ |
388 | property bool lockDrag: false |
389 | |
390 | property bool hoveringThumb: false |
391 | - property string scrollingProp: isVertical ? "y" : "x" |
392 | - property string sizeProp: isVertical ? "height" : "width" |
393 | |
394 | function initDrag(isMouse) { |
395 | __disableStateBinding = true |
396 | @@ -648,20 +648,29 @@ |
397 | if (!thumbArea.pressed) return |
398 | |
399 | var mouseScrollingProp = isVertical ? mouseY : mouseX |
400 | - if (mouseScrollingProp < slider[scrollingProp]) { |
401 | + if (mouseScrollingProp < slider[scrollbarUtils.propCoordinate]) { |
402 | scrollBy(-pageSize*visuals.longScrollingRatio, true) |
403 | - } else if (mouseScrollingProp > slider[scrollingProp] + slider[sizeProp]) { |
404 | + } else if (mouseScrollingProp > slider[scrollbarUtils.propCoordinate] + slider[scrollbarUtils.propSize]) { |
405 | scrollBy(pageSize*visuals.longScrollingRatio, true) |
406 | } |
407 | } |
408 | |
409 | + //we consider hover if it's inside the TROUGH along the scrolling axis |
410 | + //and inside the THUMB along the non-scrolling axis |
411 | + //NOTE: mouseX and mouseY are assumed to be relative to thumbArea |
412 | function handleHover(mouseX, mouseY) { |
413 | + //NOTE: we're assuming thumbArea has same size/position as the trough. |
414 | + //Use mapToItem to map the coordinates if that assumption falls (i.e. to implement |
415 | + //a larger touch detection area around the thumb) |
416 | var mouseScrollingProp = isVertical ? mouseY : mouseX |
417 | - //don't count as hover if the user is already press-and-holding the trough to |
418 | - //scroll page by page |
419 | + var otherProp = isVertical ? mouseX : mouseY |
420 | + |
421 | + //don't count as hover if the user is already press-and-holding |
422 | + //the trough to scroll page by page |
423 | hoveringThumb = !(pressHoldTimer.running && pressHoldTimer.startedBy === thumbArea) |
424 | - && mouseScrollingProp >= slider[scrollingProp] && |
425 | - mouseScrollingProp <= slider[scrollingProp] + slider[sizeProp] |
426 | + && mouseScrollingProp >= slider[scrollbarUtils.propCoordinate] && |
427 | + mouseScrollingProp <= slider[scrollbarUtils.propCoordinate] + slider[scrollbarUtils.propSize] && |
428 | + otherProp >= 0 && otherProp <= trough[scrollbarUtils.otherPropSize] |
429 | } |
430 | |
431 | function saveFlickableScrollingState() { |
432 | @@ -691,14 +700,9 @@ |
433 | previousY = mouse.y |
434 | } |
435 | |
436 | - anchors { |
437 | - fill: trough |
438 | - // set margins adding 2 dp for error area |
439 | - leftMargin: (!isVertical || frontAligned) ? 0 : units.dp(-2) |
440 | - rightMargin: (!isVertical || rearAligned) ? 0 : units.dp(-2) |
441 | - topMargin: (isVertical || topAligned) ? 0 : units.dp(-2) |
442 | - bottomMargin: (isVertical || bottomAligned) ? 0 : units.dp(-2) |
443 | - } |
444 | + //NOTE: remember to update the handleHover check if you add anchor margins |
445 | + anchors.fill: trough |
446 | + |
447 | enabled: isScrollable && interactive && __canFitSteppersAndShorterThumb |
448 | onPressed: { |
449 | cacheMousePosition(mouse) |
450 | @@ -710,8 +714,8 @@ |
451 | if (firstStepper.visible) { |
452 | var mouseScrollingProp = isVertical ? mouseY : mouseX |
453 | //don't start the press and hold timer to avoid conflicts with the drag |
454 | - if (mouseScrollingProp < slider[scrollingProp] || |
455 | - mouseScrollingProp > (slider[scrollingProp] + slider[sizeProp])) { |
456 | + if (mouseScrollingProp < slider[scrollbarUtils.propCoordinate] || |
457 | + mouseScrollingProp > (slider[scrollbarUtils.propCoordinate] + slider[scrollbarUtils.propSize])) { |
458 | handlePress(mouseX, mouseY) |
459 | pressHoldTimer.startTimer(thumbArea) |
460 | } else { |
461 | @@ -761,7 +765,8 @@ |
462 | pressHoldTimer.stop() |
463 | } |
464 | onReleased: { |
465 | - handleHover(mouseX, mouseY) |
466 | + //don't call handleHover here as touch release also triggers this handler |
467 | + //see bug #1616868 |
468 | resetDrag() |
469 | pressHoldTimer.stop() |
470 | } |
471 | @@ -797,18 +802,29 @@ |
472 | |
473 | //logic to support different colour on hovering |
474 | hoverEnabled: true |
475 | + //This means this mouse filter will only handle real mouse events! |
476 | + //i.e. the synthesized mouse events created when you use |
477 | + //touchscreen will not trigger it! This way we can have logic that |
478 | + //will not trigger when using touch |
479 | + Mouse.ignoreSynthesizedEvents: true |
480 | Mouse.enabled: true |
481 | - Mouse.ignoreSynthesizedEvents: true |
482 | Mouse.onEntered: { |
483 | hoveringThumb = false |
484 | handleHover(event.x, event.y) |
485 | } |
486 | Mouse.onPositionChanged: { |
487 | + //We need to update the hover state also onPosChanged because this area |
488 | + //covers the whole trough, not just the thumb, so entered/exited are not enough |
489 | + //e.g. when mouse moves from inside the thumb to inside the trough, or when you |
490 | + //click on the trough and the thumb scrolls and goes under the mouse cursor |
491 | handleHover(mouse.x, mouse.y) |
492 | } |
493 | Mouse.onExited: { |
494 | hoveringThumb = false |
495 | } |
496 | + Mouse.onReleased: { |
497 | + handleHover(mouse.x, mouse.y) |
498 | + } |
499 | |
500 | Timer { |
501 | id: pressHoldTimer |
502 | @@ -845,6 +861,7 @@ |
503 | |
504 | MouseArea { |
505 | id: steppersMouseArea |
506 | + objectName: "steppersMouseArea" |
507 | //size is handled by the states |
508 | |
509 | property bool hoveringFirstStepper: false |
510 | @@ -897,12 +914,7 @@ |
511 | hoveringFirstStepper = false |
512 | hoveringSecondStepper = false |
513 | } |
514 | - |
515 | - //We don't change the size of the images because we let the image reader figure the size out, |
516 | - //though that means we have to hide the images somehow while the mousearea is visible but has |
517 | - //null size. We choose to enable clipping here instead of creating bindings on images' visible prop |
518 | - clip: true |
519 | - onPressed: { |
520 | + Mouse.onPressed: { |
521 | //This additional trigger of the hovering logic is useful especially in testing |
522 | //environments, where simulating a click on the first stepper will generate onEntered, |
523 | //but then clicking on the second one (without a mouseMove) won't, because they're |
524 | @@ -910,7 +922,16 @@ |
525 | //we ensure that the hovering logic is correct even when there are no mouse moves between |
526 | //clicks on different steppers (like it happens in tst_actionSteppers test). |
527 | handleHover(mouse) |
528 | + } |
529 | + Mouse.onReleased: { |
530 | + handleHover(mouse) |
531 | + } |
532 | |
533 | + //We don't change the size of the images because we let the image reader figure the size out, |
534 | + //though that means we have to hide the images somehow while the mousearea is visible but has |
535 | + //null size. We choose to enable clipping here instead of creating bindings on images' visible prop |
536 | + clip: true |
537 | + onPressed: { |
538 | handlePress() |
539 | pressHoldTimer.startTimer(steppersMouseArea) |
540 | } |
541 | @@ -952,7 +973,7 @@ |
542 | anchors.centerIn: parent |
543 | width: __stepperAssetWidth |
544 | rotation: isVertical ? 180 : 90 |
545 | - //NOTE: Qt.resolvedUrl was removed because it does not seem to be needed and |
546 | + //NOTE: Qt.resolvedUrl was removed because it does not seem to be needed and |
547 | //the QML profiler showed it's relatively expensive |
548 | source: visible ? "../artwork/toolkit_scrollbar-stepper.svg" : "" |
549 | asynchronous: true |
550 | @@ -998,7 +1019,7 @@ |
551 | anchors.centerIn: parent |
552 | width: __stepperAssetWidth |
553 | rotation: isVertical ? 0 : -90 |
554 | - //NOTE: Qt.resolvedUrl was removed because it does not seem to be needed and |
555 | + //NOTE: Qt.resolvedUrl was removed because it does not seem to be needed and |
556 | //the QML profiler showed it's relatively expensive |
557 | source: visible ? "../artwork/toolkit_scrollbar-stepper.svg" : "" |
558 | asynchronous: true |
559 | |
560 | === added file 'src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml' |
561 | --- src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml 1970-01-01 00:00:00 +0000 |
562 | +++ src/Ubuntu/Components/Themes/Ambiance/1.3/ScrollingActionBarStyle.qml 2016-08-31 08:57:26 +0000 |
563 | @@ -0,0 +1,203 @@ |
564 | +/* |
565 | + * Copyright 2016 Canonical Ltd. |
566 | + * |
567 | + * This program is free software; you can redistribute it and/or modify |
568 | + * it under the terms of the GNU Lesser General Public License as published by |
569 | + * the Free Software Foundation; version 3. |
570 | + * |
571 | + * This program is distributed in the hope that it will be useful, |
572 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
573 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
574 | + * GNU Lesser General Public License for more details. |
575 | + * |
576 | + * You should have received a copy of the GNU Lesser General Public License |
577 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
578 | + */ |
579 | +import QtQuick 2.4 |
580 | +import Ubuntu.Components 1.3 |
581 | +import Ubuntu.Components.Styles 1.3 as Style |
582 | + |
583 | +Style.ActionBarStyle { |
584 | + id: actionBarStyle |
585 | + implicitHeight: units.gu(5) |
586 | + |
587 | + clip: true // avoid showing the icons that are scrolled out of the view. |
588 | + backgroundColor: theme.palette.normal.background |
589 | + buttons { |
590 | + foregroundColor: theme.palette.normal.backgroundText |
591 | + pressedForegroundColor: buttons.foregroundColor |
592 | + disabledForegroundColor: theme.palette.disabled.backgroundText |
593 | + backgroundColor: "transparent" // background is already colored |
594 | + pressedBackgroundColor: theme.palette.highlighted.background |
595 | + disabledBackgroundColor: buttons.backgroundColor |
596 | + } |
597 | + |
598 | + property QtObject scrollButtons: Style.ActionItemProperties { |
599 | + foregroundColor: actionBarStyle.buttons.foregroundColor |
600 | + pressedForegroundColor: actionBarStyle.buttons.pressedForegroundColor |
601 | + backgroundColor: actionBarStyle.backgroundColor // must be opaque to hide the icon buttons |
602 | + pressedBackgroundColor: actionBarStyle.buttons.pressedBackgroundColor |
603 | + property real width: units.gu(4) |
604 | + property int fadeDuration: UbuntuAnimation.FastDuration |
605 | + } |
606 | + |
607 | + /*! |
608 | + The default action delegate if the styled item does |
609 | + not provide a delegate. |
610 | + */ |
611 | + // FIXME: This ListItem { AbstractButton { } } construction can be avoided |
612 | + // when StyledItem supports cursor keys navigation, see bug #1573616. |
613 | + // FIXME: For the first and last item in the list, it is possible to move |
614 | + // the focus inside the ListItem to the AbstractButton, see bug #1590005. |
615 | + // FIXME: Focus can go on disabled item. See bug #1611327. |
616 | + defaultDelegate: ListItem { |
617 | + width: units.gu(4) |
618 | + height: actionBarStyle.height |
619 | + enabled: modelData.enabled |
620 | + objectName: modelData.objectName + "_button" |
621 | + AbstractButton { |
622 | + id: button |
623 | + anchors.fill: parent |
624 | + style: IconButtonStyle { |
625 | + foregroundColor: button.pressed ? |
626 | + actionBarStyle.buttons.pressedForegroundColor : |
627 | + button.enabled ? |
628 | + actionBarStyle.buttons.foregroundColor : |
629 | + actionBarStyle.buttons.disabledForegroundColor |
630 | + backgroundColor: button.pressed ? |
631 | + actionBarStyle.buttons.pressedBackgroundColor : |
632 | + button.enabled ? |
633 | + actionBarStyle.buttons.backgroundColor : |
634 | + actionBarStyle.buttons.disabledBackgroundColor |
635 | + } |
636 | + action: modelData |
637 | + activeFocusOnTab: false |
638 | + } |
639 | + divider.visible: false |
640 | + } |
641 | + |
642 | + Rectangle { |
643 | + color: actionBarStyle.backgroundColor |
644 | + id: listViewContainer |
645 | + anchors.fill: parent |
646 | + |
647 | + property var visibleActions: getVisibleActions(styledItem.actions) |
648 | + function getVisibleActions(actions) { |
649 | + var visibleActionList = []; |
650 | + for (var i = 0; i < actions.length; i++) { |
651 | + var action = actions[i]; |
652 | + if (action && action.hasOwnProperty("visible") && action.visible) { |
653 | + visibleActionList.push(action); |
654 | + } |
655 | + } |
656 | + return visibleActionList; |
657 | + } |
658 | + |
659 | + ListView { |
660 | + id: actionsListView |
661 | + objectName: "actions_listview" |
662 | + anchors { |
663 | + right: parent.right |
664 | + top: parent.top |
665 | + bottom: parent.bottom |
666 | + rightMargin: scrollButtons.width |
667 | + } |
668 | + displayMarginBeginning: scrollButtons.width |
669 | + displayMarginEnd: scrollButtons.width |
670 | + leftMargin: -scrollButtons.width |
671 | + rightMargin: -scrollButtons.width |
672 | + width: listViewContainer.width - 2*scrollButtons.width |
673 | + layoutDirection: Qt.RightToLeft |
674 | + |
675 | + orientation: ListView.Horizontal |
676 | + boundsBehavior: Flickable.StopAtBounds |
677 | + delegate: styledItem.delegate |
678 | + model: listViewContainer.visibleActions |
679 | + |
680 | + // This gives the correct behavior when scrollButtons.width is |
681 | + // larger/smaller/equal compared to the width of the delegate. |
682 | + onCurrentIndexChanged: positionViewAtIndex(currentIndex, ListView.Contain) |
683 | + |
684 | + SmoothedAnimation { |
685 | + objectName: "actions_scroll_animation" |
686 | + id: contentXAnim |
687 | + target: actionsListView |
688 | + property: "contentX" |
689 | + duration: UbuntuAnimation.FastDuration |
690 | + } |
691 | + |
692 | + // direction == 1 to show more icons on the left |
693 | + // direction == -1 to show more icons on the right |
694 | + function scroll(direction) { |
695 | + if (contentXAnim.running) contentXAnim.stop(); |
696 | + var newContentX = actionsListView.contentX - (actionsListView.width * direction); |
697 | + contentXAnim.from = actionsListView.contentX; |
698 | + // make sure we don't overshoot bounds |
699 | + contentXAnim.to = MathUtils.clamp( |
700 | + newContentX, |
701 | + originX + scrollButtons.width, |
702 | + actionsListView.originX + actionsListView.contentWidth |
703 | + - actionsListView.width - scrollButtons.width); |
704 | + contentXAnim.start(); |
705 | + } |
706 | + } |
707 | + |
708 | + Component { |
709 | + id: scrollButtonComponent |
710 | + AbstractButton { |
711 | + id: scrollButton |
712 | + width: actionBarStyle.scrollButtons.width |
713 | + enabled: opacity === 1.0 |
714 | + onClicked: actionsListView.scroll(scrollDirection) |
715 | + opacity: buttonOpacity |
716 | + objectName: buttonName |
717 | + Behavior on opacity { |
718 | + UbuntuNumberAnimation { |
719 | + duration: actionBarStyle.scrollButtons.fadeDuration |
720 | + } |
721 | + } |
722 | + Rectangle { |
723 | + anchors.fill: parent |
724 | + color: scrollButton.pressed ? actionBarStyle.scrollButtons.pressedBackgroundColor |
725 | + : actionBarStyle.scrollButtons.backgroundColor |
726 | + } |
727 | + Icon { |
728 | + // FIXME: Use new theme icon from |
729 | + // https://code.launchpad.net/~tiheum/ubuntu-themes/toolkit-arrows/+merge/298609 |
730 | + // after it lands in overlay and archive. |
731 | + anchors.centerIn: parent |
732 | + width: units.gu(1) |
733 | + height: units.gu(1) |
734 | + rotation: iconRotation |
735 | + name: "chevron" |
736 | + color: scrollButton.pressed ? actionBarStyle.scrollButtons.pressedForegroundColor |
737 | + : actionBarStyle.scrollButtons.foregroundColor |
738 | + } |
739 | + } |
740 | + } |
741 | + Loader { |
742 | + anchors { |
743 | + left: parent.left |
744 | + top: parent.top |
745 | + bottom: parent.bottom |
746 | + } |
747 | + sourceComponent: scrollButtonComponent |
748 | + property int iconRotation: 180 |
749 | + property real buttonOpacity: actionsListView.atXBeginning ? 0.0 : 1.0 |
750 | + property int scrollDirection: 1 |
751 | + property string buttonName: "leading_scroll_button" |
752 | + } |
753 | + Loader { |
754 | + anchors { |
755 | + right: parent.right |
756 | + top: parent.top |
757 | + bottom: parent.bottom |
758 | + } |
759 | + sourceComponent: scrollButtonComponent |
760 | + property int iconRotation: 0 |
761 | + property real buttonOpacity: actionsListView.atXEnd ? 0.0 : 1.0 |
762 | + property int scrollDirection: -1 |
763 | + property string buttonName: "trailing_scroll_button" |
764 | + } |
765 | + } |
766 | +} |
767 | |
768 | === modified file 'src/Ubuntu/Components/Themes/Ambiance/Ambiance.pro' |
769 | --- src/Ubuntu/Components/Themes/Ambiance/Ambiance.pro 2016-04-21 11:34:55 +0000 |
770 | +++ src/Ubuntu/Components/Themes/Ambiance/Ambiance.pro 2016-08-31 08:57:26 +0000 |
771 | @@ -103,6 +103,7 @@ |
772 | 1.3/ProgressionVisualStyle.qml \ |
773 | 1.3/PullToRefreshStyle.qml \ |
774 | 1.3/ScrollbarStyle.qml \ |
775 | + 1.3/ScrollingActionBarStyle.qml \ |
776 | 1.3/SectionsStyle.qml \ |
777 | 1.3/SheetForegroundStyle.qml \ |
778 | 1.3/SliderStyle.qml \ |
779 | |
780 | === modified file 'src/Ubuntu/Components/Themes/Ambiance/qmldir' |
781 | --- src/Ubuntu/Components/Themes/Ambiance/qmldir 2016-02-19 11:18:51 +0000 |
782 | +++ src/Ubuntu/Components/Themes/Ambiance/qmldir 2016-08-31 08:57:26 +0000 |
783 | @@ -59,6 +59,7 @@ |
784 | #version 1.3 |
785 | AmbianceNormal 1.3 ./1.3/AmbianceNormal.qml |
786 | ActionBarStyle 1.3 ./1.3/ActionBarStyle.qml |
787 | +ScrollingActionBarStyle 1.3 ./1.3/ScrollingActionBarStyle.qml |
788 | MainViewStyle 1.3 ./1.3/MainViewStyle.qml |
789 | ListItemOptionSelector 1.3 ./1.3/ListItemOptionSelector.qml |
790 | OptionSelectorStyle 1.3 ./1.3/OptionSelectorStyle.qml |
791 | |
792 | === modified file 'src/Ubuntu/Components/qmldir' |
793 | --- src/Ubuntu/Components/qmldir 2016-07-29 13:21:05 +0000 |
794 | +++ src/Ubuntu/Components/qmldir 2016-08-31 08:57:26 +0000 |
795 | @@ -96,7 +96,6 @@ |
796 | ################################################# |
797 | #version 1.3 |
798 | ActionBar 1.3 1.3/ActionBar.qml |
799 | -ActionList 1.3 1.3/ActionList.qml |
800 | AdaptivePageLayout 1.3 1.3/AdaptivePageLayout.qml |
801 | PageColumnsLayout 1.3 1.3/PageColumnsLayout.qml |
802 | PageColumn 1.3 1.3/PageColumn.qml |
803 | |
804 | === modified file 'src/Ubuntu/Test/UbuntuTestCase13.qml' |
805 | --- src/Ubuntu/Test/UbuntuTestCase13.qml 2016-03-15 13:17:53 +0000 |
806 | +++ src/Ubuntu/Test/UbuntuTestCase13.qml 2016-08-31 08:57:26 +0000 |
807 | @@ -15,7 +15,7 @@ |
808 | */ |
809 | |
810 | import QtQuick 2.4 |
811 | -import QtTest 1.0 |
812 | +import QtTest 1.1 |
813 | import Ubuntu.Components 1.3 |
814 | |
815 | /*! |
816 | |
817 | === modified file 'src/Ubuntu/UbuntuMetrics/applicationmonitor.cpp' |
818 | --- src/Ubuntu/UbuntuMetrics/applicationmonitor.cpp 2016-08-06 00:54:30 +0000 |
819 | +++ src/Ubuntu/UbuntuMetrics/applicationmonitor.cpp 2016-08-31 08:57:26 +0000 |
820 | @@ -567,7 +567,9 @@ |
821 | event.generic.id = id; |
822 | // We don't bother fixing up non null-terminated string, just potential |
823 | // overflows. |
824 | - event.generic.stringSize = qMin(size, UMGenericEvent::maxStringSize); |
825 | + // Note: GCC5 comoiler/linker cannot find UMGenericEvent::maxStringSize |
826 | + // if used in qMin(); force type to satisfy it |
827 | + event.generic.stringSize = qMin(size, quint32(UMGenericEvent::maxStringSize)); |
828 | memcpy(event.generic.string, string, event.generic.stringSize); |
829 | d->m_loggingThread->push(&event); |
830 | return true; |
831 | |
832 | === modified file 'src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro' |
833 | --- src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro 2016-08-04 17:25:09 +0000 |
834 | +++ src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro 2016-08-31 08:57:26 +0000 |
835 | @@ -44,7 +44,15 @@ |
836 | mousetouchadaptor_p.h \ |
837 | mousetouchadaptor_p_p.h \ |
838 | propertychange_p.h \ |
839 | - ubuntutoolkitmodule.h |
840 | + ubuntutoolkitmodule.h \ |
841 | + splitview_p.h \ |
842 | + splitview_p_p.h \ |
843 | + privates/splitviewhandler_p_p.h \ |
844 | + menu_p_p.h \ |
845 | + menubar_p_p.h \ |
846 | + menu_p.h \ |
847 | + menubar_p.h \ |
848 | + menugroup_p.h |
849 | |
850 | SOURCES += \ |
851 | colorutils.cpp \ |
852 | @@ -52,7 +60,13 @@ |
853 | asyncloader.cpp \ |
854 | mousetouchadaptor.cpp \ |
855 | propertychange.cpp \ |
856 | - ubuntutoolkitmodule.cpp |
857 | + ubuntutoolkitmodule.cpp \ |
858 | + splitview.cpp \ |
859 | + privates/splitviewhandler.cpp \ |
860 | + splitviewlayout.cpp \ |
861 | + menu.cpp \ |
862 | + menubar.cpp \ |
863 | + menugroup.cpp |
864 | |
865 | HEADERS += \ |
866 | uctheme_p.h \ |
867 | @@ -149,7 +163,9 @@ |
868 | privates/appheaderbase_p.h \ |
869 | label_p.h \ |
870 | ucbottomedgeregion_p_p.h \ |
871 | - privates/ucscrollbarutils_p.h |
872 | + privates/ucscrollbarutils_p.h \ |
873 | + actionlist_p.h \ |
874 | + exclusivegroup_p.h |
875 | |
876 | SOURCES += \ |
877 | uctheme.cpp \ |
878 | @@ -226,7 +242,9 @@ |
879 | privates/ucpagewrapper.cpp \ |
880 | privates/ucpagewrapperincubator.cpp \ |
881 | privates/appheaderbase.cpp \ |
882 | - privates/ucscrollbarutils.cpp |
883 | + privates/ucscrollbarutils.cpp \ |
884 | + actionlist.cpp \ |
885 | + exclusivegroup.cpp |
886 | |
887 | # adapters |
888 | SOURCES += $$PWD/adapters/alarmsadapter_organizer.cpp |
889 | |
890 | === added file 'src/Ubuntu/UbuntuToolkit/actionlist.cpp' |
891 | --- src/Ubuntu/UbuntuToolkit/actionlist.cpp 1970-01-01 00:00:00 +0000 |
892 | +++ src/Ubuntu/UbuntuToolkit/actionlist.cpp 2016-08-31 08:57:26 +0000 |
893 | @@ -0,0 +1,158 @@ |
894 | +/* |
895 | + * Copyright 2016 Canonical Ltd. |
896 | + * |
897 | + * This program is free software; you can redistribute it and/or modify |
898 | + * it under the terms of the GNU Lesser General Public License as published by |
899 | + * the Free Software Foundation; version 3. |
900 | + * |
901 | + * This program is distributed in the hope that it will be useful, |
902 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
903 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
904 | + * GNU Lesser General Public License for more details. |
905 | + * |
906 | + * You should have received a copy of the GNU Lesser General Public License |
907 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
908 | + */ |
909 | + |
910 | +#include "actionlist_p.h" |
911 | +#include "ucaction_p.h" |
912 | + |
913 | +UT_NAMESPACE_BEGIN |
914 | + |
915 | +/*! |
916 | + * \qmltype ActionList |
917 | + * \inqmlmodule Ubuntu.Components |
918 | + * \ingroup ubuntu |
919 | + * \brief List of \l Action items |
920 | + * An ActionList provies a way of grouping actions together. |
921 | + * \qml |
922 | + * ActionList { |
923 | + * Action { |
924 | + * id: action1 |
925 | + * } |
926 | + * Action { |
927 | + * id: action2 |
928 | + * } |
929 | + * } |
930 | + * \endqml |
931 | + */ |
932 | +/*! |
933 | + * \qmlsignal ActionList::added(Action action) |
934 | + * \since Ubuntu.Components 1.3 |
935 | + * Signal called when an action is added to the list |
936 | + */ |
937 | +/*! |
938 | + * \qmlsignal ActionList::removed(Action action) |
939 | + * \since Ubuntu.Components 1.3 |
940 | + * Signal called when an action is removed from the list |
941 | + */ |
942 | +ActionList::ActionList(QObject *parent) |
943 | + : QObject(parent) |
944 | +{ |
945 | +} |
946 | + |
947 | +/*! |
948 | + * \qmlmethod ActionList::addAction(Action action) |
949 | + * \since Ubuntu.Components 1.3 |
950 | + * Adds an Action to the list programatically. |
951 | + * \qml |
952 | + * Item { |
953 | + * Instantiator { |
954 | + * model: 4 |
955 | + * onObjectAdded: actionList.addAction(object) |
956 | + * onObjectRemoved: actionList.removeAction(object) |
957 | + * |
958 | + * Action {} |
959 | + * } |
960 | + * |
961 | + * ActionList { |
962 | + * id: actionList |
963 | + * } |
964 | + * } |
965 | + * \endqml |
966 | + * \sa ActionList::removeAction |
967 | + */ |
968 | +void ActionList::addAction(UCAction *action) |
969 | +{ |
970 | + if (m_actions.contains(action)) { |
971 | + return; |
972 | + } |
973 | + m_actions.append(action); |
974 | + Q_EMIT added(action); |
975 | +} |
976 | + |
977 | +/*! |
978 | + * \qmlmethod ActionList::removeAction(Action action) |
979 | + * \since Ubuntu.Components 1.3 |
980 | + * Removes an action from the list programatically. |
981 | + * \sa ActionList::addAction |
982 | + */ |
983 | +void ActionList::removeAction(UCAction *action) |
984 | +{ |
985 | + if (!action) { |
986 | + return; |
987 | + } |
988 | + if (m_actions.removeOne(action)) { |
989 | + Q_EMIT removed(action); |
990 | + } |
991 | +} |
992 | + |
993 | +/*! |
994 | + * \qmlproperty list<Action> ActionList::actions |
995 | + * \default |
996 | + * List of Actions in this ActionList |
997 | + * Note that when you set this property, the children of the ActionList will be ignored, |
998 | + * so do not set the list and define children. |
999 | + * |
1000 | + * The advantage of setting actions over using the children is that the same |
1001 | + * \l Action items can be used in several sets of actions. |
1002 | + */ |
1003 | +QQmlListProperty<UCAction> ActionList::actions() |
1004 | +{ |
1005 | + return QQmlListProperty<UCAction>(this, 0, |
1006 | + ActionList::append, |
1007 | + ActionList::count, |
1008 | + ActionList::at, |
1009 | + ActionList::clear); |
1010 | +} |
1011 | + |
1012 | +const QList<UCAction*> &ActionList::list() const |
1013 | +{ |
1014 | + return m_actions; |
1015 | +} |
1016 | + |
1017 | +void ActionList::append(QQmlListProperty<UCAction> *list, UCAction *action) |
1018 | +{ |
1019 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
1020 | + if (actionList) { |
1021 | + actionList->addAction(action); |
1022 | + } |
1023 | +} |
1024 | + |
1025 | +void ActionList::clear(QQmlListProperty<UCAction> *list) |
1026 | +{ |
1027 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
1028 | + if (actionList) { |
1029 | + actionList->m_actions.clear(); |
1030 | + } |
1031 | +} |
1032 | + |
1033 | +UCAction* ActionList::at(QQmlListProperty<UCAction> *list, int index) |
1034 | +{ |
1035 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
1036 | + if (actionList) { |
1037 | + return actionList->m_actions.value(index, nullptr); |
1038 | + } |
1039 | + return Q_NULLPTR; |
1040 | +} |
1041 | + |
1042 | +int ActionList::count(QQmlListProperty<UCAction> *list) |
1043 | +{ |
1044 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
1045 | + if (actionList) { |
1046 | + return actionList->m_actions.count(); |
1047 | + } |
1048 | + return 0; |
1049 | +} |
1050 | + |
1051 | +UT_NAMESPACE_END |
1052 | |
1053 | === added file 'src/Ubuntu/UbuntuToolkit/actionlist_p.h' |
1054 | --- src/Ubuntu/UbuntuToolkit/actionlist_p.h 1970-01-01 00:00:00 +0000 |
1055 | +++ src/Ubuntu/UbuntuToolkit/actionlist_p.h 2016-08-31 08:57:26 +0000 |
1056 | @@ -0,0 +1,58 @@ |
1057 | +/* |
1058 | + * Copyright 2016 Canonical Ltd. |
1059 | + * |
1060 | + * This program is free software; you can redistribute it and/or modify |
1061 | + * it under the terms of the GNU Lesser General Public License as published by |
1062 | + * the Free Software Foundation; version 3. |
1063 | + * |
1064 | + * This program is distributed in the hope that it will be useful, |
1065 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1066 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1067 | + * GNU Lesser General Public License for more details. |
1068 | + * |
1069 | + * You should have received a copy of the GNU Lesser General Public License |
1070 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1071 | + */ |
1072 | + |
1073 | +#ifndef ACTIONLIST_H |
1074 | +#define ACTIONLIST_H |
1075 | + |
1076 | +#include <QObject> |
1077 | +#include <QtQml/QQmlListProperty> |
1078 | +#include "ubuntutoolkitglobal.h" |
1079 | + |
1080 | +UT_NAMESPACE_BEGIN |
1081 | + |
1082 | +class UCAction; |
1083 | +class UBUNTUTOOLKIT_EXPORT ActionList : public QObject |
1084 | +{ |
1085 | + Q_OBJECT |
1086 | + Q_PROPERTY(QQmlListProperty<UT_PREPEND_NAMESPACE(UCAction)> actions READ actions) |
1087 | + Q_CLASSINFO("DefaultProperty", "actions") |
1088 | +public: |
1089 | + explicit ActionList(QObject *parent = 0); |
1090 | + |
1091 | + QQmlListProperty<UCAction> actions(); |
1092 | + |
1093 | + const QList<UCAction*> &list() const; |
1094 | + |
1095 | +public Q_SLOTS: |
1096 | + void addAction(UT_PREPEND_NAMESPACE(UCAction) *action); |
1097 | + void removeAction(UT_PREPEND_NAMESPACE(UCAction) *action); |
1098 | + |
1099 | +Q_SIGNALS: |
1100 | + void added(UCAction *action); |
1101 | + void removed(UCAction *action); |
1102 | + |
1103 | +protected: |
1104 | + QList<UCAction*> m_actions; |
1105 | + |
1106 | + static void append(QQmlListProperty<UCAction> *list, UCAction *action); |
1107 | + static void clear(QQmlListProperty<UCAction> *list); |
1108 | + static UCAction* at(QQmlListProperty<UCAction> *list, int index); |
1109 | + static int count(QQmlListProperty<UCAction> *list); |
1110 | +}; |
1111 | + |
1112 | +UT_NAMESPACE_END |
1113 | + |
1114 | +#endif // ACTIONLIST_H |
1115 | |
1116 | === added file 'src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp' |
1117 | --- src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp 1970-01-01 00:00:00 +0000 |
1118 | +++ src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp 2016-08-31 08:57:26 +0000 |
1119 | @@ -0,0 +1,168 @@ |
1120 | +/* |
1121 | + * Copyright 2016 Canonical Ltd. |
1122 | + * |
1123 | + * This program is free software; you can redistribute it and/or modify |
1124 | + * it under the terms of the GNU Lesser General Public License as published by |
1125 | + * the Free Software Foundation; version 3. |
1126 | + * |
1127 | + * This program is distributed in the hope that it will be useful, |
1128 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1129 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1130 | + * GNU Lesser General Public License for more details. |
1131 | + * |
1132 | + * You should have received a copy of the GNU Lesser General Public License |
1133 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1134 | + * |
1135 | + */ |
1136 | + |
1137 | +#include "exclusivegroup_p.h" |
1138 | +#include "ucaction_p.h" |
1139 | + |
1140 | +#include <QSignalMapper> |
1141 | + |
1142 | +#define CHECKED_PROPERTY "checked" |
1143 | + |
1144 | +UT_NAMESPACE_BEGIN |
1145 | + |
1146 | +static const char *checkableSignals[] = { |
1147 | + "toggled(bool)", |
1148 | + 0 |
1149 | +}; |
1150 | + |
1151 | +static bool isChecked(const QObject *o) |
1152 | +{ |
1153 | + if (!o) return false; |
1154 | + QVariant checkedVariant = o->property(CHECKED_PROPERTY); |
1155 | + return checkedVariant.isValid() && checkedVariant.toBool(); |
1156 | +} |
1157 | + |
1158 | +/*! |
1159 | + * \qmltype ExclusiveGroup |
1160 | + * \inqmlmodule Ubuntu.Components |
1161 | + * \since Ubuntu.Components 1.3 |
1162 | + * \ingroup ubuntu |
1163 | + * \inherits ActionList |
1164 | + * \brief ExclusiveGroup provides a way to declare several checkable controls as mutually exclusive. |
1165 | + * |
1166 | + * The ExclusiveGroup will only allow a single object to have it's checkable property set to "true" |
1167 | + * at any one time. The exclusive group accepts child Actions, but objects other than Actions can be |
1168 | + * used by using the \l bindCheckable function as long as they support one of the required signals, |
1169 | + * and a "checked" property. |
1170 | + * \qml |
1171 | + * ExclusiveGroup { |
1172 | + * Action { |
1173 | + * parameterType: Action.Bool |
1174 | + * state: true |
1175 | + * } |
1176 | + * Action { |
1177 | + * parameterType: Action.Bool |
1178 | + * state: false |
1179 | + * } |
1180 | + * } |
1181 | + * \endqml |
1182 | + */ |
1183 | +ExclusiveGroup::ExclusiveGroup(QObject *parent) |
1184 | + : ActionList(parent) |
1185 | + , m_signalMapper(new QSignalMapper(this)) |
1186 | + , m_entranceGuard(false) |
1187 | +{ |
1188 | + connect(this, &ActionList::added, this, &ExclusiveGroup::onActionAdded); |
1189 | + connect(this, &ActionList::removed, this, &ExclusiveGroup::onActionRemoved); |
1190 | + |
1191 | + int index = m_signalMapper->metaObject()->indexOfMethod("map()"); |
1192 | + m_updateCurrentMethod = m_signalMapper->metaObject()->method(index); |
1193 | + connect(m_signalMapper, static_cast<void(QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped), this, [this](QObject *object) { |
1194 | + if (isChecked(object)) { |
1195 | + setCurrent(object); |
1196 | + } |
1197 | + }); |
1198 | +} |
1199 | + |
1200 | +void ExclusiveGroup::onActionAdded(UCAction *action) |
1201 | +{ |
1202 | + action->setExclusiveGroup(this); |
1203 | +} |
1204 | + |
1205 | +void ExclusiveGroup::onActionRemoved(UCAction *action) |
1206 | +{ |
1207 | + action->setExclusiveGroup(nullptr); |
1208 | +} |
1209 | + |
1210 | +/*! |
1211 | + * \qmlproperty Action ExclusiveGroup::current |
1212 | + * Returns the currently checked action |
1213 | + */ |
1214 | +void ExclusiveGroup::setCurrent(QObject *object) |
1215 | +{ |
1216 | + if (m_current == object) |
1217 | + return; |
1218 | + |
1219 | + if (m_current) |
1220 | + m_current->setProperty(CHECKED_PROPERTY, QVariant(false)); |
1221 | + m_current = object; |
1222 | + if (m_current) |
1223 | + m_current->setProperty(CHECKED_PROPERTY, QVariant(true)); |
1224 | + Q_EMIT currentChanged(); |
1225 | +} |
1226 | + |
1227 | +QObject *ExclusiveGroup::current() const |
1228 | +{ |
1229 | + return m_current.data(); |
1230 | +} |
1231 | + |
1232 | +/*! |
1233 | + * \qmlmethod void ExclusiveGroup::bindCheckable(object object) |
1234 | + * Explicitly bind an objects checkability to this exclusive group. |
1235 | + * \note This only works with objects which support the following signals signals: |
1236 | + * \list |
1237 | + * \li \b toggled(bool) |
1238 | + * \endlist |
1239 | + * \qml |
1240 | + * Item { |
1241 | + * ExclusiveGroup { |
1242 | + * id: exclusiveGroup |
1243 | + * } |
1244 | + * Instantiator { |
1245 | + * model: 4 |
1246 | + * onObjectAdded: exclusiveGroup.bindCheckable(object) |
1247 | + * onObjectRemoved: exclusiveGroup.unbindCheckable(object) |
1248 | + * |
1249 | + * Action { |
1250 | + * checkable: true |
1251 | + * } |
1252 | + * } |
1253 | + * } |
1254 | + * \endqml |
1255 | + * \sa ExclusiveGroup::unbindCheckable |
1256 | + */ |
1257 | +void ExclusiveGroup::bindCheckable(QObject *object) |
1258 | +{ |
1259 | + for (const char **signalName = checkableSignals; *signalName; signalName++) { |
1260 | + int signalIndex = object->metaObject()->indexOfSignal(*signalName); |
1261 | + if (signalIndex != -1) { |
1262 | + QMetaMethod signalMethod = object->metaObject()->method(signalIndex); |
1263 | + connect(object, signalMethod, m_signalMapper, m_updateCurrentMethod, Qt::UniqueConnection); |
1264 | + m_signalMapper->setMapping(object, object); |
1265 | + connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*)), Qt::UniqueConnection); |
1266 | + if (!m_current && isChecked(object)) |
1267 | + setCurrent(object); |
1268 | + break; |
1269 | + } |
1270 | + } |
1271 | +} |
1272 | + |
1273 | +/*! |
1274 | + * \qmlmethod void ExclusiveGroup::unbindCheckable(object object) |
1275 | + * Explicitly unbind an objects checkability from this exclusive group. |
1276 | + * \sa ExclusiveGroup::bindCheckable |
1277 | + */ |
1278 | +void ExclusiveGroup::unbindCheckable(QObject *object) |
1279 | +{ |
1280 | + if (m_current == object) |
1281 | + setCurrent(0); |
1282 | + |
1283 | + disconnect(object, 0, m_signalMapper, 0); |
1284 | + disconnect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*))); |
1285 | +} |
1286 | + |
1287 | +UT_NAMESPACE_END |
1288 | |
1289 | === added file 'src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h' |
1290 | --- src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h 1970-01-01 00:00:00 +0000 |
1291 | +++ src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h 2016-08-31 08:57:26 +0000 |
1292 | @@ -0,0 +1,62 @@ |
1293 | +/* |
1294 | + * Copyright 2016 Canonical Ltd. |
1295 | + * |
1296 | + * This program is free software; you can redistribute it and/or modify |
1297 | + * it under the terms of the GNU Lesser General Public License as published by |
1298 | + * the Free Software Foundation; version 3. |
1299 | + * |
1300 | + * This program is distributed in the hope that it will be useful, |
1301 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1302 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1303 | + * GNU Lesser General Public License for more details. |
1304 | + * |
1305 | + * You should have received a copy of the GNU Lesser General Public License |
1306 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1307 | + * |
1308 | + */ |
1309 | + |
1310 | +#ifndef EXCLUSIVEGROUP_H |
1311 | +#define EXCLUSIVEGROUP_H |
1312 | + |
1313 | +#include "actionlist_p.h" |
1314 | +#include "ubuntutoolkitglobal.h" |
1315 | + |
1316 | +#include <QMetaMethod> |
1317 | +#include <QPointer> |
1318 | + |
1319 | +class QSignalMapper; |
1320 | + |
1321 | +UT_NAMESPACE_BEGIN |
1322 | + |
1323 | +class UBUNTUTOOLKIT_EXPORT ExclusiveGroup : public ActionList |
1324 | +{ |
1325 | + Q_OBJECT |
1326 | + Q_PROPERTY(QObject* current READ current NOTIFY currentChanged) |
1327 | + |
1328 | +public: |
1329 | + explicit ExclusiveGroup(QObject *parent = 0); |
1330 | + |
1331 | + QObject* current() const; |
1332 | + |
1333 | + Q_INVOKABLE void bindCheckable(QObject* object); |
1334 | + Q_INVOKABLE void unbindCheckable(QObject* object); |
1335 | + |
1336 | +Q_SIGNALS: |
1337 | + void currentChanged(); |
1338 | + |
1339 | +protected Q_SLOTS: |
1340 | + void onActionAdded(UCAction* action); |
1341 | + void onActionRemoved(UCAction* action); |
1342 | + |
1343 | +private: |
1344 | + void setCurrent(QObject* action); |
1345 | + |
1346 | + QSignalMapper* m_signalMapper; |
1347 | + QPointer<QObject> m_current; |
1348 | + QMetaMethod m_updateCurrentMethod; |
1349 | + bool m_entranceGuard; |
1350 | +}; |
1351 | + |
1352 | +UT_NAMESPACE_END |
1353 | + |
1354 | +#endif // EXCLUSIVEGROUP_H |
1355 | |
1356 | === added file 'src/Ubuntu/UbuntuToolkit/menu.cpp' |
1357 | --- src/Ubuntu/UbuntuToolkit/menu.cpp 1970-01-01 00:00:00 +0000 |
1358 | +++ src/Ubuntu/UbuntuToolkit/menu.cpp 2016-08-31 08:57:26 +0000 |
1359 | @@ -0,0 +1,671 @@ |
1360 | +/* |
1361 | + * Copyright 2016 Canonical Ltd. |
1362 | + * |
1363 | + * This program is free software; you can redistribute it and/or modify |
1364 | + * it under the terms of the GNU Lesser General Public License as published by |
1365 | + * the Free Software Foundation; version 3. |
1366 | + * |
1367 | + * This program is distributed in the hope that it will be useful, |
1368 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1369 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1370 | + * GNU Lesser General Public License for more details. |
1371 | + * |
1372 | + * You should have received a copy of the GNU Lesser General Public License |
1373 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1374 | + */ |
1375 | + |
1376 | +#include "menu_p.h" |
1377 | +#include "menu_p_p.h" |
1378 | +#include "menubar_p.h" |
1379 | +#include "actionlist_p.h" |
1380 | +#include "menugroup_p.h" |
1381 | + |
1382 | +// Qt |
1383 | +#include <QPointer> |
1384 | +#include <QQuickItem> |
1385 | +#include <QLoggingCategory> |
1386 | +#include <QtGui/qpa/qplatformtheme.h> |
1387 | +#include <QtGui/qpa/qplatformmenu.h> |
1388 | +#include <private/qguiapplication_p.h> |
1389 | +#include <private/qquickitem_p.h> |
1390 | + |
1391 | +#include <functional> |
1392 | + |
1393 | +Q_LOGGING_CATEGORY(ucMenu, "ubuntu.components.Menu", QtMsgType::QtWarningMsg) |
1394 | + |
1395 | +UT_NAMESPACE_BEGIN |
1396 | + |
1397 | +namespace { |
1398 | + |
1399 | +QWindow* findWindowForObject(QObject* object) |
1400 | +{ |
1401 | + QObject* window = object; |
1402 | + while (window && !window->isWindowType()) { |
1403 | + window = window->parent(); |
1404 | + if (QQuickItem* item = qobject_cast<QQuickItem*>(window)) { |
1405 | + window = item->window(); |
1406 | + } |
1407 | + } |
1408 | + return qobject_cast<QWindow*>(window); |
1409 | +} |
1410 | + |
1411 | +// recursively get the all the object from the menu group which are not lists or groups. |
1412 | +QObjectList getActionsFromMenuGroup(MenuGroup* menuGroup) { |
1413 | + |
1414 | + QObjectList objectList; |
1415 | + |
1416 | + Q_FOREACH(QObject* data, menuGroup->list()) { |
1417 | + |
1418 | + if (auto actionList = qobject_cast<ActionList*>(data)) { |
1419 | + Q_FOREACH(UCAction* action, actionList->list()) { |
1420 | + objectList << action; |
1421 | + } |
1422 | + } else if (auto subMenuGroup = qobject_cast<MenuGroup*>(data)) { |
1423 | + objectList << getActionsFromMenuGroup(subMenuGroup); |
1424 | + } else { |
1425 | + objectList << data; |
1426 | + } |
1427 | + } |
1428 | + return objectList; |
1429 | +} |
1430 | + |
1431 | +// recursively get the first object from the menu group which is not a list or a group. |
1432 | +QObject* getFirstObject(MenuGroup* menuGroup) { |
1433 | + |
1434 | + Q_FOREACH(QObject* data, menuGroup->list()) { |
1435 | + |
1436 | + if (auto subMenuGroup = qobject_cast<MenuGroup*>(data)) { |
1437 | + QObject* object = getFirstObject(subMenuGroup); |
1438 | + if (object) { return object; } |
1439 | + } else if (auto actionList = qobject_cast<ActionList*>(data)) { |
1440 | + return actionList->list().count() > 0 ? actionList->list()[0] : 0; |
1441 | + } else { |
1442 | + return data; |
1443 | + } |
1444 | + } |
1445 | + return Q_NULLPTR; |
1446 | +} |
1447 | + |
1448 | +} |
1449 | + |
1450 | +MenuPrivate::MenuPrivate(Menu *qq) |
1451 | + : q_ptr(qq) |
1452 | + , m_platformMenu(QGuiApplicationPrivate::platformTheme()->createPlatformMenu()) |
1453 | +{ |
1454 | +} |
1455 | + |
1456 | +MenuPrivate::~MenuPrivate() |
1457 | +{ |
1458 | + qDeleteAll(m_platformItems); |
1459 | + m_platformItems.clear(); |
1460 | + |
1461 | + delete m_platformMenu; |
1462 | + m_platformMenu = Q_NULLPTR; |
1463 | +} |
1464 | + |
1465 | +void MenuPrivate::insertObject(int index, QObject *o) |
1466 | +{ |
1467 | + Q_Q(Menu); |
1468 | + if (!o) return; |
1469 | + qCDebug(ucMenu).nospace() << "Menu::insertObject(index="<< index << ", object=" << o << ")"; |
1470 | + |
1471 | + if (!m_platformMenu) { |
1472 | + m_data.insert(m_data.count() > index ? index : m_data.count(), o); |
1473 | + return; |
1474 | + } |
1475 | + |
1476 | + // If the menus contains lists or groups, we need to alter the insertion index to account for them. |
1477 | + int actualIndex = 0; |
1478 | + bool insertSeparator = false; |
1479 | + for (int i = 0; i < index && i < m_data.count(); i++) { |
1480 | + QObject* data = m_data[i]; |
1481 | + |
1482 | + int dataObjectCount = 0; |
1483 | + if (auto menuGroup = qobject_cast<MenuGroup*>(data)) { |
1484 | + dataObjectCount = getActionsFromMenuGroup(menuGroup).count(); |
1485 | + } else if (auto list = qobject_cast<ActionList*>(data)) { |
1486 | + dataObjectCount = list->list().count(); |
1487 | + } else { |
1488 | + dataObjectCount = 1; |
1489 | + } |
1490 | + actualIndex += dataObjectCount; |
1491 | + // insert a separator before the new item if there are previous items. |
1492 | + insertSeparator |= dataObjectCount > 0; |
1493 | + |
1494 | + // account for item separators if it's not the last data item. |
1495 | + actualIndex += dataObjectCount > 0 && (i+1 < index && i+1 < m_data.count()) ? 1: 0; |
1496 | + } |
1497 | + |
1498 | + // need to make sure the item after the insertion index has a separator |
1499 | + if (index < m_data.count()) { |
1500 | + QObject* data = m_data.at(index); |
1501 | + QObject* objectToSeparate(Q_NULLPTR); |
1502 | + |
1503 | + if (auto menuGroup = qobject_cast<MenuGroup*>(data)) { |
1504 | + objectToSeparate = getFirstObject(menuGroup); |
1505 | + } else if (auto actionList = qobject_cast<ActionList*>(data)) { |
1506 | + objectToSeparate = actionList->list().count() > 0 ? actionList->list()[0] : 0; |
1507 | + } else { |
1508 | + objectToSeparate = data; |
1509 | + } |
1510 | + |
1511 | + if (objectToSeparate && m_platformItems.contains(objectToSeparate)) { |
1512 | + m_platformItems.value(objectToSeparate)->setSeparator(); |
1513 | + } |
1514 | + } |
1515 | + |
1516 | + m_data.insert(m_data.count() > index ? index : m_data.count(), o); |
1517 | + |
1518 | + // if an object changes, we need to remove and re-add it. |
1519 | + std::function<void()> refreshObject = [o, this]() { |
1520 | + int index = m_data.indexOf(o); |
1521 | + if (index >= 0) { |
1522 | + removeObject(o); |
1523 | + insertObject(index, o); |
1524 | + } |
1525 | + }; |
1526 | + |
1527 | + // Get All the menu item objects |
1528 | + QObjectList objects; |
1529 | + if (auto menuGroup = qobject_cast<MenuGroup*>(o)) { |
1530 | + Q_FOREACH(QObject* menuGroupObject, getActionsFromMenuGroup(menuGroup)) { |
1531 | + objects << menuGroupObject; |
1532 | + } |
1533 | + // connect to content changes |
1534 | + QObject::connect(menuGroup, &MenuGroup::changed, q, refreshObject); |
1535 | + } else if (auto actionList = qobject_cast<ActionList*>(o)) { |
1536 | + Q_FOREACH(UCAction* action, actionList->list()) { |
1537 | + objects << action; |
1538 | + } |
1539 | + // connect to content changes |
1540 | + QObject::connect(actionList, &ActionList::added, q, refreshObject); |
1541 | + QObject::connect(actionList, &ActionList::removed, q, refreshObject); |
1542 | + } else { |
1543 | + objects << o; |
1544 | + } |
1545 | + |
1546 | + Q_FOREACH(QObject* platformObject, objects) { |
1547 | + // add to platform |
1548 | + auto platformWrapper = new PlatformItemWrapper(platformObject, q); |
1549 | + platformWrapper->insert(actualIndex++, insertSeparator); |
1550 | + if (platformWrapper->hasSeparator()) { // we also inserted an separator, need to increment for next position. |
1551 | + actualIndex++; |
1552 | + } |
1553 | + insertSeparator = false; |
1554 | + m_platformItems[platformObject] = platformWrapper; |
1555 | + // map the inserted item with the object which sources the platformItem info. |
1556 | + if (!m_dataPlatformObjectMap.contains(o, platformObject)) { |
1557 | + m_dataPlatformObjectMap.insertMulti(o, platformObject); |
1558 | + } |
1559 | + |
1560 | + QObject::connect(platformObject, &QObject::destroyed, q, [o, this](QObject* platformObject) { |
1561 | + m_dataPlatformObjectMap.remove(o, platformObject); |
1562 | + |
1563 | + if (m_platformItems.contains(platformObject)) { |
1564 | + PlatformItemWrapper* wrapper = m_platformItems.take(platformObject); |
1565 | + wrapper->remove(); |
1566 | + delete wrapper; |
1567 | + } |
1568 | + }); |
1569 | + } |
1570 | +} |
1571 | + |
1572 | +void MenuPrivate::removeObject(QObject *o) |
1573 | +{ |
1574 | + Q_Q(Menu); |
1575 | + m_data.removeOne(o); |
1576 | + qCDebug(ucMenu).nospace() << "Menu::removeObject(" << o << ")"; |
1577 | + |
1578 | + if (m_platformMenu) { |
1579 | + if (auto menuGroup = qobject_cast<MenuGroup*>(o)) { |
1580 | + // disconnect from content changes |
1581 | + QObject::disconnect(menuGroup, &MenuGroup::changed, q, 0); |
1582 | + } else if (ActionList* actionList = qobject_cast<ActionList*>(o)) { |
1583 | + // disconnect from content changes |
1584 | + QObject::disconnect(actionList, &ActionList::added, q, 0); |
1585 | + QObject::disconnect(actionList, &ActionList::removed, q, 0); |
1586 | + } |
1587 | + |
1588 | + QList<QObject*> platformObjects = m_dataPlatformObjectMap.values(o); |
1589 | + m_dataPlatformObjectMap.remove(o); |
1590 | + |
1591 | + Q_FOREACH(QObject* platformObject, platformObjects) { |
1592 | + // remove from platform. |
1593 | + if (m_platformItems.contains(platformObject)) { |
1594 | + PlatformItemWrapper* wrapper = m_platformItems.take(platformObject); |
1595 | + wrapper->remove(); |
1596 | + delete wrapper; |
1597 | + } |
1598 | + } |
1599 | + } |
1600 | +} |
1601 | + |
1602 | +void MenuPrivate::_q_updateEnabled() |
1603 | +{ |
1604 | + Q_Q(Menu); |
1605 | + |
1606 | + bool isEnabled = q->isEnabled(); |
1607 | + if (m_platformMenu) { m_platformMenu->setEnabled(isEnabled); } |
1608 | +} |
1609 | + |
1610 | +void MenuPrivate::_q_updateText() |
1611 | +{ |
1612 | + Q_Q(Menu); |
1613 | + |
1614 | + QString text = q->text(); |
1615 | + if (m_platformMenu) { m_platformMenu->setText(text); } |
1616 | +} |
1617 | + |
1618 | +void MenuPrivate::_q_updateIcon() |
1619 | +{ |
1620 | + Q_Q(Menu); |
1621 | + |
1622 | + QIcon icon; |
1623 | + if (!q->iconSource().isEmpty()) { |
1624 | + icon = QIcon(q->iconSource().path()); |
1625 | + } else if (!q->iconName().isEmpty()) { |
1626 | + icon = QIcon::fromTheme(q->iconName()); |
1627 | + } |
1628 | + |
1629 | + if (m_platformMenu) { m_platformMenu->setIcon(icon); } |
1630 | +} |
1631 | + |
1632 | +void MenuPrivate::_q_updateVisible() |
1633 | +{ |
1634 | + Q_Q(Menu); |
1635 | + |
1636 | + bool visible = q->visible(); |
1637 | + if (m_platformMenu) { m_platformMenu->setVisible(visible); } |
1638 | +} |
1639 | + |
1640 | +void MenuPrivate::data_append(QQmlListProperty<QObject> *prop, QObject *o) |
1641 | +{ |
1642 | + Menu *q = qobject_cast<Menu *>(prop->object); |
1643 | + q->appendObject(o); |
1644 | +} |
1645 | + |
1646 | +int MenuPrivate::data_count(QQmlListProperty<QObject> *prop) |
1647 | +{ |
1648 | + MenuPrivate *p = static_cast<MenuPrivate *>(prop->data); |
1649 | + return p->m_data.count(); |
1650 | +} |
1651 | + |
1652 | +QObject *MenuPrivate::data_at(QQmlListProperty<QObject> *prop, int index) |
1653 | +{ |
1654 | + MenuPrivate *p = static_cast<MenuPrivate *>(prop->data); |
1655 | + return p->m_data.value(index, Q_NULLPTR); |
1656 | +} |
1657 | + |
1658 | +void MenuPrivate::data_clear(QQmlListProperty<QObject> *prop) |
1659 | +{ |
1660 | + MenuPrivate *p = static_cast<MenuPrivate *>(prop->data); |
1661 | + p->m_data.clear(); |
1662 | +} |
1663 | + |
1664 | +/*! |
1665 | + * \qmltype Menu |
1666 | + * \inqmlmodule Ubuntu.Components |
1667 | + * \ingroup ubuntu |
1668 | + * \brief Menu defines a context menu or submenu structure of a MenuBar |
1669 | + * |
1670 | + * Example usage: |
1671 | + * \qml |
1672 | + * import QtQuick 2.4 |
1673 | + * import Ubuntu.Components 1.3 |
1674 | + * Menu { |
1675 | + * text: "&File" |
1676 | + * |
1677 | + * MenuGroup { |
1678 | + * Action { |
1679 | + * text: "&New" |
1680 | + * shortcut: "Ctrl+N" |
1681 | + * } |
1682 | + * |
1683 | + * Action { |
1684 | + * text: "&Open" |
1685 | + * shortcut: "Ctrl+O" |
1686 | + * } |
1687 | + * } |
1688 | + * |
1689 | + * Menu { |
1690 | + * text: "Recent Files" |
1691 | + * |
1692 | + * ActionList { |
1693 | + * Action { text: "1.txt" } |
1694 | + * Action { text: "2.txt" } |
1695 | + * Action { text: "3.txt" } |
1696 | + * } |
1697 | + * } |
1698 | + * |
1699 | + * Action { |
1700 | + * action: Action { |
1701 | + * text: "E&xit" |
1702 | + * shortcut: "Ctrl+X" |
1703 | + * } |
1704 | + * } |
1705 | + * } |
1706 | + * \endqml |
1707 | + */ |
1708 | +Menu::Menu(QObject *parent) |
1709 | + : UCAction(parent) |
1710 | + , d_ptr(new MenuPrivate(this)) |
1711 | +{ |
1712 | + Q_D(Menu); |
1713 | + |
1714 | + connect(this, SIGNAL(enabledChanged()), this, SLOT(_q_updateEnabled())); |
1715 | + connect(this, SIGNAL(textChanged()), this, SLOT(_q_updateText())); |
1716 | + connect(this, SIGNAL(iconNameChanged()), this, SLOT(_q_updateIcon())); |
1717 | + connect(this, SIGNAL(iconSourceChanged()), this, SLOT(_q_updateIcon())); |
1718 | + connect(this, SIGNAL(visibleChanged()), this, SLOT(_q_updateVisible())); |
1719 | +} |
1720 | + |
1721 | +Menu::~Menu() |
1722 | +{ |
1723 | +} |
1724 | + |
1725 | +/*! |
1726 | + * \qmlproperty list<Object> Menu::data |
1727 | + * \default |
1728 | + * List of objects representing menu items within the menu. |
1729 | + * |
1730 | + * Currently supports Menu, Action, AcionList & MenuGroup objects. |
1731 | + * \note Item object which do not support platformItem will not be exported for native menus. |
1732 | + */ |
1733 | +QQmlListProperty<QObject> Menu::data() |
1734 | +{ |
1735 | + Q_D(Menu); |
1736 | + return QQmlListProperty<QObject>(this, d, |
1737 | + &MenuPrivate::data_append, |
1738 | + &MenuPrivate::data_count, |
1739 | + &MenuPrivate::data_at, |
1740 | + &MenuPrivate::data_clear); |
1741 | +} |
1742 | + |
1743 | +/*! |
1744 | + * \qmlmethod Menu::appendObject(object o) |
1745 | + * Add a object tto the menu |
1746 | + */ |
1747 | +void Menu::appendObject(QObject *o) |
1748 | +{ |
1749 | + Q_D(Menu); |
1750 | + |
1751 | + insertObject(d->m_data.count(), o); |
1752 | +} |
1753 | + |
1754 | +/*! |
1755 | + * \qmlmethod Menu::insertObject(int index, object o) |
1756 | + * Inserts an item at the index in the menu. |
1757 | + * |
1758 | + * Currently supports Menu, Action, AcionList & MenuGroup objects. |
1759 | + * \note Item object which do not support platformItem will not be exported for native menus. |
1760 | + */ |
1761 | +void Menu::insertObject(int index, QObject *o) |
1762 | +{ |
1763 | + Q_D(Menu); |
1764 | + d->insertObject(index, o); |
1765 | +} |
1766 | + |
1767 | +/*! |
1768 | + * \qmlmethod Menu::removeObject(object o) |
1769 | + * Removes the item from the menu. |
1770 | + */ |
1771 | +void Menu::removeObject(QObject *o) |
1772 | +{ |
1773 | + Q_D(Menu); |
1774 | + qCDebug(ucMenu) << "Menu::removeObject" << o; |
1775 | + |
1776 | + d->removeObject(o); |
1777 | +} |
1778 | + |
1779 | +QPlatformMenu* Menu::platformMenu() const |
1780 | +{ |
1781 | + Q_D(const Menu); |
1782 | + return d->m_platformMenu; |
1783 | +} |
1784 | + |
1785 | +/*! |
1786 | + * \qmlmethod Menu::show(point point) |
1787 | + * Show the menu popup at the given point |
1788 | + */ |
1789 | +void Menu::show(const QPoint &point) |
1790 | +{ |
1791 | + Q_D(Menu); |
1792 | + qCDebug(ucMenu, "Menu::popup(%s, point(%d,%d))", qPrintable(text()), point.x(), point.y()); |
1793 | + |
1794 | + if (d->m_platformMenu) { |
1795 | + d->m_platformMenu->showPopup(findWindowForObject(this), QRect(point, QSize()), Q_NULLPTR); |
1796 | + } |
1797 | +} |
1798 | + |
1799 | +/*! |
1800 | + * \qmlmethod Menu::dismiss() |
1801 | + * Dismiss and destroy the menu popup. |
1802 | + */ |
1803 | +void Menu::dismiss() |
1804 | +{ |
1805 | + Q_D(Menu); |
1806 | + qCDebug(ucMenu, "Menu::dismiss(%s)", qPrintable(text())); |
1807 | + |
1808 | + if (d->m_platformMenu) { |
1809 | + d->m_platformMenu->dismiss(); |
1810 | + } |
1811 | +} |
1812 | + |
1813 | + |
1814 | +PlatformItemWrapper::PlatformItemWrapper(QObject *target, Menu* menu) |
1815 | + : QObject(menu) |
1816 | + , m_target(target) |
1817 | + , m_menu(menu) |
1818 | + , m_platformItem(menu->platformMenu() ? menu->platformMenu()->createMenuItem() : Q_NULLPTR) |
1819 | + , m_platformItemSeparator(Q_NULLPTR) |
1820 | + , m_inserted(false) |
1821 | +{ |
1822 | + if (Menu* menu = qobject_cast<Menu*>(m_target)) { |
1823 | + if (m_platformItem) { |
1824 | + m_platformItem->setMenu(menu->platformMenu()); |
1825 | + } |
1826 | + |
1827 | + connect(menu, &Menu::visibleChanged, this, &PlatformItemWrapper::updateVisible); |
1828 | + connect(menu, &Menu::textChanged, this, &PlatformItemWrapper::updateText); |
1829 | + connect(menu, &Menu::enabledChanged, this, &PlatformItemWrapper::updateEnabled); |
1830 | + connect(menu, &Menu::iconSourceChanged, this, &PlatformItemWrapper::updateIcon); |
1831 | + connect(menu, &Menu::iconNameChanged, this, &PlatformItemWrapper::updateIcon); |
1832 | + |
1833 | + } else if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
1834 | + |
1835 | + connect(action, &UCAction::visibleChanged, this, &PlatformItemWrapper::updateVisible); |
1836 | + connect(action, &UCAction::textChanged, this, &PlatformItemWrapper::updateText); |
1837 | + connect(action, &UCAction::enabledChanged, this, &PlatformItemWrapper::updateEnabled); |
1838 | + connect(action, &UCAction::iconSourceChanged, this, &PlatformItemWrapper::updateIcon); |
1839 | + connect(action, &UCAction::iconNameChanged, this, &PlatformItemWrapper::updateIcon); |
1840 | + connect(action, &UCAction::shortcutChanged, this, &PlatformItemWrapper::updateShortcut); |
1841 | + connect(action, &UCAction::checkableChanged, this, &PlatformItemWrapper::updateCheck); |
1842 | + connect(action, &UCAction::toggled, this, &PlatformItemWrapper::updateCheck); |
1843 | + |
1844 | + if (m_platformItem) { |
1845 | + // Connect to activate (with inversion for checkables) |
1846 | + connect(m_platformItem, &QPlatformMenuItem::activated, action, [action]() { |
1847 | + action->trigger(); |
1848 | + }); |
1849 | + } |
1850 | + |
1851 | + } |
1852 | + syncPlatformItem(); |
1853 | +} |
1854 | + |
1855 | +PlatformItemWrapper::~PlatformItemWrapper() |
1856 | +{ |
1857 | + if (m_inserted && m_menu && m_menu->platformMenu()) { |
1858 | + m_menu->platformMenu()->removeMenuItem(m_platformItem); |
1859 | + if (m_platformItemSeparator) { |
1860 | + m_menu->platformMenu()->removeMenuItem(m_platformItemSeparator); |
1861 | + delete m_platformItemSeparator; |
1862 | + } |
1863 | + } |
1864 | + delete m_platformItem; |
1865 | +} |
1866 | + |
1867 | +void PlatformItemWrapper::insert(int index, bool withSeparator) |
1868 | +{ |
1869 | + if (m_inserted) return; |
1870 | + qCDebug(ucMenu).nospace() << " PlatformItemWrapper::insert(menu=" << m_menu |
1871 | + << ", index=" << index |
1872 | + << ", object=" << m_target << ")"; |
1873 | + |
1874 | + auto platformMenu = m_menu->platformMenu(); |
1875 | + if (!platformMenu) return; |
1876 | + if (!m_platformItem) return; |
1877 | + |
1878 | + QPlatformMenuItem* before = platformMenu->menuItemAt(index); |
1879 | + platformMenu->insertMenuItem(m_platformItem, before); |
1880 | + m_inserted = true; |
1881 | + |
1882 | + if (withSeparator) setSeparator(); |
1883 | +} |
1884 | + |
1885 | +void PlatformItemWrapper::remove() |
1886 | +{ |
1887 | + if (!m_inserted) return; |
1888 | + qCDebug(ucMenu).nospace() << " PlatformItemWrapper::remove(menu=" << m_menu |
1889 | + << ", object=" << m_target << ")"; |
1890 | + |
1891 | + auto platformMenu = m_menu->platformMenu(); |
1892 | + if (!platformMenu) return; |
1893 | + if (!m_platformItem) return; |
1894 | + |
1895 | + platformMenu->removeMenuItem(m_platformItem); |
1896 | + m_inserted = false; |
1897 | + |
1898 | + if (m_platformItemSeparator) { |
1899 | + qCDebug(ucMenu).nospace() << " PlatformItemWrapper::removeSeparator(menu=" << m_menu |
1900 | + << ", object=" << m_target << ")"; |
1901 | + platformMenu->removeMenuItem(m_platformItemSeparator); |
1902 | + delete m_platformItemSeparator; |
1903 | + m_platformItemSeparator = Q_NULLPTR; |
1904 | + } |
1905 | +} |
1906 | + |
1907 | +void PlatformItemWrapper::setSeparator() |
1908 | +{ |
1909 | + // already created |
1910 | + if (m_platformItemSeparator) return; |
1911 | + // not inserted yet. |
1912 | + if (!m_inserted) return; |
1913 | + |
1914 | + auto platformMenu = m_menu->platformMenu(); |
1915 | + if (!platformMenu) return; |
1916 | + |
1917 | + // insert separator before? |
1918 | + m_platformItemSeparator = platformMenu->createMenuItem(); |
1919 | + if (m_platformItemSeparator) { |
1920 | + qCDebug(ucMenu).nospace() << " PlatformItemWrapper::setSeparator(menu=" << m_menu |
1921 | + << ", object=" << m_target << ")"; |
1922 | + |
1923 | + m_platformItemSeparator->setIsSeparator(true); |
1924 | + platformMenu->insertMenuItem(m_platformItemSeparator, m_platformItem); |
1925 | + } |
1926 | +} |
1927 | + |
1928 | +void PlatformItemWrapper::updateVisible() |
1929 | +{ |
1930 | + if (Menu* menu = qobject_cast<Menu*>(m_target)) { |
1931 | + if (m_platformItem) m_platformItem->setVisible(menu->visible()); |
1932 | + if (menu->platformMenu()) menu->platformMenu()->setVisible(menu->visible()); |
1933 | + } else if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
1934 | + if (m_platformItem) m_platformItem->setVisible(action->visible()); |
1935 | + } |
1936 | +} |
1937 | + |
1938 | +void PlatformItemWrapper::updateEnabled() |
1939 | +{ |
1940 | + if (Menu* menu = qobject_cast<Menu*>(m_target)) { |
1941 | + if (m_platformItem) m_platformItem->setEnabled(menu->isEnabled()); |
1942 | + if (menu->platformMenu()) menu->platformMenu()->setEnabled(menu->isEnabled()); |
1943 | + } else if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
1944 | + if (m_platformItem) m_platformItem->setEnabled(action->isEnabled()); |
1945 | + } |
1946 | +} |
1947 | + |
1948 | +void PlatformItemWrapper::updateText() |
1949 | +{ |
1950 | + if (Menu* menu = qobject_cast<Menu*>(m_target)) { |
1951 | + if (m_platformItem) m_platformItem->setText(menu->text()); |
1952 | + if (menu->platformMenu()) menu->platformMenu()->setText(menu->text()); |
1953 | + } else if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
1954 | + if (m_platformItem) m_platformItem->setText(action->text()); |
1955 | + } |
1956 | +} |
1957 | + |
1958 | +void PlatformItemWrapper::updateIcon() |
1959 | +{ |
1960 | + QIcon icon; |
1961 | + if (Menu* menu = qobject_cast<Menu*>(m_target)) { |
1962 | + |
1963 | + if (!menu->iconSource().isEmpty()) { |
1964 | + icon = QIcon(menu->iconSource().path()); |
1965 | + } else if (!menu->iconName().isEmpty()) { |
1966 | + icon = QIcon::fromTheme(menu->iconName()); |
1967 | + } |
1968 | + if (menu->platformMenu()) menu->platformMenu()->setIcon(icon); |
1969 | + |
1970 | + } else if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
1971 | + |
1972 | + if (!action->iconSource().isEmpty()) { |
1973 | + icon = QIcon(action->iconSource().path()); |
1974 | + } else if (!action->iconName().isEmpty()) { |
1975 | + icon = QIcon::fromTheme(action->iconName()); |
1976 | + } |
1977 | + } |
1978 | + |
1979 | + if (m_platformItem) { m_platformItem->setIcon(icon); } |
1980 | +} |
1981 | + |
1982 | + |
1983 | +inline QKeySequence sequenceFromVariant(const QVariant& variant) |
1984 | +{ |
1985 | + if (variant.type() == QVariant::Int) { |
1986 | + return static_cast<QKeySequence::StandardKey>(variant.toInt()); |
1987 | + } |
1988 | + if (variant.type() == QVariant::String) { |
1989 | + return QKeySequence::fromString(variant.toString()); |
1990 | + } |
1991 | + return QKeySequence(); |
1992 | +} |
1993 | + |
1994 | +void PlatformItemWrapper::updateShortcut() |
1995 | +{ |
1996 | + if (!m_platformItem) return; |
1997 | + |
1998 | + if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
1999 | + m_platformItem->setShortcut(sequenceFromVariant(action->shortcut())); |
2000 | + } |
2001 | +} |
2002 | + |
2003 | +void PlatformItemWrapper::updateCheck() |
2004 | +{ |
2005 | + if (!m_platformItem) return; |
2006 | + |
2007 | + if (UCAction* action = qobject_cast<UCAction*>(m_target)) { |
2008 | + bool checkable = action->isCheckable(); |
2009 | + m_platformItem->setCheckable(checkable); |
2010 | + m_platformItem->setChecked(checkable && action->isChecked()); |
2011 | + } |
2012 | +} |
2013 | + |
2014 | +void PlatformItemWrapper::syncPlatformItem() |
2015 | +{ |
2016 | + updateVisible(); |
2017 | + updateEnabled(); |
2018 | + updateText(); |
2019 | + updateIcon(); |
2020 | + updateShortcut(); |
2021 | + updateCheck(); |
2022 | + |
2023 | + if (m_menu->platformMenu() && m_platformItem) { |
2024 | + m_menu->platformMenu()->syncMenuItem(m_platformItem); |
2025 | + } |
2026 | +} |
2027 | + |
2028 | +UT_NAMESPACE_END |
2029 | + |
2030 | +#include "moc_menu_p.cpp" |
2031 | |
2032 | === added file 'src/Ubuntu/UbuntuToolkit/menu_p.h' |
2033 | --- src/Ubuntu/UbuntuToolkit/menu_p.h 1970-01-01 00:00:00 +0000 |
2034 | +++ src/Ubuntu/UbuntuToolkit/menu_p.h 2016-08-31 08:57:26 +0000 |
2035 | @@ -0,0 +1,75 @@ |
2036 | +/* |
2037 | + * Copyright 2016 Canonical Ltd. |
2038 | + * |
2039 | + * This program is free software; you can redistribute it and/or modify |
2040 | + * it under the terms of the GNU Lesser General Public License as published by |
2041 | + * the Free Software Foundation; version 3. |
2042 | + * |
2043 | + * This program is distributed in the hope that it will be useful, |
2044 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2045 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2046 | + * GNU Lesser General Public License for more details. |
2047 | + * |
2048 | + * You should have received a copy of the GNU Lesser General Public License |
2049 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2050 | + * |
2051 | + */ |
2052 | + |
2053 | +#ifndef MENU_P_H |
2054 | +#define MENU_P_H |
2055 | + |
2056 | +#include <QQmlListProperty> |
2057 | +#include <QLoggingCategory> |
2058 | +#include <QPointer> |
2059 | +#include <ubuntutoolkitglobal.h> |
2060 | + |
2061 | +#include "ucaction_p.h" |
2062 | + |
2063 | +Q_DECLARE_LOGGING_CATEGORY(ucMenu); |
2064 | + |
2065 | +class QPlatformMenu; |
2066 | +class QPlatformMenuItem; |
2067 | +class QQuickItem; |
2068 | + |
2069 | +UT_NAMESPACE_BEGIN |
2070 | + |
2071 | +class MenuPrivate; |
2072 | +class MenuBar; |
2073 | +class UCAction; |
2074 | +class UBUNTUTOOLKIT_EXPORT Menu : public UCAction |
2075 | +{ |
2076 | + Q_OBJECT |
2077 | + |
2078 | + Q_PROPERTY(QQmlListProperty<QObject> data READ data FINAL) |
2079 | + Q_CLASSINFO("DefaultProperty", "data") |
2080 | + |
2081 | +public: |
2082 | + explicit Menu(QObject *parent = 0); |
2083 | + ~Menu(); |
2084 | + |
2085 | + QQmlListProperty<QObject> data(); |
2086 | + |
2087 | + Q_INVOKABLE void appendObject(QObject* obj); |
2088 | + Q_INVOKABLE void insertObject(int index, QObject* obj); |
2089 | + Q_INVOKABLE void removeObject(QObject* obj); |
2090 | + |
2091 | + QPlatformMenu *platformMenu() const; |
2092 | + |
2093 | +public Q_SLOTS: |
2094 | + void show(const QPoint& pt); |
2095 | + void dismiss(); |
2096 | + |
2097 | +private: |
2098 | + Q_DISABLE_COPY(Menu) |
2099 | + Q_DECLARE_PRIVATE(Menu) |
2100 | + QScopedPointer<MenuPrivate> d_ptr; |
2101 | + |
2102 | + Q_PRIVATE_SLOT(d_func(), void _q_updateEnabled()) |
2103 | + Q_PRIVATE_SLOT(d_func(), void _q_updateText()) |
2104 | + Q_PRIVATE_SLOT(d_func(), void _q_updateIcon()) |
2105 | + Q_PRIVATE_SLOT(d_func(), void _q_updateVisible()) |
2106 | +}; |
2107 | + |
2108 | +UT_NAMESPACE_END |
2109 | + |
2110 | +#endif // MENU_P_H |
2111 | |
2112 | === added file 'src/Ubuntu/UbuntuToolkit/menu_p_p.h' |
2113 | --- src/Ubuntu/UbuntuToolkit/menu_p_p.h 1970-01-01 00:00:00 +0000 |
2114 | +++ src/Ubuntu/UbuntuToolkit/menu_p_p.h 2016-08-31 08:57:26 +0000 |
2115 | @@ -0,0 +1,95 @@ |
2116 | +/* |
2117 | + * Copyright 2016 Canonical Ltd. |
2118 | + * |
2119 | + * This program is free software; you can redistribute it and/or modify |
2120 | + * it under the terms of the GNU Lesser General Public License as published by |
2121 | + * the Free Software Foundation; version 3. |
2122 | + * |
2123 | + * This program is distributed in the hope that it will be useful, |
2124 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2125 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2126 | + * GNU Lesser General Public License for more details. |
2127 | + * |
2128 | + * You should have received a copy of the GNU Lesser General Public License |
2129 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2130 | + * |
2131 | + */ |
2132 | + |
2133 | +#ifndef MENU_P_P_H |
2134 | +#define MENU_P_P_H |
2135 | + |
2136 | +#include "menu_p.h" |
2137 | +#include <ubuntutoolkitglobal.h> |
2138 | + |
2139 | +class QObject; |
2140 | +class QQmlComponent; |
2141 | + |
2142 | +UT_NAMESPACE_BEGIN |
2143 | + |
2144 | +class UCAction; |
2145 | +class Menu; |
2146 | +class PlatformItemWrapper; |
2147 | + |
2148 | +class MenuPrivate |
2149 | +{ |
2150 | + Q_DECLARE_PUBLIC(Menu) |
2151 | +public: |
2152 | + MenuPrivate(Menu *qq); |
2153 | + virtual ~MenuPrivate(); |
2154 | + |
2155 | + void insertObject(int index, QObject *obj); |
2156 | + void removeObject(QObject *obj); |
2157 | + |
2158 | + void _q_updateEnabled(); |
2159 | + void _q_updateText(); |
2160 | + void _q_updateIcon(); |
2161 | + void _q_updateVisible(); |
2162 | + |
2163 | + static void data_append(QQmlListProperty<QObject> *prop, QObject *o); |
2164 | + static int data_count(QQmlListProperty<QObject> *prop); |
2165 | + static QObject *data_at(QQmlListProperty<QObject> *prop, int index); |
2166 | + static void data_clear(QQmlListProperty<QObject> *prop); |
2167 | + |
2168 | + Menu* q_ptr; |
2169 | + QPlatformMenu* m_platformMenu; |
2170 | + UCAction* m_action; |
2171 | + |
2172 | + QHash<QObject*, PlatformItemWrapper*> m_platformItems; |
2173 | + QMultiHash<QObject*, QObject*> m_dataPlatformObjectMap; |
2174 | + QVector<QObject*> m_data; |
2175 | +}; |
2176 | + |
2177 | +class PlatformItemWrapper : public QObject |
2178 | +{ |
2179 | + Q_OBJECT |
2180 | +public: |
2181 | + PlatformItemWrapper(QObject *target, Menu* menu); |
2182 | + ~PlatformItemWrapper(); |
2183 | + |
2184 | + void insert(int index, bool withSeparator); |
2185 | + void remove(); |
2186 | + void setSeparator(); |
2187 | + |
2188 | + bool hasSeparator() const { return m_platformItemSeparator != Q_NULLPTR; } |
2189 | + |
2190 | +public Q_SLOTS: |
2191 | + void updateVisible(); |
2192 | + void updateEnabled(); |
2193 | + void updateText(); |
2194 | + void updateIcon(); |
2195 | + void updateShortcut(); |
2196 | + void updateCheck(); |
2197 | + |
2198 | +private: |
2199 | + void syncPlatformItem(); |
2200 | + |
2201 | + QObject* m_target; |
2202 | + QPointer<Menu> m_menu; |
2203 | + QPlatformMenuItem* m_platformItem; |
2204 | + QPlatformMenuItem* m_platformItemSeparator; |
2205 | + bool m_inserted; |
2206 | +}; |
2207 | + |
2208 | +UT_NAMESPACE_END |
2209 | + |
2210 | +#endif // MENU_P_P_H |
2211 | |
2212 | === added file 'src/Ubuntu/UbuntuToolkit/menubar.cpp' |
2213 | --- src/Ubuntu/UbuntuToolkit/menubar.cpp 1970-01-01 00:00:00 +0000 |
2214 | +++ src/Ubuntu/UbuntuToolkit/menubar.cpp 2016-08-31 08:57:26 +0000 |
2215 | @@ -0,0 +1,340 @@ |
2216 | +/* |
2217 | + * Copyright 2016 Canonical Ltd. |
2218 | + * |
2219 | + * This program is free software; you can redistribute it and/or modify |
2220 | + * it under the terms of the GNU Lesser General Public License as published by |
2221 | + * the Free Software Foundation; version 3. |
2222 | + * |
2223 | + * This program is distributed in the hope that it will be useful, |
2224 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2225 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2226 | + * GNU Lesser General Public License for more details. |
2227 | + * |
2228 | + * You should have received a copy of the GNU Lesser General Public License |
2229 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2230 | + * |
2231 | + */ |
2232 | + |
2233 | +#include "menubar_p.h" |
2234 | +#include "menubar_p_p.h" |
2235 | + |
2236 | +// Qt |
2237 | +#include <QQuickItem> |
2238 | +#include <QQuickWindow> |
2239 | +#include <QLoggingCategory> |
2240 | +#include <private/qguiapplication_p.h> |
2241 | +#include <QtGui/qpa/qplatformtheme.h> |
2242 | +#include <QtGui/qpa/qplatformmenu.h> |
2243 | + |
2244 | +UT_NAMESPACE_BEGIN |
2245 | + |
2246 | +MenuBarPrivate::MenuBarPrivate(MenuBar *qq) |
2247 | + : q_ptr(qq) |
2248 | +{ |
2249 | + m_platformBar = QGuiApplicationPrivate::platformTheme()->createPlatformMenuBar(); |
2250 | +} |
2251 | + |
2252 | +MenuBarPrivate::~MenuBarPrivate() |
2253 | +{ |
2254 | + qDeleteAll(m_platformMenus); |
2255 | + m_platformMenus.clear(); |
2256 | + |
2257 | + delete m_platformBar; |
2258 | + m_platformBar = Q_NULLPTR; |
2259 | +} |
2260 | + |
2261 | +void MenuBarPrivate::insertMenu(int index, Menu* menu) |
2262 | +{ |
2263 | + Q_Q(MenuBar); |
2264 | + Menu* prevMenu = m_menus.count() > index ? m_menus[index] : Q_NULLPTR; |
2265 | + |
2266 | + m_menus.insert(index, menu); |
2267 | + |
2268 | + // add to platform |
2269 | + if (m_platformBar && menu->platformMenu()) { |
2270 | + auto platformWrapper = new PlatformMenuWrapper(menu, q); |
2271 | + platformWrapper->insert(prevMenu ? prevMenu->platformMenu() : Q_NULLPTR); |
2272 | + m_platformMenus[menu] = platformWrapper; |
2273 | + |
2274 | + QObject::connect(menu, &QObject::destroyed, q, [this](QObject* object) { |
2275 | + if (m_platformMenus.contains(object)) { |
2276 | + PlatformMenuWrapper* wrapper = m_platformMenus.take(object); |
2277 | + wrapper->remove(); |
2278 | + delete wrapper; |
2279 | + } |
2280 | + }); |
2281 | + } |
2282 | +} |
2283 | + |
2284 | +void MenuBarPrivate::removeMenu(Menu *menu) |
2285 | +{ |
2286 | + m_menus.removeOne(menu); |
2287 | + |
2288 | + if (m_platformBar) { |
2289 | + if (m_platformMenus.contains(menu)) { |
2290 | + PlatformMenuWrapper* wrapper = m_platformMenus.take(menu); |
2291 | + wrapper->remove(); |
2292 | + delete wrapper; |
2293 | + } |
2294 | + } |
2295 | +} |
2296 | + |
2297 | +void MenuBarPrivate::menu_append(QQmlListProperty<Menu> *prop, Menu *o) |
2298 | +{ |
2299 | + MenuBarPrivate *q = static_cast<MenuBarPrivate *>(prop->data); |
2300 | + // menubar is the menus parent |
2301 | + o->setParent(prop->object); |
2302 | + q->insertMenu(q->m_menus.count(), o); |
2303 | +} |
2304 | + |
2305 | +int MenuBarPrivate::menu_count(QQmlListProperty<Menu> *prop) |
2306 | +{ |
2307 | + MenuBarPrivate *p = static_cast<MenuBarPrivate *>(prop->data); |
2308 | + return p->m_menus.count(); |
2309 | +} |
2310 | + |
2311 | +Menu *MenuBarPrivate::menu_at(QQmlListProperty<Menu> *prop, int index) |
2312 | +{ |
2313 | + MenuBarPrivate *p = static_cast<MenuBarPrivate *>(prop->data); |
2314 | + return p->m_menus.value(index); |
2315 | +} |
2316 | + |
2317 | +void MenuBarPrivate::menu_clear(QQmlListProperty<Menu> *prop) |
2318 | +{ |
2319 | + MenuBarPrivate *p = static_cast<MenuBarPrivate *>(prop->data); |
2320 | + p->m_menus.clear(); |
2321 | +} |
2322 | + |
2323 | +/*! |
2324 | + * \qmltype MenuBar |
2325 | + * \inqmlmodule Ubuntu.Components 1.3 |
2326 | + * \ingroup ubuntu |
2327 | + * \brief MenuBar defines an application menu bar structure |
2328 | + * |
2329 | + * Example usage: |
2330 | + * \qml |
2331 | + * import QtQuick 2.4 |
2332 | + * import Ubuntu.Components 1.3 |
2333 | + * MainView { |
2334 | + * MenuBar { |
2335 | + * Menu { |
2336 | + * text: "_File" |
2337 | + * |
2338 | + * MenuItem { |
2339 | + * text: "_New" |
2340 | + * shortcut: "Ctrl+N" |
2341 | + * } |
2342 | + * |
2343 | + * MenuItem { |
2344 | + * text: "_Open" |
2345 | + * shortcut: "Ctrl+O" |
2346 | + * } |
2347 | + * |
2348 | + * MenuSeparator {} |
2349 | + * |
2350 | + * MenuItem { |
2351 | + * action: exitAction |
2352 | + * } |
2353 | + * } |
2354 | + * |
2355 | + * Menu { |
2356 | + * text: "_Edit" |
2357 | + * |
2358 | + * MenuItem { |
2359 | + * text: "_Undo" |
2360 | + * iconSource: "image://theme/undo" |
2361 | + * } |
2362 | + * } |
2363 | + * Menu { |
2364 | + * text: "_Window" |
2365 | + * |
2366 | + * MenuItem { |
2367 | + * text: "Fullscreen" |
2368 | + * checkable: true |
2369 | + * checked: false |
2370 | + * } |
2371 | + * } |
2372 | + * } |
2373 | + * Action { |
2374 | + * id: boundAction |
2375 | + * text: "E_xit" |
2376 | + * onTriggered: { |
2377 | + * Qt.quit(); |
2378 | + * } |
2379 | + * } |
2380 | + * } |
2381 | + * \endqml |
2382 | + */ |
2383 | +MenuBar::MenuBar(QObject *parent) |
2384 | + : QObject(parent) |
2385 | + , d_ptr(new MenuBarPrivate(this)) |
2386 | +{ |
2387 | +} |
2388 | + |
2389 | +MenuBar::~MenuBar() |
2390 | +{ |
2391 | +} |
2392 | + |
2393 | +/*! |
2394 | + * \qmlmethod void MenuBar::appendMenu(Menu menu) |
2395 | + * Append a Menu to the MenuBar |
2396 | + */ |
2397 | +void MenuBar::appendMenu(Menu *menu) |
2398 | +{ |
2399 | + Q_D(MenuBar); |
2400 | + insertMenu(d->m_menus.count(), menu); |
2401 | +} |
2402 | + |
2403 | +/*! |
2404 | + * \qmlmethod void MenuBar::insertMenu(int index, Menu menu) |
2405 | + * Insert a Menu to the MenuBar at the specified position |
2406 | + */ |
2407 | +void MenuBar::insertMenu(int index, Menu *menu) |
2408 | +{ |
2409 | + Q_D(MenuBar); |
2410 | + if (!menu) return; |
2411 | + |
2412 | + d->insertMenu(index, menu); |
2413 | + Q_EMIT menusChanged(); |
2414 | +} |
2415 | + |
2416 | +/*! |
2417 | + * \qmlmethod void MenuBar::removeMenu(Menu menu) |
2418 | + * Remove a Menu from the MenuBar |
2419 | + */ |
2420 | +void MenuBar::removeMenu(Menu *menu) |
2421 | +{ |
2422 | + Q_D(MenuBar); |
2423 | + if (!menu) return; |
2424 | + |
2425 | + d->removeMenu(menu); |
2426 | + Q_EMIT menusChanged(); |
2427 | +} |
2428 | +/*! |
2429 | + * \qmlproperty list<Menu> MenuBar::menus |
2430 | + * \default |
2431 | + * List of Menus in this MenuBar. |
2432 | + */ |
2433 | +QQmlListProperty<Menu> MenuBar::menus() |
2434 | +{ |
2435 | + Q_D(MenuBar); |
2436 | + return QQmlListProperty<Menu>(this, d, |
2437 | + &MenuBarPrivate::menu_append, |
2438 | + &MenuBarPrivate::menu_count, |
2439 | + &MenuBarPrivate::menu_at, |
2440 | + &MenuBarPrivate::menu_clear); |
2441 | +} |
2442 | + |
2443 | +QPlatformMenuBar *MenuBar::platformMenuBar() const |
2444 | +{ |
2445 | + Q_D(const MenuBar); |
2446 | + return d->m_platformBar; |
2447 | +} |
2448 | + |
2449 | +void MenuBar::classBegin() |
2450 | +{ |
2451 | +} |
2452 | + |
2453 | +void MenuBar::componentComplete() |
2454 | +{ |
2455 | + Q_D(MenuBar); |
2456 | + |
2457 | + auto parentItem = qobject_cast<QQuickItem*>(parent()); |
2458 | + if (parentItem && d->m_platformBar) { |
2459 | + d->m_platformBar->handleReparent(parentItem->window()); |
2460 | + } |
2461 | +} |
2462 | + |
2463 | + |
2464 | +PlatformMenuWrapper::PlatformMenuWrapper(Menu *target, MenuBar* bar) |
2465 | + : QObject(bar) |
2466 | + , m_bar(bar) |
2467 | + , m_target(target) |
2468 | + , m_inserted(false) |
2469 | +{ |
2470 | + connect(m_target, &Menu::visibleChanged, this, &PlatformMenuWrapper::updateVisible); |
2471 | + connect(m_target, &Menu::textChanged, this, &PlatformMenuWrapper::updateText); |
2472 | + connect(m_target, &Menu::enabledChanged, this, &PlatformMenuWrapper::updateEnabled); |
2473 | + connect(m_target, &Menu::iconSourceChanged, this, &PlatformMenuWrapper::updateIcon); |
2474 | + connect(m_target, &Menu::iconNameChanged, this, &PlatformMenuWrapper::updateIcon); |
2475 | + |
2476 | + syncPlatformMenu(); |
2477 | +} |
2478 | + |
2479 | +PlatformMenuWrapper::~PlatformMenuWrapper() |
2480 | +{ |
2481 | + if (m_inserted && m_bar && m_bar->platformMenuBar()) { |
2482 | + if (m_target && m_target->platformMenu()) { |
2483 | + m_bar->platformMenuBar()->removeMenu(m_target->platformMenu()); |
2484 | + } |
2485 | + } |
2486 | +} |
2487 | + |
2488 | +void PlatformMenuWrapper::insert(QPlatformMenu *before) |
2489 | +{ |
2490 | + if (m_inserted) return; |
2491 | + qCDebug(ucMenu).nospace() << " PlatformMenuWrapper::insert(bar=" << m_bar |
2492 | + << ", before=" << before |
2493 | + << ", menu=" << m_target << ")"; |
2494 | + |
2495 | + auto platformBar = m_bar->platformMenuBar(); |
2496 | + if (!platformBar) return; |
2497 | + auto platformMenu = m_target->platformMenu(); |
2498 | + if (!platformMenu) return; |
2499 | + |
2500 | + platformBar->insertMenu(platformMenu, before); |
2501 | + m_inserted = true; |
2502 | +} |
2503 | + |
2504 | +void PlatformMenuWrapper::remove() |
2505 | +{ |
2506 | + if (!m_inserted) return; |
2507 | + qCDebug(ucMenu).nospace() << " PlatformMenuWrapper::remove(bar=" << m_bar |
2508 | + << ", menu=" << m_target << ")"; |
2509 | + |
2510 | + auto platformBar = m_bar->platformMenuBar(); |
2511 | + if (!platformBar) return; |
2512 | + auto platformMenu = m_target->platformMenu(); |
2513 | + if (!platformMenu) return; |
2514 | + |
2515 | + platformBar->removeMenu(platformMenu); |
2516 | + m_inserted = false; |
2517 | +} |
2518 | + |
2519 | +void PlatformMenuWrapper::updateVisible() |
2520 | +{ |
2521 | + if (m_target->platformMenu()) m_target->platformMenu()->setVisible(m_target->visible()); |
2522 | +} |
2523 | + |
2524 | +void PlatformMenuWrapper::updateEnabled() |
2525 | +{ |
2526 | + if (m_target->platformMenu()) m_target->platformMenu()->setEnabled(m_target->isEnabled()); |
2527 | +} |
2528 | + |
2529 | +void PlatformMenuWrapper::updateText() |
2530 | +{ |
2531 | + if (m_target->platformMenu()) m_target->platformMenu()->setText(m_target->text()); |
2532 | +} |
2533 | + |
2534 | +void PlatformMenuWrapper::updateIcon() |
2535 | +{ |
2536 | + QIcon icon; |
2537 | + if (!m_target->iconSource().isEmpty()) { |
2538 | + icon = QIcon(m_target->iconSource().path()); |
2539 | + } else if (!m_target->iconName().isEmpty()) { |
2540 | + icon = QIcon::fromTheme(m_target->iconName()); |
2541 | + } |
2542 | + if (m_target->platformMenu()) m_target->platformMenu()->setIcon(icon); |
2543 | +} |
2544 | + |
2545 | +void PlatformMenuWrapper::syncPlatformMenu() |
2546 | +{ |
2547 | + updateVisible(); |
2548 | + updateEnabled(); |
2549 | + updateText(); |
2550 | + updateIcon(); |
2551 | +} |
2552 | + |
2553 | +UT_NAMESPACE_END |
2554 | + |
2555 | +#include "moc_menubar_p.cpp" |
2556 | |
2557 | === added file 'src/Ubuntu/UbuntuToolkit/menubar_p.h' |
2558 | --- src/Ubuntu/UbuntuToolkit/menubar_p.h 1970-01-01 00:00:00 +0000 |
2559 | +++ src/Ubuntu/UbuntuToolkit/menubar_p.h 2016-08-31 08:57:26 +0000 |
2560 | @@ -0,0 +1,65 @@ |
2561 | +/* |
2562 | + * Copyright 2016 Canonical Ltd. |
2563 | + * |
2564 | + * This program is free software; you can redistribute it and/or modify |
2565 | + * it under the terms of the GNU Lesser General Public License as published by |
2566 | + * the Free Software Foundation; version 3. |
2567 | + * |
2568 | + * This program is distributed in the hope that it will be useful, |
2569 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2570 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2571 | + * GNU Lesser General Public License for more details. |
2572 | + * |
2573 | + * You should have received a copy of the GNU Lesser General Public License |
2574 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2575 | + * |
2576 | + */ |
2577 | + |
2578 | +#ifndef MENUBAR_P_H |
2579 | +#define MENUBAR_P_H |
2580 | + |
2581 | +#include "menu_p.h" |
2582 | +#include <ubuntutoolkitglobal.h> |
2583 | + |
2584 | +#include <QQmlParserStatus> |
2585 | + |
2586 | +class QPlatformMenuBar; |
2587 | + |
2588 | +UT_NAMESPACE_BEGIN |
2589 | + |
2590 | +class MenuBarPrivate; |
2591 | +class UBUNTUTOOLKIT_EXPORT MenuBar : public QObject, public QQmlParserStatus |
2592 | +{ |
2593 | + Q_OBJECT |
2594 | + Q_INTERFACES(QQmlParserStatus) |
2595 | + |
2596 | + Q_PROPERTY(QQmlListProperty<UT_PREPEND_NAMESPACE(Menu)> menus READ menus NOTIFY menusChanged FINAL) |
2597 | + Q_CLASSINFO("DefaultProperty", "menus") |
2598 | + |
2599 | +public: |
2600 | + explicit MenuBar(QObject *parent = 0); |
2601 | + ~MenuBar(); |
2602 | + |
2603 | + Q_INVOKABLE void appendMenu(Menu *menu); |
2604 | + Q_INVOKABLE void insertMenu(int index, Menu *menu); |
2605 | + Q_INVOKABLE void removeMenu(Menu *menu); |
2606 | + |
2607 | + QQmlListProperty<Menu> menus(); |
2608 | + |
2609 | + QPlatformMenuBar *platformMenuBar() const; |
2610 | + |
2611 | + void classBegin() Q_DECL_OVERRIDE; |
2612 | + void componentComplete() Q_DECL_OVERRIDE; |
2613 | + |
2614 | +Q_SIGNALS: |
2615 | + void menusChanged(); |
2616 | + |
2617 | +private: |
2618 | + Q_DISABLE_COPY(MenuBar) |
2619 | + Q_DECLARE_PRIVATE(MenuBar) |
2620 | + QScopedPointer<MenuBarPrivate> d_ptr; |
2621 | +}; |
2622 | + |
2623 | +UT_NAMESPACE_END |
2624 | + |
2625 | +#endif // MENUBAR_P_H |
2626 | |
2627 | === added file 'src/Ubuntu/UbuntuToolkit/menubar_p_p.h' |
2628 | --- src/Ubuntu/UbuntuToolkit/menubar_p_p.h 1970-01-01 00:00:00 +0000 |
2629 | +++ src/Ubuntu/UbuntuToolkit/menubar_p_p.h 2016-08-31 08:57:26 +0000 |
2630 | @@ -0,0 +1,78 @@ |
2631 | +/* |
2632 | + * Copyright 2016 Canonical Ltd. |
2633 | + * |
2634 | + * This program is free software; you can redistribute it and/or modify |
2635 | + * it under the terms of the GNU Lesser General Public License as published by |
2636 | + * the Free Software Foundation; version 3. |
2637 | + * |
2638 | + * This program is distributed in the hope that it will be useful, |
2639 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2640 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2641 | + * GNU Lesser General Public License for more details. |
2642 | + * |
2643 | + * You should have received a copy of the GNU Lesser General Public License |
2644 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2645 | + * |
2646 | + */ |
2647 | + |
2648 | +#ifndef MENUBAR_P_P_H |
2649 | +#define MENUBAR_P_P_H |
2650 | + |
2651 | +#include "menubar_p.h" |
2652 | +#include <ubuntutoolkitglobal.h> |
2653 | + |
2654 | +class QPlatformMenuBar; |
2655 | + |
2656 | +UT_NAMESPACE_BEGIN |
2657 | + |
2658 | +class PlatformMenuWrapper; |
2659 | + |
2660 | +class MenuBarPrivate |
2661 | +{ |
2662 | + Q_DECLARE_PUBLIC(MenuBar) |
2663 | +public: |
2664 | + MenuBarPrivate(MenuBar *qq); |
2665 | + ~MenuBarPrivate(); |
2666 | + |
2667 | + void insertMenu(int index, Menu *menu); |
2668 | + void removeMenu(Menu *menu); |
2669 | + |
2670 | + static void menu_append(QQmlListProperty<Menu> *prop, Menu *o); |
2671 | + static int menu_count(QQmlListProperty<Menu> *prop); |
2672 | + static Menu *menu_at(QQmlListProperty<Menu> *prop, int index); |
2673 | + static void menu_clear(QQmlListProperty<Menu> *prop); |
2674 | + |
2675 | + MenuBar* q_ptr; |
2676 | + QPlatformMenuBar* m_platformBar; |
2677 | + QVector<Menu*> m_menus; |
2678 | + QHash<QObject*, PlatformMenuWrapper*> m_platformMenus; |
2679 | +}; |
2680 | + |
2681 | +class PlatformMenuWrapper : public QObject |
2682 | +{ |
2683 | + Q_OBJECT |
2684 | +public: |
2685 | + PlatformMenuWrapper(Menu *target, MenuBar *bar); |
2686 | + ~PlatformMenuWrapper(); |
2687 | + |
2688 | + void insert(QPlatformMenu *before); |
2689 | + void remove(); |
2690 | + |
2691 | +public Q_SLOTS: |
2692 | + void updateVisible(); |
2693 | + void updateEnabled(); |
2694 | + void updateText(); |
2695 | + void updateIcon(); |
2696 | + |
2697 | +private: |
2698 | + void syncPlatformMenu(); |
2699 | + |
2700 | + QPointer<MenuBar> m_bar; |
2701 | + QPointer<Menu> m_target; |
2702 | + bool m_inserted; |
2703 | +}; |
2704 | + |
2705 | +UT_NAMESPACE_END |
2706 | + |
2707 | +#endif // MENUBAR_P_P_H |
2708 | + |
2709 | |
2710 | === added file 'src/Ubuntu/UbuntuToolkit/menugroup.cpp' |
2711 | --- src/Ubuntu/UbuntuToolkit/menugroup.cpp 1970-01-01 00:00:00 +0000 |
2712 | +++ src/Ubuntu/UbuntuToolkit/menugroup.cpp 2016-08-31 08:57:26 +0000 |
2713 | @@ -0,0 +1,164 @@ |
2714 | +/* |
2715 | + * Copyright 2016 Canonical Ltd. |
2716 | + * |
2717 | + * This program is free software; you can redistribute it and/or modify |
2718 | + * it under the terms of the GNU Lesser General Public License as published by |
2719 | + * the Free Software Foundation; version 3. |
2720 | + * |
2721 | + * This program is distributed in the hope that it will be useful, |
2722 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2723 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2724 | + * GNU Lesser General Public License for more details. |
2725 | + * |
2726 | + * You should have received a copy of the GNU Lesser General Public License |
2727 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2728 | + */ |
2729 | + |
2730 | +#include "menugroup_p.h" |
2731 | +#include "actionlist_p.h" |
2732 | +#include "ucaction_p.h" |
2733 | + |
2734 | +UT_NAMESPACE_BEGIN |
2735 | + |
2736 | +/*! |
2737 | + * \qmltype MenuGroup |
2738 | + * \inqmlmodule Ubuntu.Components |
2739 | + * \ingroup ubuntu |
2740 | + * \brief Logical list of items for a menu. |
2741 | + * |
2742 | + * Example usage: |
2743 | + * \qml |
2744 | + * import QtQuick 2.4 |
2745 | + * import Ubuntu.Components 1.3 |
2746 | + * Menu { |
2747 | + * text: "Edit" |
2748 | + * |
2749 | + * MenuGroup { |
2750 | + * Action { text: "Undo" } |
2751 | + * Action { text: "Redo" } |
2752 | + * } |
2753 | + * |
2754 | + * MenuGroup { |
2755 | + * Action { text: "Cut" } |
2756 | + * ActionList { |
2757 | + * Action { text: "Copy" } |
2758 | + * Action { text: "Paste" } |
2759 | + * } |
2760 | + * } |
2761 | + * |
2762 | + * MenuGroup { |
2763 | + * Action { text: "Select All" } |
2764 | + * } |
2765 | + * } |
2766 | + * \endqml |
2767 | + */ |
2768 | +/*! |
2769 | + * \qmlsignal MenuGroup::added(Object action) |
2770 | + * Signal called when a action is added to the list |
2771 | + */ |
2772 | + |
2773 | +/*! |
2774 | + * \qmlsignal MenuGroup::removed(Object action) |
2775 | + * Signal called when a action is removed from the list |
2776 | + */ |
2777 | +/*! |
2778 | + * \qmlsignal MenuGroup::changed() |
2779 | + * Signal called when the contents of the group change, |
2780 | + * including child content changes (eg. \l ActionList child add/remove) |
2781 | + */ |
2782 | +MenuGroup::MenuGroup(QObject *parent) |
2783 | + : QObject(parent) |
2784 | +{ |
2785 | +} |
2786 | + |
2787 | +/*! |
2788 | + * \qmlmethod MenuGroup::addObject(Object object) |
2789 | + * Adds an Object to the list programatically. |
2790 | + */ |
2791 | +void MenuGroup::addObject(QObject *object) |
2792 | +{ |
2793 | + if (m_data.contains(object)) { |
2794 | + return; |
2795 | + } |
2796 | + m_data.push_back(object); |
2797 | + |
2798 | + if (auto childGroup = qobject_cast<MenuGroup*>(object)) { |
2799 | + connect(childGroup, &MenuGroup::changed, this, &MenuGroup::changed); |
2800 | + } else if (auto actionList = qobject_cast<ActionList*>(object)) { |
2801 | + connect(actionList, &ActionList::added, this, &MenuGroup::changed); |
2802 | + connect(actionList, &ActionList::removed, this, &MenuGroup::changed); |
2803 | + } |
2804 | + |
2805 | + Q_EMIT added(object); |
2806 | + Q_EMIT changed(); |
2807 | +} |
2808 | + |
2809 | +/*! |
2810 | + * \qmlmethod MenuGroup::removeObject(Object object) |
2811 | + * Removes an object from the list programatically. |
2812 | + */ |
2813 | +void MenuGroup::removeObject(QObject *object) |
2814 | +{ |
2815 | + if (!object) { |
2816 | + return; |
2817 | + } |
2818 | + if (m_data.removeOne(object)) { |
2819 | + disconnect(object, 0, this, 0); |
2820 | + Q_EMIT removed(object); |
2821 | + Q_EMIT changed(); |
2822 | + } |
2823 | +} |
2824 | + |
2825 | +/*! |
2826 | + |
2827 | + * \qmlproperty list<Object> MenuGroup::data |
2828 | + * \default |
2829 | + * List of Objects in this MenuGroup |
2830 | + * Note that when you set this property, the children of the MenuGroup will be ignored, |
2831 | + * so do not set the list and define children. |
2832 | + */ |
2833 | +QQmlListProperty<QObject> MenuGroup::data() |
2834 | +{ |
2835 | + return QQmlListProperty<QObject>(this, 0, MenuGroup::append, MenuGroup::count, MenuGroup::at, MenuGroup::clear); |
2836 | +} |
2837 | + |
2838 | +const QVector<QObject*> &MenuGroup::list() const |
2839 | +{ |
2840 | + return m_data; |
2841 | +} |
2842 | + |
2843 | +void MenuGroup::append(QQmlListProperty<QObject> *list, QObject *object) |
2844 | +{ |
2845 | + MenuGroup *menuGroup = qobject_cast<MenuGroup*>(list->object); |
2846 | + if (menuGroup) { |
2847 | + menuGroup->addObject(object); |
2848 | + } |
2849 | +} |
2850 | + |
2851 | +int MenuGroup::count(QQmlListProperty<QObject> *list) |
2852 | +{ |
2853 | + MenuGroup *menuGroup = qobject_cast<MenuGroup*>(list->object); |
2854 | + if (menuGroup) { |
2855 | + return menuGroup->m_data.count(); |
2856 | + } |
2857 | + return 0; |
2858 | +} |
2859 | + |
2860 | +QObject* MenuGroup::at(QQmlListProperty<QObject> *list, int index) |
2861 | +{ |
2862 | + MenuGroup *menuGroup = qobject_cast<MenuGroup*>(list->object); |
2863 | + if (menuGroup && index >=0 && menuGroup->m_data.count() > index) { |
2864 | + return menuGroup->m_data[index]; |
2865 | + } |
2866 | + return Q_NULLPTR; |
2867 | +} |
2868 | + |
2869 | +void MenuGroup::clear(QQmlListProperty<QObject> *list) |
2870 | +{ |
2871 | + MenuGroup *menuGroup = qobject_cast<MenuGroup*>(list->object); |
2872 | + if (menuGroup) { |
2873 | + menuGroup->m_data.clear(); |
2874 | + } |
2875 | +} |
2876 | + |
2877 | +UT_NAMESPACE_END |
2878 | |
2879 | === added file 'src/Ubuntu/UbuntuToolkit/menugroup_p.h' |
2880 | --- src/Ubuntu/UbuntuToolkit/menugroup_p.h 1970-01-01 00:00:00 +0000 |
2881 | +++ src/Ubuntu/UbuntuToolkit/menugroup_p.h 2016-08-31 08:57:26 +0000 |
2882 | @@ -0,0 +1,60 @@ |
2883 | +/* |
2884 | + * Copyright 2016 Canonical Ltd. |
2885 | + * |
2886 | + * This program is free software; you can redistribute it and/or modify |
2887 | + * it under the terms of the GNU Lesser General Public License as published by |
2888 | + * the Free Software Foundation; version 3. |
2889 | + * |
2890 | + * This program is distributed in the hope that it will be useful, |
2891 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2892 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2893 | + * GNU Lesser General Public License for more details. |
2894 | + * |
2895 | + * You should have received a copy of the GNU Lesser General Public License |
2896 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2897 | + */ |
2898 | + |
2899 | +#ifndef MENUGROUP_P_H |
2900 | +#define MENUGROUP_P_H |
2901 | + |
2902 | +#include <ubuntutoolkitglobal.h> |
2903 | + |
2904 | +#include <QObject> |
2905 | +#include <QVector> |
2906 | +#include <QQmlListProperty> |
2907 | + |
2908 | +UT_NAMESPACE_BEGIN |
2909 | + |
2910 | +class MenuGroup : public QObject |
2911 | +{ |
2912 | + Q_OBJECT |
2913 | + Q_PROPERTY(QQmlListProperty<QObject> data READ data) |
2914 | + Q_CLASSINFO("DefaultProperty", "data") |
2915 | +public: |
2916 | + explicit MenuGroup(QObject *parent = 0); |
2917 | + |
2918 | + QQmlListProperty<QObject> data(); |
2919 | + |
2920 | + const QVector<QObject*> &list() const; |
2921 | + |
2922 | +public Q_SLOTS: |
2923 | + void addObject(QObject *object); |
2924 | + void removeObject(QObject *object); |
2925 | + |
2926 | +Q_SIGNALS: |
2927 | + void added(QObject *object); |
2928 | + void removed(QObject *object); |
2929 | + void changed(); |
2930 | + |
2931 | +protected: |
2932 | + QVector<QObject*> m_data; |
2933 | + |
2934 | + static void append(QQmlListProperty<QObject> *list, QObject *action); |
2935 | + static int count(QQmlListProperty<QObject> *list); |
2936 | + static QObject* at(QQmlListProperty<QObject> *list, int); |
2937 | + static void clear(QQmlListProperty<QObject> *list); |
2938 | +}; |
2939 | + |
2940 | +UT_NAMESPACE_END |
2941 | + |
2942 | +#endif // MENUGROUP_P_H |
2943 | |
2944 | === added file 'src/Ubuntu/UbuntuToolkit/privates/splitviewhandler.cpp' |
2945 | --- src/Ubuntu/UbuntuToolkit/privates/splitviewhandler.cpp 1970-01-01 00:00:00 +0000 |
2946 | +++ src/Ubuntu/UbuntuToolkit/privates/splitviewhandler.cpp 2016-08-31 08:57:26 +0000 |
2947 | @@ -0,0 +1,137 @@ |
2948 | +/* |
2949 | + * Copyright 2016 Canonical Ltd. |
2950 | + * |
2951 | + * This program is free software; you can redistribute it and/or modify |
2952 | + * it under the terms of the GNU Lesser General Public License as published by |
2953 | + * the Free Software Foundation; version 3. |
2954 | + * |
2955 | + * This program is distributed in the hope that it will be useful, |
2956 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
2957 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
2958 | + * GNU Lesser General Public License for more details. |
2959 | + * |
2960 | + * You should have received a copy of the GNU Lesser General Public License |
2961 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
2962 | + * |
2963 | + * Author: Zsombor Egri <zsombor.egri@canonical.com> |
2964 | + */ |
2965 | + |
2966 | +#include "splitviewhandler_p_p.h" |
2967 | +#include <QtQuick/private/qquickanchors_p.h> |
2968 | +#include <QtQuick/private/qquickitem_p.h> |
2969 | +#include <QtQuick/private/qquickevents_p_p.h> |
2970 | +#include <QtQml/QQmlEngine> |
2971 | +#include <QtQml/QQmlInfo> |
2972 | +#include <splitview_p_p.h> |
2973 | + |
2974 | +UT_NAMESPACE_BEGIN |
2975 | + |
2976 | +SplitViewHandler::SplitViewHandler(QQuickItem *parent) |
2977 | + : QQuickMouseArea(parent) |
2978 | +{ |
2979 | + // for testing purposes |
2980 | + setObjectName("resize_handle"); |
2981 | + setFlag(ItemHasContents); |
2982 | + setHoverEnabled(true); |
2983 | + setAcceptedButtons(Qt::LeftButton); |
2984 | + |
2985 | + setCursorShape(Qt::SplitHCursor); |
2986 | + setParentItem(parent); |
2987 | + |
2988 | + // attach the handler to the parent's rigth edge |
2989 | + QQuickItemPrivate *dParent = QQuickItemPrivate::get(parentItem()); |
2990 | + QQuickAnchors *anchors = QQuickItemPrivate::get(this)->anchors(); |
2991 | + anchors->setTop(dParent->top()); |
2992 | + anchors->setBottom(dParent->bottom()); |
2993 | + anchors->setLeft(dParent->right()); |
2994 | + |
2995 | + // capture mouse events |
2996 | + connect(this, SIGNAL(pressed(QQuickMouseEvent*)), this, SLOT(onPressed(QQuickMouseEvent*))); |
2997 | + connect(this, &SplitViewHandler::released, this, &SplitViewHandler::onReleased); |
2998 | + connect(this, &SplitViewHandler::positionChanged, this, &SplitViewHandler::onPositionChanged); |
2999 | +} |
3000 | + |
3001 | +SplitViewHandler::~SplitViewHandler() |
3002 | +{ |
3003 | + if (spacing) { |
3004 | + disconnect(*spacing); |
3005 | + } |
3006 | + delete spacing; |
3007 | +} |
3008 | + |
3009 | +void SplitViewHandler::connectToView(SplitView *view) |
3010 | +{ |
3011 | + this->view = view; |
3012 | + // grab the context of the parent |
3013 | + QQmlEngine::setContextForObject(this, qmlContext(view)); |
3014 | + |
3015 | + // bind SplitView spacing, use it to specify the resize handle |
3016 | + auto resizer = [view, this]() { |
3017 | + setWidth(view->spacing()); |
3018 | + }; |
3019 | + spacing = new QMetaObject::Connection; |
3020 | + *spacing = connect(view, &SplitView::spacingChanged, resizer); |
3021 | + setWidth(view->spacing()); |
3022 | + |
3023 | + // connect to receive handle delegate |
3024 | + connect(view, &SplitView::handleDelegateChanged, |
3025 | + this, &SplitViewHandler::onDelegateChanged); |
3026 | + |
3027 | + onDelegateChanged(); |
3028 | +} |
3029 | + |
3030 | +void SplitViewHandler::onPressed(QQuickMouseEvent *event) |
3031 | +{ |
3032 | + prevPos = QPointF(event->x(), event->y()); |
3033 | +} |
3034 | + |
3035 | +void SplitViewHandler::onReleased(QQuickMouseEvent *event) |
3036 | +{ |
3037 | + Q_UNUSED(event); |
3038 | + prevPos = QPointF(); |
3039 | +} |
3040 | + |
3041 | +void SplitViewHandler::onPositionChanged(QQuickMouseEvent *event) |
3042 | +{ |
3043 | + if (!pressed()) { |
3044 | + return; |
3045 | + } |
3046 | + qreal dx = event->x() - prevPos.x(); |
3047 | + SplitViewAttached *attached = SplitViewAttached::get(parentItem()); |
3048 | + if (attached) { |
3049 | + attached->resize(dx); |
3050 | + } |
3051 | +} |
3052 | + |
3053 | +void SplitViewHandler::onDelegateChanged() |
3054 | +{ |
3055 | + static bool warningShown = false; |
3056 | + // the child is an instance of the delegate |
3057 | + QList<QQuickItem*> children = childItems(); |
3058 | + qDeleteAll(children); |
3059 | + |
3060 | + // and set the new delegate - if any |
3061 | + if (SplitViewPrivate::get(view)->handleDelegate) { |
3062 | + QQmlContext *context = new QQmlContext(qmlContext(this), this); |
3063 | + context->setContextProperty("handle", QVariant::fromValue(this)); |
3064 | + QObject *object = SplitViewPrivate::get(view)->handleDelegate->beginCreate(context); |
3065 | + if (object) { |
3066 | + QQuickItem *item = qobject_cast<QQuickItem*>(object); |
3067 | + if (!item) { |
3068 | + if (!warningShown) { |
3069 | + qmlInfo(view) << "handle delegate not an Item"; |
3070 | + warningShown = true; |
3071 | + } |
3072 | + SplitViewPrivate::get(view)->handleDelegate->completeCreate(); |
3073 | + delete object; |
3074 | + } else { |
3075 | + warningShown = false; |
3076 | + QQml_setParent_noEvent(item, this); |
3077 | + item->setParentItem(this); |
3078 | + SplitViewPrivate::get(view)->handleDelegate->completeCreate(); |
3079 | + } |
3080 | + } |
3081 | + } |
3082 | +} |
3083 | + |
3084 | +UT_NAMESPACE_END |
3085 | |
3086 | === added file 'src/Ubuntu/UbuntuToolkit/privates/splitviewhandler_p_p.h' |
3087 | --- src/Ubuntu/UbuntuToolkit/privates/splitviewhandler_p_p.h 1970-01-01 00:00:00 +0000 |
3088 | +++ src/Ubuntu/UbuntuToolkit/privates/splitviewhandler_p_p.h 2016-08-31 08:57:26 +0000 |
3089 | @@ -0,0 +1,52 @@ |
3090 | +/* |
3091 | + * Copyright 2016 Canonical Ltd. |
3092 | + * |
3093 | + * This program is free software; you can redistribute it and/or modify |
3094 | + * it under the terms of the GNU Lesser General Public License as published by |
3095 | + * the Free Software Foundation; version 3. |
3096 | + * |
3097 | + * This program is distributed in the hope that it will be useful, |
3098 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3099 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3100 | + * GNU Lesser General Public License for more details. |
3101 | + * |
3102 | + * You should have received a copy of the GNU Lesser General Public License |
3103 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3104 | + * |
3105 | + * Author: Zsombor Egri <zsombor.egri@canonical.com> |
3106 | + */ |
3107 | + |
3108 | +#ifndef SPLITVIEWHANDLER_H |
3109 | +#define SPLITVIEWHANDLER_H |
3110 | + |
3111 | +#include <QtQuick/QQuickItem> |
3112 | +#include <QtCore/QPointer> |
3113 | +#include <QtQuick/private/qquickmousearea_p.h> |
3114 | +#include <ubuntutoolkitglobal.h> |
3115 | + |
3116 | +UT_NAMESPACE_BEGIN |
3117 | + |
3118 | +class SplitView; |
3119 | +class SplitViewHandler : public QQuickMouseArea |
3120 | +{ |
3121 | + Q_OBJECT |
3122 | +public: |
3123 | + explicit SplitViewHandler(QQuickItem *parent = 0); |
3124 | + ~SplitViewHandler(); |
3125 | + void connectToView(SplitView *view); |
3126 | + |
3127 | +protected: |
3128 | + QPointF prevPos; |
3129 | + QPointer<SplitView> view; |
3130 | + QMetaObject::Connection *spacing{nullptr}; |
3131 | + |
3132 | +private Q_SLOTS: |
3133 | + void onPressed(QQuickMouseEvent *event); |
3134 | + void onReleased(QQuickMouseEvent *event); |
3135 | + void onPositionChanged(QQuickMouseEvent *event); |
3136 | + void onDelegateChanged(); |
3137 | +}; |
3138 | + |
3139 | +UT_NAMESPACE_END |
3140 | + |
3141 | +#endif // SPLITVIEWHANDLER_H |
3142 | |
3143 | === modified file 'src/Ubuntu/UbuntuToolkit/quickutils.cpp' |
3144 | --- src/Ubuntu/UbuntuToolkit/quickutils.cpp 2016-07-07 07:21:48 +0000 |
3145 | +++ src/Ubuntu/UbuntuToolkit/quickutils.cpp 2016-08-31 08:57:26 +0000 |
3146 | @@ -36,6 +36,7 @@ |
3147 | |
3148 | QuickUtils::QuickUtils(QObject *parent) : |
3149 | QObject(parent), |
3150 | + m_rootWindow(0), |
3151 | m_rootView(0), |
3152 | m_mouseAttached(false), |
3153 | m_keyboardAttached(false) |
3154 | @@ -86,7 +87,7 @@ |
3155 | // make sure we have the m_rootView updated |
3156 | lookupQuickView(); |
3157 | if (!object) { |
3158 | - return (m_rootView) ? m_rootView->rootObject() : 0; |
3159 | + return m_rootView ? m_rootView->rootObject() : (m_rootWindow ? m_rootWindow->contentItem() : 0); |
3160 | } |
3161 | |
3162 | QQuickItem *item = qobject_cast<QQuickItem*>(object); |
3163 | @@ -175,9 +176,10 @@ |
3164 | */ |
3165 | void QuickUtils::lookupQuickView() |
3166 | { |
3167 | - if (m_rootView) |
3168 | + if (m_rootWindow) |
3169 | return; |
3170 | Q_FOREACH (QWindow *w, QGuiApplication::topLevelWindows()) { |
3171 | + m_rootWindow = qobject_cast<QQuickWindow*>(w); |
3172 | m_rootView = qobject_cast<QQuickView*>(w); |
3173 | if (m_rootView) { |
3174 | // connect in case we get the root object changed |
3175 | |
3176 | === modified file 'src/Ubuntu/UbuntuToolkit/quickutils_p.h' |
3177 | --- src/Ubuntu/UbuntuToolkit/quickutils_p.h 2016-07-07 07:21:48 +0000 |
3178 | +++ src/Ubuntu/UbuntuToolkit/quickutils_p.h 2016-08-31 08:57:26 +0000 |
3179 | @@ -85,6 +85,7 @@ |
3180 | |
3181 | private: |
3182 | explicit QuickUtils(QObject *parent = 0); |
3183 | + QPointer<QQuickWindow> m_rootWindow; |
3184 | QPointer<QQuickView> m_rootView; |
3185 | QStringList m_omitIM; |
3186 | bool m_mouseAttached; |
3187 | |
3188 | === added file 'src/Ubuntu/UbuntuToolkit/splitview.cpp' |
3189 | --- src/Ubuntu/UbuntuToolkit/splitview.cpp 1970-01-01 00:00:00 +0000 |
3190 | +++ src/Ubuntu/UbuntuToolkit/splitview.cpp 2016-08-31 08:57:26 +0000 |
3191 | @@ -0,0 +1,675 @@ |
3192 | +/* |
3193 | + * Copyright 2016 Canonical Ltd. |
3194 | + * |
3195 | + * This program is free software; you can redistribute it and/or modify |
3196 | + * it under the terms of the GNU Lesser General Public License as published by |
3197 | + * the Free Software Foundation; version 3. |
3198 | + * |
3199 | + * This program is distributed in the hope that it will be useful, |
3200 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3201 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3202 | + * GNU Lesser General Public License for more details. |
3203 | + * |
3204 | + * You should have received a copy of the GNU Lesser General Public License |
3205 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3206 | + * |
3207 | + * Author: Zsombor Egri <zsombor.egri@canonical.com> |
3208 | + */ |
3209 | + |
3210 | +#include <QtQuick/private/qquickitem_p.h> |
3211 | +#include <QtQuick/private/qquickanchors_p.h> |
3212 | +#include <QtQml/QQmlInfo> |
3213 | + |
3214 | +#include "splitview_p.h" |
3215 | +#include "splitview_p_p.h" |
3216 | +#include "ucunits_p.h" |
3217 | +#include "ucpagetreenode_p.h" |
3218 | + |
3219 | +#include "privates/splitviewhandler_p_p.h" |
3220 | + |
3221 | +#define DEFAULT_SPACING_DP 4 |
3222 | + |
3223 | +UT_NAMESPACE_BEGIN |
3224 | + |
3225 | +/*! |
3226 | + * \qmltype SplitView |
3227 | + * \inqmlmodule Ubuntu.Components.Labs 1.0 |
3228 | + * \inherits QQuickBasePositioner |
3229 | + * \ingroup ubuntu-labs |
3230 | + * \brief A view component with a flexible layout configuration setup. |
3231 | + * |
3232 | + * The component arranges the declared child elements horizontally based on an active |
3233 | + * column configuration layout. Child elements are considered to be views, and each view |
3234 | + * is identified with a column index, specified by the SplitView.column attached property. |
3235 | + * Views should not have width declared, because the width of each view is specified |
3236 | + * by the active layout's configuration (ViewColumn) and will overwrite the value |
3237 | + * specified by the view. On the other hand they should have a height specified, or |
3238 | + * they can be anchored to the top and bottom of the view. SplitView being a positioner, |
3239 | + * remember not to anchor horizontal anchor lines or anchor fill the columns. |
3240 | + * |
3241 | + * In order for a SplitView to show some content it must have at least one active layout |
3242 | + * present. Views which are not configured by the active layout will be hidden. Hidden |
3243 | + * views may be resized, therefore if the content is size sensitive (i.e. the amount |
3244 | + * shown differs depending on the space available), make sure the content of your view |
3245 | + * does take this into account. |
3246 | + * \code |
3247 | + * import QtQuick 2.4 |
3248 | + * import Ubuntu.Components 1.3 |
3249 | + * import Ubuntu.Components.Labs 1.0 |
3250 | + * |
3251 | + * MainView { |
3252 | + * id: main |
3253 | + * width: units.gu(300) |
3254 | + * height: units.gu(80) |
3255 | + * SplitView { |
3256 | + * anchors.fill: parent |
3257 | + * layouts: [ |
3258 | + * SplitViewLayout { |
3259 | + * when: main.width < units.gu(80) |
3260 | + * ViewColumn { |
3261 | + * fillWidth: true |
3262 | + * } |
3263 | + * }, |
3264 | + * SplitViewLayout { |
3265 | + * when: main.width >= units.gu(80) |
3266 | + * ViewColumn { |
3267 | + * minimumWidth: units.gu(30) |
3268 | + * maximumWidth: units.gu(100) |
3269 | + * preferredWidth: units.gu(40) |
3270 | + * } |
3271 | + * ViewColumn { |
3272 | + * minimumWidth: units.gu(40) |
3273 | + * fillWidth: true |
3274 | + * } |
3275 | + * } |
3276 | + * ] |
3277 | + * } |
3278 | + * |
3279 | + * Page { |
3280 | + * id: column1 |
3281 | + * height: parent.height |
3282 | + * } |
3283 | + * Page { |
3284 | + * id: column2 |
3285 | + * height: parent.height |
3286 | + * } |
3287 | + * } |
3288 | + * \endcode |
3289 | + * |
3290 | + * The SplitView can be used with a Repeater in case the content of the view columns |
3291 | + * doesn't need to be preserved between layout changes. The example above with a |
3292 | + * Repeater would look as follows: |
3293 | + * \code |
3294 | + * import QtQuick 2.4 |
3295 | + * import Ubuntu.Components 1.3 |
3296 | + * import Ubuntu.Components.Labs 1.0 |
3297 | + * |
3298 | + * MainView { |
3299 | + * id: main |
3300 | + * width: units.gu(300) |
3301 | + * height: units.gu(80) |
3302 | + * SplitView { |
3303 | + * id: view |
3304 | + * anchors.fill: parent |
3305 | + * layouts: [ |
3306 | + * SplitViewLayout { |
3307 | + * when: main.width < units.gu(80) |
3308 | + * ViewColumn { |
3309 | + * fillWidth: true |
3310 | + * } |
3311 | + * }, |
3312 | + * SplitViewLayout { |
3313 | + * when: main.width >= units.gu(80) |
3314 | + * ViewColumn { |
3315 | + * minimumWidth: units.gu(30) |
3316 | + * maximumWidth: units.gu(100) |
3317 | + * preferredWidth: units.gu(40) |
3318 | + * } |
3319 | + * ViewColumn { |
3320 | + * minimumWidth: units.gu(40) |
3321 | + * fillWidth: true |
3322 | + * } |
3323 | + * } |
3324 | + * ] |
3325 | + * } |
3326 | + * |
3327 | + * Repeater { |
3328 | + * model: view.activeLayout.columns |
3329 | + * Page { |
3330 | + * height: parent.height |
3331 | + * } |
3332 | + * } |
3333 | + * } |
3334 | + * \endcode |
3335 | + * |
3336 | + * \section2 Resizing |
3337 | + * SplitView provides the ability to resize view columns. Each column has an attached |
3338 | + * handle which provides the ability to resize the columns using a mouse or touch. |
3339 | + * Columns can be resized if the spacing property is set and the column configurations |
3340 | + * allow that (see \l spacing property). |
3341 | + * |
3342 | + * \section2 Attached properties |
3343 | + * SplitView provides a set of attached properties to each column view. Views can in |
3344 | + * this way have access to various values of the SplitView and configurations. |
3345 | + */ |
3346 | + |
3347 | +/*! |
3348 | + * \qmlproperty real SplitView::spacing |
3349 | + * Spacing between view columns. A value bigger than 0 enables resizing of columns with |
3350 | + * a \l{ViewColumn::minimumWidth}{minimumWidth} lower than \l {ViewColumn::maximumWidth}{maximumWidth}. |
3351 | + * If spacing is 0 the columns cannot be resized. |
3352 | + * Defaults to 4 device pixels. |
3353 | + */ |
3354 | +void SplitView::setSpacing2(qreal spacing, bool reset) |
3355 | +{ |
3356 | + Q_D(SplitView); |
3357 | + if (reset && d->defaultSpacing) { |
3358 | + QObject::disconnect(*d->defaultSpacing); |
3359 | + delete d->defaultSpacing; |
3360 | + d->defaultSpacing = Q_NULLPTR; |
3361 | + } |
3362 | + if (qFuzzyCompare(this->spacing(), spacing)) { |
3363 | + return; |
3364 | + } |
3365 | + static_cast<QQuickBasePositioner*>(this)->setSpacing(spacing); |
3366 | +} |
3367 | + |
3368 | +/****************************************************************************** |
3369 | + * SplitViewAttached |
3370 | + */ |
3371 | +SplitViewAttached::SplitViewAttached(QObject *parent) |
3372 | + : QObject(*(new SplitViewAttachedPrivate), parent) |
3373 | +{ |
3374 | +} |
3375 | + |
3376 | +SplitViewAttached *SplitViewAttached::get(QQuickItem *item) |
3377 | +{ |
3378 | + SplitViewAttached *attached = static_cast<SplitViewAttached*>( |
3379 | + qmlAttachedPropertiesObject<SplitView>(item, false)); |
3380 | + return attached; |
3381 | +} |
3382 | + |
3383 | +ViewColumn *SplitViewAttachedPrivate::getConfig(QQuickItem *attachee) |
3384 | +{ |
3385 | + SplitViewAttached *attached = SplitViewAttached::get(attachee); |
3386 | + if (!attached) { |
3387 | + return nullptr; |
3388 | + } |
3389 | + return get(attached)->config(); |
3390 | +} |
3391 | + |
3392 | +void SplitViewAttachedPrivate::configure(SplitView *view, int column) |
3393 | +{ |
3394 | + this->column = column; |
3395 | + splitView = view; |
3396 | + Q_EMIT q_func()->columnChanged(); |
3397 | +} |
3398 | + |
3399 | +void SplitViewAttached::resize(qreal delta) |
3400 | +{ |
3401 | + Q_D(SplitViewAttached); |
3402 | + ViewColumn *config = d->config(); |
3403 | + if (config) { |
3404 | + config->resize(delta); |
3405 | + } |
3406 | +} |
3407 | + |
3408 | +/*! |
3409 | + * \qmlattachedproperty ViewColumn SplitView::columnConfig |
3410 | + * The attached property holds the active layout's column configuration data. |
3411 | + * The value is null if there is no active configuration value provided for |
3412 | + * the column. |
3413 | + */ |
3414 | +UT_PREPEND_NAMESPACE(ViewColumn*) SplitViewAttachedPrivate::config() |
3415 | +{ |
3416 | + if (!splitView || column < 0) { |
3417 | + return nullptr; |
3418 | + } |
3419 | + SplitViewPrivate *d = SplitViewPrivate::get(splitView); |
3420 | + if (!d->activeLayout) { |
3421 | + return nullptr; |
3422 | + } |
3423 | + if (SplitViewLayoutPrivate::get(d->activeLayout)->columnData.size() <= column) { |
3424 | + return nullptr; |
3425 | + } |
3426 | + return SplitViewLayoutPrivate::get(d->activeLayout)->columnData[column]; |
3427 | +} |
3428 | + |
3429 | +/*! |
3430 | + * \qmlattachedproperty SplitView SplitView::view |
3431 | + * Contains the SplitView instance of the column. |
3432 | + */ |
3433 | + |
3434 | +/*! |
3435 | + * \qmlattachedproperty int SplitView::column |
3436 | + * The property holds the column index the view is configured to. |
3437 | + */ |
3438 | + |
3439 | +/****************************************************************************** |
3440 | + * SplitView |
3441 | + */ |
3442 | +SplitViewPrivate::SplitViewPrivate(SplitView *qq) |
3443 | + : q_ptr(qq) |
3444 | +{ |
3445 | +} |
3446 | + |
3447 | +SplitViewPrivate::~SplitViewPrivate() |
3448 | +{ |
3449 | +} |
3450 | + |
3451 | +UT_PREPEND_NAMESPACE(SplitViewAttached) *SplitView::qmlAttachedProperties(QObject *owner) |
3452 | +{ |
3453 | + return new SplitViewAttached(owner); |
3454 | +} |
3455 | + |
3456 | +// layouts property |
3457 | +/*! |
3458 | + * \qmlproperty list<SplitViewLayout> SplitView::layouts |
3459 | + * The property holds the layout configurations declared for the given SplitView. |
3460 | + * \sa SplitViewLayout |
3461 | + */ |
3462 | +void SplitViewPrivate::layout_Append(QQmlListProperty<SplitViewLayout> *list, SplitViewLayout* layout) |
3463 | +{ |
3464 | + SplitView *view = static_cast<SplitView*>(list->object); |
3465 | + SplitViewPrivate *d = SplitViewPrivate::get(view); |
3466 | + d->columnLatouts.append(layout); |
3467 | + // parent layout to view |
3468 | + layout->setParent(view); |
3469 | + // capture layout activation |
3470 | + QObject::connect(layout, SIGNAL(whenChanged()), view, SLOT(changeLayout()), Qt::DirectConnection); |
3471 | + Q_EMIT view->layoutsChanged(); |
3472 | +} |
3473 | +int SplitViewPrivate::layout_Count(QQmlListProperty<SplitViewLayout> *list) |
3474 | +{ |
3475 | + SplitView *view = static_cast<SplitView*>(list->object); |
3476 | + SplitViewPrivate *d = SplitViewPrivate::get(view); |
3477 | + return d->columnLatouts.size(); |
3478 | +} |
3479 | +SplitViewLayout *SplitViewPrivate::layout_At(QQmlListProperty<SplitViewLayout> *list, int index) |
3480 | +{ |
3481 | + SplitView *view = static_cast<SplitView*>(list->object); |
3482 | + SplitViewPrivate *d = SplitViewPrivate::get(view); |
3483 | + return d->columnLatouts.at(index); |
3484 | +} |
3485 | +void SplitViewPrivate::layout_Clear(QQmlListProperty<SplitViewLayout> *list) |
3486 | +{ |
3487 | + SplitView *view = static_cast<SplitView*>(list->object); |
3488 | + SplitViewPrivate *d = SplitViewPrivate::get(view); |
3489 | + for (SplitViewLayout *layout : d->columnLatouts) { |
3490 | + // disconnect layout activation |
3491 | + QObject::disconnect(layout, SIGNAL(whenChanged()), view, SLOT(changeLayout())); |
3492 | + delete layout; |
3493 | + } |
3494 | + d->columnLatouts.clear(); |
3495 | + d->activeLayout = nullptr; |
3496 | + Q_EMIT view->layoutsChanged(); |
3497 | +} |
3498 | +QQmlListProperty<SplitViewLayout> SplitViewPrivate::layouts() |
3499 | +{ |
3500 | + Q_Q(SplitView); |
3501 | + return QQmlListProperty<SplitViewLayout>(q, (void*)&columnLatouts, |
3502 | + &layout_Append, |
3503 | + &layout_Count, |
3504 | + &layout_At, |
3505 | + &layout_Clear); |
3506 | +} |
3507 | + |
3508 | +/*! |
3509 | + * \qmlproperty SplitViewLayout SplitView::activeLayout |
3510 | + * \readonly |
3511 | + * The property holds the active SplitViewLayout instance, or null is no layout |
3512 | + * is active. |
3513 | + */ |
3514 | +UT_PREPEND_NAMESPACE(SplitViewLayout) *SplitViewPrivate::getActiveLayout() |
3515 | +{ |
3516 | + return activeLayout; |
3517 | +} |
3518 | + |
3519 | +// invoked when one of the SplitViewLayouts emits whenChanged() |
3520 | +void SplitViewPrivate::changeLayout() |
3521 | +{ |
3522 | + // go through layouts and check who's the active one |
3523 | + SplitViewLayout *newActive = nullptr; |
3524 | + for (SplitViewLayout *layout : columnLatouts) { |
3525 | + if (SplitViewLayoutPrivate::get(layout)->when) { |
3526 | + newActive = layout; |
3527 | + break; |
3528 | + } |
3529 | + } |
3530 | + if (newActive == activeLayout) { |
3531 | + return; |
3532 | + } |
3533 | + |
3534 | + // Q: should we reset the sizes of the previous layout? |
3535 | + // at least it feels right to preserve the last state of the layout... |
3536 | + activeLayout = newActive; |
3537 | + |
3538 | + Q_EMIT q_func()->activeLayoutChanged(); |
3539 | + |
3540 | + updateLayout(); |
3541 | + if (q_func()->sender()) { |
3542 | + // was called by a whenChanged() signal invocation |
3543 | + recalculateWidths(RecalculateAll); |
3544 | + } |
3545 | +} |
3546 | + |
3547 | +void SplitViewPrivate::updateLayout() |
3548 | +{ |
3549 | + Q_Q(SplitView); |
3550 | + for (QQuickItem *child : q->childItems()) { |
3551 | + bool visible = true; |
3552 | + ViewColumn *columnConfig = SplitViewAttachedPrivate::getConfig(child); |
3553 | + if (!columnConfig) { |
3554 | + // no configuration for the column, hide it |
3555 | + visible = false; |
3556 | + } else { |
3557 | + ViewColumnPrivate *config = ViewColumnPrivate::get(columnConfig); |
3558 | + visible = activeLayout |
3559 | + && (config->column < SplitViewLayoutPrivate::get(activeLayout)->columnData.size()); |
3560 | + } |
3561 | + dirty = dirty | (child->isVisible() != visible); |
3562 | + child->setVisible(visible); |
3563 | + } |
3564 | +} |
3565 | + |
3566 | +void SplitViewPrivate::recalculateWidths(RelayoutOperation operation) |
3567 | +{ |
3568 | + if (!activeLayout || (!QQuickItemPrivate::get(q_func())->componentComplete && !dirty)) { |
3569 | + return; |
3570 | + } |
3571 | + Q_Q(SplitView); |
3572 | + |
3573 | + // remove the spacing from the width |
3574 | + qreal fillWidth = q->width() - q->spacing() * (SplitViewLayoutPrivate::get(activeLayout)->columnData.size() - 1); |
3575 | + // stack of columns with fillWidth true |
3576 | + QList<QQuickItem*> fillStack; |
3577 | + |
3578 | + for (QQuickItem *child : q->childItems()) { |
3579 | + ViewColumn *columnConfig = SplitViewAttachedPrivate::getConfig(child); |
3580 | + if (!columnConfig) { |
3581 | + continue; |
3582 | + } |
3583 | + ViewColumnPrivate *config = ViewColumnPrivate::get(columnConfig); |
3584 | + |
3585 | + if (config->fillWidth && !config->resized) { |
3586 | + // add to the fillWidth stack |
3587 | + if (operation & CalculateFillWidth) { |
3588 | + fillStack << child; |
3589 | + } |
3590 | + } else { |
3591 | + if (operation & SetPreferredSize) { |
3592 | + child->setWidth(config->preferredWidth); |
3593 | + } |
3594 | + if (operation & CalculateFillWidth) { |
3595 | + fillWidth -= config->preferredWidth; |
3596 | + } |
3597 | + } |
3598 | + } |
3599 | + |
3600 | + // split the width between the stacked fillWidth columns |
3601 | + if (fillStack.size() && (operation & CalculateFillWidth)) { |
3602 | + fillWidth /= fillStack.size(); |
3603 | + for (QQuickItem *child : fillStack) { |
3604 | + // even though the column is fillWidth, it may have min and max specified; |
3605 | + // check if the size can be applied |
3606 | + ViewColumnPrivate *config = ViewColumnPrivate::get(SplitViewAttachedPrivate::getConfig(child)); |
3607 | + config->setPreferredWidth(fillWidth, false); |
3608 | + // update preferredWidth so it can be used in case of resize |
3609 | + child->setWidth(config->preferredWidth); |
3610 | + } |
3611 | + } |
3612 | + dirty = false; |
3613 | +} |
3614 | + |
3615 | +/*! |
3616 | + * \qmlproperty Component SplitView::handleDelegate |
3617 | + * The property holds the delegate to be shown for the column resizing handle. |
3618 | + * The delegate is for pure visual, mouse and touch handling is provided by the |
3619 | + * SplitView component itself. The component provides a context property called |
3620 | + * \e handle which embeds the visuals. This can be used to anchor the visuals |
3621 | + * to the resize handle. The thickness of the handle is driven by the \l spacing |
3622 | + * property. |
3623 | + * \code |
3624 | + * import QtQuick 2.4 |
3625 | + * import Ubuntu.Components 1.3 |
3626 | + * import Ubuntu.Components.Labs 1.0 |
3627 | + * |
3628 | + * MainView { |
3629 | + * id: main |
3630 | + * width: units.gu(300) |
3631 | + * height: units.gu(80) |
3632 | + * |
3633 | + * SplitView { |
3634 | + * anchors.fill: parent |
3635 | + * handleDelegate: Rectangle { |
3636 | + * anchors { |
3637 | + * fill: parent |
3638 | + * leftMargin: units.dp(2) |
3639 | + * rightMargin: units.dp(2) |
3640 | + * topMargin: handle.height / 2 - units.gu(3) |
3641 | + * bottomMargin: handle.height / 2 - units.gu(3) |
3642 | + * } |
3643 | + * color: UbuntuColors.graphite |
3644 | + * scale: handle.containsMouse || handle.pressed ? 1.6 : 1.0 |
3645 | + * Behavior on scale { UbuntuNumberAnimation {} } |
3646 | + * } |
3647 | + * layouts: [ |
3648 | + * SplitViewLayout { |
3649 | + * when: main.width < units.gu(80) |
3650 | + * ViewColumn { |
3651 | + * fillWidth: true |
3652 | + * } |
3653 | + * }, |
3654 | + * SplitViewLayout { |
3655 | + * when: main.width >= units.gu(80) |
3656 | + * ViewColumn { |
3657 | + * minimumWidth: units.gu(30) |
3658 | + * maximumWidth: units.gu(100) |
3659 | + * preferredWidth: units.gu(40) |
3660 | + * } |
3661 | + * ViewColumn { |
3662 | + * minimumWidth: units.gu(40) |
3663 | + * fillWidth: true |
3664 | + * } |
3665 | + * } |
3666 | + * ] |
3667 | + * } |
3668 | + * |
3669 | + * Page { |
3670 | + * id: column1 |
3671 | + * height: parent.height |
3672 | + * } |
3673 | + * Page { |
3674 | + * id: column2 |
3675 | + * height: parent.height |
3676 | + * } |
3677 | + * } |
3678 | + * \endcode |
3679 | + */ |
3680 | + |
3681 | +SplitView::SplitView(QQuickItem *parent) |
3682 | + : QQuickBasePositioner(Horizontal, parent) |
3683 | + , d_ptr(new SplitViewPrivate(this)) |
3684 | +{ |
3685 | + Q_D(SplitView); |
3686 | + d->init(); |
3687 | +} |
3688 | + |
3689 | +SplitView::SplitView(SplitViewPrivate &dd, QQuickItem *parent) |
3690 | + : QQuickBasePositioner(Vertical, parent) |
3691 | + , d_ptr(&dd) |
3692 | +{ |
3693 | + Q_D(SplitView); |
3694 | + d->init(); |
3695 | +} |
3696 | + |
3697 | +SplitView::~SplitView() |
3698 | +{ |
3699 | + Q_D(SplitView); |
3700 | + delete d; |
3701 | +} |
3702 | + |
3703 | +void SplitViewPrivate::init() |
3704 | +{ |
3705 | + Q_Q(SplitView); |
3706 | + auto spacingHandle = [q]() { |
3707 | + q->setSpacing2(UCUnits::instance(q)->dp(DEFAULT_SPACING_DP), false); |
3708 | + }; |
3709 | + defaultSpacing = new QMetaObject::Connection; |
3710 | + *defaultSpacing = QObject::connect(UCUnits::instance(q), &UCUnits::gridUnitChanged, spacingHandle); |
3711 | + |
3712 | + // connect the spacingChanged signals |
3713 | + QObject::connect(q, SIGNAL(spacingChanged()), q, SIGNAL(spacingChanged2())); |
3714 | + |
3715 | + // set the defaults |
3716 | + q->setSpacing2(UCUnits::instance(q)->dp(DEFAULT_SPACING_DP), false); |
3717 | +} |
3718 | + |
3719 | +void SplitView::doPositioning(QSizeF *contentSize) |
3720 | +{ |
3721 | + // Inspired from QtQuick QQuickRow code |
3722 | + // FIXME: revisit the code once we move to Qt 5.6 as there were more properties added to positioner |
3723 | + |
3724 | + // calculate the layout before we go into the positioning |
3725 | + d_func()->recalculateWidths(SplitViewPrivate::RecalculateAll); |
3726 | + |
3727 | + //Precondition: All items in the positioned list have a valid item pointer and should be positioned |
3728 | + QQuickItemPrivate *d = QQuickItemPrivate::get(this); |
3729 | + qreal hoffset = 0; |
3730 | + |
3731 | + QList<qreal> hoffsets; |
3732 | + for (int ii = 0; ii < positionedItems.count(); ++ii) { |
3733 | + PositionedItem &child = positionedItems[ii]; |
3734 | + |
3735 | + if (!d->effectiveLayoutMirror) { |
3736 | + positionItemX(hoffset, &child); |
3737 | + } else { |
3738 | + hoffsets << hoffset; |
3739 | + } |
3740 | + |
3741 | + contentSize->setHeight(qMax(contentSize->height(), child.item->height())); |
3742 | + |
3743 | + hoffset += child.item->width(); |
3744 | + hoffset += spacing(); |
3745 | + } |
3746 | + |
3747 | + if (hoffset != 0) { //If we positioned any items, undo the extra spacing from the last item |
3748 | + hoffset -= spacing(); |
3749 | + } |
3750 | + contentSize->setWidth(hoffset); |
3751 | + |
3752 | + if (!d->effectiveLayoutMirror) { |
3753 | + return; |
3754 | + } |
3755 | + |
3756 | + //Right to Left layout |
3757 | + qreal end = 0; |
3758 | + if (!widthValid()) { |
3759 | + end = contentSize->width(); |
3760 | + } else { |
3761 | + end = width(); |
3762 | + } |
3763 | + |
3764 | + int acc = 0; |
3765 | + for (int ii = 0; ii < positionedItems.count(); ++ii) { |
3766 | + PositionedItem &child = positionedItems[ii]; |
3767 | + hoffset = end - hoffsets[acc++] - child.item->width(); |
3768 | + positionItemX(hoffset, &child); |
3769 | + } |
3770 | +} |
3771 | + |
3772 | +void SplitView::reportConflictingAnchors() |
3773 | +{ |
3774 | + // Inspired from QtQuick QQuickColumn code |
3775 | + bool anchorConflict = false; |
3776 | + for (int ii = 0; ii < positionedItems.count(); ++ii) { |
3777 | + const PositionedItem &child = positionedItems.at(ii); |
3778 | + if (child.item) { |
3779 | + QQuickAnchors *anchors = QQuickItemPrivate::get(static_cast<QQuickItem *>(child.item))->_anchors; |
3780 | + if (anchors) { |
3781 | + // can we patch out the anchors (Page?) |
3782 | + if (qobject_cast<UCPageTreeNode*>(child.item)) { |
3783 | + // unset left anchor |
3784 | + anchors->resetLeft(); |
3785 | + } |
3786 | + QQuickAnchors::Anchors usedAnchors = anchors->usedAnchors(); |
3787 | + if (usedAnchors & QQuickAnchors::LeftAnchor || |
3788 | + usedAnchors & QQuickAnchors::RightAnchor || |
3789 | + usedAnchors & QQuickAnchors::HCenterAnchor || |
3790 | + anchors->fill() || anchors->centerIn()) { |
3791 | + anchorConflict = true; |
3792 | + break; |
3793 | + } |
3794 | + } |
3795 | + } |
3796 | + } |
3797 | + if (anchorConflict) { |
3798 | + qmlInfo(this) << "Cannot specify left, right, horizontalCenter, fill or centerIn anchors for items inside SplitView." |
3799 | + << " SplitView will not function."; |
3800 | + } |
3801 | +} |
3802 | + |
3803 | +void SplitView::componentComplete() |
3804 | +{ |
3805 | + Q_D(SplitView); |
3806 | + d->changeLayout(); |
3807 | + QQuickBasePositioner::componentComplete(); |
3808 | +} |
3809 | + |
3810 | +void SplitView::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) |
3811 | +{ |
3812 | + QQuickBasePositioner::geometryChanged(newGeometry, oldGeometry); |
3813 | + // if we have fillWidths, recalculate those! |
3814 | + // call this on horizontal resize, vertical one will do its job |
3815 | + if (newGeometry.width() != oldGeometry.width()) { |
3816 | + Q_D(SplitView); |
3817 | + d->recalculateWidths(SplitViewPrivate::CalculateFillWidth); |
3818 | + } |
3819 | +} |
3820 | + |
3821 | +void SplitView::itemChange(ItemChange change, const ItemChangeData &data) |
3822 | +{ |
3823 | + QQuickBasePositioner::itemChange(change, data); |
3824 | + // we must exclude Repeater |
3825 | + switch (change) { |
3826 | + case ItemChildAddedChange: |
3827 | + if (data.item && !data.item->inherits("QQuickRepeater")) { |
3828 | + // attach properties and configure |
3829 | + SplitViewAttached *attached = static_cast<SplitViewAttached*>( |
3830 | + qmlAttachedPropertiesObject<SplitView>(data.item, true)); |
3831 | + |
3832 | + Q_D(SplitView); |
3833 | + SplitViewAttachedPrivate::get(attached)->configure(this, d->viewCount++); |
3834 | + |
3835 | + // attach the split handler to it |
3836 | + SplitViewHandler *handler = new SplitViewHandler(data.item); |
3837 | + handler->connectToView(this); |
3838 | + } |
3839 | + break; |
3840 | + case ItemChildRemovedChange: |
3841 | + if (data.item && !data.item->inherits("QQuickRepeater")) { |
3842 | + Q_D(SplitView); |
3843 | + d->viewCount--; |
3844 | + } |
3845 | + break; |
3846 | + default: // ommit the rest |
3847 | + break; |
3848 | + } |
3849 | +} |
3850 | + |
3851 | +/****************************************************************** |
3852 | + * properties |
3853 | + */ |
3854 | + |
3855 | +void SplitViewPrivate::setHandle(QQmlComponent *delegate) |
3856 | +{ |
3857 | + if (handleDelegate == delegate) { |
3858 | + return; |
3859 | + } |
3860 | + handleDelegate = delegate; |
3861 | + Q_EMIT q_func()->handleDelegateChanged(); |
3862 | +} |
3863 | + |
3864 | +UT_NAMESPACE_END |
3865 | + |
3866 | +#include "moc_splitview_p.cpp" |
3867 | |
3868 | === added file 'src/Ubuntu/UbuntuToolkit/splitview_p.h' |
3869 | --- src/Ubuntu/UbuntuToolkit/splitview_p.h 1970-01-01 00:00:00 +0000 |
3870 | +++ src/Ubuntu/UbuntuToolkit/splitview_p.h 2016-08-31 08:57:26 +0000 |
3871 | @@ -0,0 +1,160 @@ |
3872 | +/* |
3873 | + * Copyright 2016 Canonical Ltd. |
3874 | + * |
3875 | + * This program is free software; you can redistribute it and/or modify |
3876 | + * it under the terms of the GNU Lesser General Public License as published by |
3877 | + * the Free Software Foundation; version 3. |
3878 | + * |
3879 | + * This program is distributed in the hope that it will be useful, |
3880 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
3881 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
3882 | + * GNU Lesser General Public License for more details. |
3883 | + * |
3884 | + * You should have received a copy of the GNU Lesser General Public License |
3885 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
3886 | + * |
3887 | + * Author: Zsombor Egri <zsombor.egri@canonical.com> |
3888 | + */ |
3889 | + |
3890 | +#ifndef SPLITVIEW_P_H |
3891 | +#define SPLITVIEW_P_H |
3892 | + |
3893 | +#include <QtQuick/private/qquickpositioners_p.h> |
3894 | + |
3895 | +#include "ubuntutoolkitglobal.h" |
3896 | + |
3897 | +UT_NAMESPACE_BEGIN |
3898 | + |
3899 | +class ViewColumnPrivate; |
3900 | +class ViewColumn : public QObject, public QQmlParserStatus |
3901 | +{ |
3902 | + Q_OBJECT |
3903 | + Q_INTERFACES(QQmlParserStatus) |
3904 | + Q_PRIVATE_PROPERTY(ViewColumn::d_func(), bool fillWidth MEMBER fillWidth WRITE setFillWidth NOTIFY fillWidthChanged) |
3905 | + Q_PRIVATE_PROPERTY(ViewColumn::d_func(), qreal minimumWidth MEMBER minimumWidth WRITE setMinimumWidth NOTIFY minimumWidthChanged) |
3906 | + Q_PRIVATE_PROPERTY(ViewColumn::d_func(), qreal maximumWidth MEMBER maximumWidth WRITE setMaximumWidth NOTIFY maximumWidthChanged) |
3907 | + Q_PRIVATE_PROPERTY(ViewColumn::d_func(), qreal preferredWidth MEMBER preferredWidth WRITE setPreferredWidth NOTIFY preferredWidthChanged) |
3908 | +public: |
3909 | + explicit ViewColumn(QObject *parent = 0); |
3910 | + |
3911 | + bool resize(qreal delta); |
3912 | + |
3913 | +Q_SIGNALS: |
3914 | + void minimumWidthChanged(); |
3915 | + void maximumWidthChanged(); |
3916 | + void preferredWidthChanged(); |
3917 | + void fillWidthChanged(); |
3918 | + |
3919 | +protected: |
3920 | + // from QQmlParserStatus |
3921 | + void classBegin() override {} |
3922 | + void componentComplete() override; |
3923 | +private: |
3924 | + Q_DECLARE_PRIVATE(ViewColumn) |
3925 | +}; |
3926 | + |
3927 | +class SplitViewLayoutPrivate; |
3928 | +class SplitViewLayout : public QObject |
3929 | +{ |
3930 | + Q_OBJECT |
3931 | + |
3932 | + Q_PRIVATE_PROPERTY(SplitViewLayout::d_func(), bool when MEMBER when NOTIFY whenChanged) |
3933 | +#ifdef Q_QDOC |
3934 | + Q_PRIVATE_PROPERTY(SplitViewLayout::d_func(), QQmlListProperty<ViewColumn> columns READ columns NOTIFY columnsChanged DESIGNABLE false) |
3935 | +#else |
3936 | + Q_PRIVATE_PROPERTY(SplitViewLayout::d_func(), QQmlListProperty<UT_PREPEND_NAMESPACE(ViewColumn)> columns READ columns NOTIFY columnsChanged DESIGNABLE false) |
3937 | +#endif |
3938 | + Q_CLASSINFO("DefaultProperty", "columns") |
3939 | +public: |
3940 | + explicit SplitViewLayout(QObject *parent = 0); |
3941 | + |
3942 | +Q_SIGNALS: |
3943 | + void whenChanged(); |
3944 | + void columnsChanged(); |
3945 | + |
3946 | +private: |
3947 | + Q_DECLARE_PRIVATE(SplitViewLayout) |
3948 | +}; |
3949 | + |
3950 | +class SplitView; |
3951 | +class SplitViewAttachedPrivate; |
3952 | +class SplitViewAttached : public QObject |
3953 | +{ |
3954 | + Q_OBJECT |
3955 | + Q_PRIVATE_PROPERTY(SplitViewAttached::d_func(), int column READ getColumn NOTIFY columnChanged) |
3956 | +#ifdef Q_QDOC |
3957 | + Q_PRIVATE_PROPERTY(SplitViewAttached::d_func(), SplitView* view READ view) |
3958 | + Q_PRIVATE_PROPERTY(SplitViewAttached::d_func(), ViewColumn* columnConfig READ config NOTIFY columnChanged) |
3959 | +#else |
3960 | + Q_PRIVATE_PROPERTY(SplitViewAttached::d_func(), UT_PREPEND_NAMESPACE(SplitView*) view READ view) |
3961 | + Q_PRIVATE_PROPERTY(SplitViewAttached::d_func(), UT_PREPEND_NAMESPACE(ViewColumn*) columnConfig READ config NOTIFY columnChanged) |
3962 | +#endif |
3963 | +public: |
3964 | + explicit SplitViewAttached(QObject *parent = 0); |
3965 | + |
3966 | + static SplitViewAttached *get(QQuickItem *item); |
3967 | + |
3968 | + void resize(qreal delta); |
3969 | + |
3970 | +Q_SIGNALS: |
3971 | + void columnChanged(); |
3972 | + |
3973 | +private: |
3974 | + Q_DECLARE_PRIVATE(SplitViewAttached) |
3975 | +}; |
3976 | + |
3977 | +class SplitViewPrivate; |
3978 | +class SplitView : public QQuickBasePositioner |
3979 | +{ |
3980 | + Q_OBJECT |
3981 | +#ifdef Q_QDOC |
3982 | + Q_PRIVATE_PROPERTY(SplitView::d_func(), QQmlListProperty<SplitViewLayout> layouts READ layouts NOTIFY layoutsChanged DESIGNABLE false) |
3983 | + Q_PRIVATE_PROPERTY(SplitView::d_func(), SplitViewLayout *activeLayout READ getActiveLayout NOTIFY activeLayoutChanged) |
3984 | +#else |
3985 | + Q_PRIVATE_PROPERTY(SplitView::d_func(), QQmlListProperty<UT_PREPEND_NAMESPACE(SplitViewLayout)> layouts READ layouts NOTIFY layoutsChanged DESIGNABLE false) |
3986 | + Q_PRIVATE_PROPERTY(SplitView::d_func(), UT_PREPEND_NAMESPACE(SplitViewLayout) *activeLayout READ getActiveLayout NOTIFY activeLayoutChanged) |
3987 | +#endif |
3988 | + Q_PRIVATE_PROPERTY(SplitView::d_func(), QQmlComponent *handleDelegate MEMBER handleDelegate WRITE setHandle NOTIFY handleDelegateChanged) |
3989 | + // overrides |
3990 | + Q_PROPERTY(qreal spacing READ spacing WRITE setSpacing2 NOTIFY spacingChanged2) |
3991 | +public: |
3992 | + explicit SplitView(QQuickItem *parent = 0); |
3993 | + |
3994 | + static UT_PREPEND_NAMESPACE(SplitViewAttached) *qmlAttachedProperties(QObject*); |
3995 | + |
3996 | +Q_SIGNALS: |
3997 | + void layoutsChanged(); |
3998 | + void activeLayoutChanged(); |
3999 | + void handleDelegateChanged(); |
4000 | + void spacingChanged2(); |
4001 | + |
4002 | +protected: |
4003 | + SplitView(SplitViewPrivate &, QQuickItem *); |
4004 | + ~SplitView(); |
4005 | + |
4006 | + // property setters |
4007 | + void setSpacing2(qreal spacing, bool reset = true); |
4008 | + |
4009 | + // from QQuickBasePositioner |
4010 | + void doPositioning(QSizeF *contentSize) override; |
4011 | + void reportConflictingAnchors() override; |
4012 | + |
4013 | + // overrides |
4014 | + void componentComplete() override; |
4015 | + void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; |
4016 | + void itemChange(ItemChange, const ItemChangeData &) override; |
4017 | +private: |
4018 | + // QQuickBasePositionerPrivate is not an exported API, therefore we cannot derive from it |
4019 | + SplitViewPrivate* const d_ptr; |
4020 | + Q_DECLARE_PRIVATE_D(d_ptr, SplitView) |
4021 | + Q_PRIVATE_SLOT(d_func(), void changeLayout()) |
4022 | +}; |
4023 | + |
4024 | +UT_NAMESPACE_END |
4025 | + |
4026 | +QML_DECLARE_TYPE(UT_PREPEND_NAMESPACE(ViewColumn)) |
4027 | +QML_DECLARE_TYPE(UT_PREPEND_NAMESPACE(SplitViewLayout)) |
4028 | +QML_DECLARE_TYPE(UT_PREPEND_NAMESPACE(SplitView)) |
4029 | +QML_DECLARE_TYPEINFO(UT_PREPEND_NAMESPACE(SplitView), QML_HAS_ATTACHED_PROPERTIES) |
4030 | + |
4031 | +#endif // SPLITVIEW_P_H |
4032 | |
4033 | === added file 'src/Ubuntu/UbuntuToolkit/splitview_p_p.h' |
4034 | --- src/Ubuntu/UbuntuToolkit/splitview_p_p.h 1970-01-01 00:00:00 +0000 |
4035 | +++ src/Ubuntu/UbuntuToolkit/splitview_p_p.h 2016-08-31 08:57:26 +0000 |
4036 | @@ -0,0 +1,154 @@ |
4037 | +/* |
4038 | + * Copyright 2016 Canonical Ltd. |
4039 | + * |
4040 | + * This program is free software; you can redistribute it and/or modify |
4041 | + * it under the terms of the GNU Lesser General Public License as published by |
4042 | + * the Free Software Foundation; version 3. |
4043 | + * |
4044 | + * This program is distributed in the hope that it will be useful, |
4045 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4046 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4047 | + * GNU Lesser General Public License for more details. |
4048 | + * |
4049 | + * You should have received a copy of the GNU Lesser General Public License |
4050 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4051 | + * |
4052 | + * Author: Zsombor Egri <zsombor.egri@canonical.com> |
4053 | + */ |
4054 | + |
4055 | +#ifndef SPLITVIEW_P_P_H |
4056 | +#define SPLITVIEW_P_P_H |
4057 | + |
4058 | +#include "splitview_p.h" |
4059 | +#include <QtCore/private/qobject_p.h> |
4060 | + |
4061 | +UT_NAMESPACE_BEGIN |
4062 | + |
4063 | +class ViewColumnPrivate : public QObjectPrivate |
4064 | +{ |
4065 | + Q_DECLARE_PUBLIC(ViewColumn) |
4066 | + |
4067 | +public: |
4068 | + ViewColumnPrivate() {} |
4069 | + |
4070 | + static ViewColumnPrivate *get(ViewColumn *q) |
4071 | + { |
4072 | + return q ? q->d_func() : nullptr; |
4073 | + } |
4074 | + |
4075 | + void setMinimumWidth(qreal width); |
4076 | + void setMaximumWidth(qreal width); |
4077 | + void setPreferredWidth(qreal width, bool notify = true); |
4078 | + void setFillWidth(bool fill); |
4079 | + |
4080 | + void recalculateLayoutContent(); |
4081 | + |
4082 | + qreal minimumWidth{0.0}; |
4083 | + qreal maximumWidth{std::numeric_limits<qreal>::max()}; |
4084 | + qreal preferredWidth{0.0}; |
4085 | + int column{-1}; |
4086 | + bool fillWidth{false}; |
4087 | + bool resized{false}; |
4088 | + bool completed{false}; |
4089 | +}; |
4090 | + |
4091 | +class SplitViewLayoutPrivate : public QObjectPrivate |
4092 | +{ |
4093 | + Q_DECLARE_PUBLIC(SplitViewLayout) |
4094 | +public: |
4095 | + SplitViewLayoutPrivate() {} |
4096 | + static SplitViewLayoutPrivate *get(SplitViewLayout *q) |
4097 | + { |
4098 | + return q->d_func(); |
4099 | + } |
4100 | + |
4101 | + QQmlListProperty<UT_PREPEND_NAMESPACE(ViewColumn)> columns(); |
4102 | + |
4103 | + QList<ViewColumn*> columnData; |
4104 | + bool when{false}; |
4105 | + |
4106 | +private: |
4107 | + static void columns_Append(QQmlListProperty<ViewColumn> *, ViewColumn*); |
4108 | + static int columns_Count(QQmlListProperty<ViewColumn> *); |
4109 | + static ViewColumn *columns_At(QQmlListProperty<ViewColumn> *, int); |
4110 | + static void columns_Clear(QQmlListProperty<ViewColumn> *); |
4111 | +}; |
4112 | + |
4113 | +class SplitViewAttachedPrivate : public QObjectPrivate |
4114 | +{ |
4115 | + Q_DECLARE_PUBLIC(SplitViewAttached) |
4116 | +public: |
4117 | + SplitViewAttachedPrivate() {} |
4118 | + |
4119 | + static SplitViewAttachedPrivate *get(SplitViewAttached *q) |
4120 | + { |
4121 | + return q->d_func(); |
4122 | + } |
4123 | + static ViewColumn *getConfig(QQuickItem *attachee); |
4124 | + |
4125 | + UT_PREPEND_NAMESPACE(SplitView*) view() const |
4126 | + { |
4127 | + return splitView; |
4128 | + } |
4129 | + int getColumn() const |
4130 | + { |
4131 | + return column; |
4132 | + } |
4133 | + void configure(SplitView *view, int column); |
4134 | + UT_PREPEND_NAMESPACE(ViewColumn*) config(); |
4135 | + |
4136 | + SplitView *splitView{nullptr}; |
4137 | + int column{-1}; |
4138 | +}; |
4139 | + |
4140 | +class SplitViewPrivate |
4141 | +{ |
4142 | + SplitView *const q_ptr{nullptr}; |
4143 | + Q_DECLARE_PUBLIC(SplitView) |
4144 | + |
4145 | +public: |
4146 | + enum RelayoutOperation { |
4147 | + SetPreferredSize = 0x01, |
4148 | + CalculateFillWidth = 0x02, |
4149 | + RecalculateAll = 0xFF |
4150 | + }; |
4151 | + |
4152 | + SplitViewPrivate(SplitView *qq); |
4153 | + virtual ~SplitViewPrivate(); |
4154 | + void init(); |
4155 | + |
4156 | + static SplitViewPrivate *get(SplitView *q) |
4157 | + { |
4158 | + return q->d_func(); |
4159 | + } |
4160 | + |
4161 | + QQmlListProperty<QObject> data(); |
4162 | + QQmlListProperty<UT_PREPEND_NAMESPACE(SplitViewLayout)> layouts(); |
4163 | + UT_PREPEND_NAMESPACE(SplitViewLayout) *getActiveLayout(); |
4164 | + |
4165 | + void updateLayout(); |
4166 | + void recalculateWidths(RelayoutOperation operation); |
4167 | + void setHandle(QQmlComponent *delegate); |
4168 | + |
4169 | + // private slots |
4170 | + void changeLayout(); |
4171 | + |
4172 | + // members |
4173 | + QList<SplitViewLayout*> columnLatouts; |
4174 | + SplitViewLayout* activeLayout{nullptr}; |
4175 | + QQmlComponent *handleDelegate{nullptr}; |
4176 | + QMetaObject::Connection *defaultSpacing{nullptr}; |
4177 | + int viewCount{0}; |
4178 | + bool dirty{false}; |
4179 | + |
4180 | +private: |
4181 | + static void layout_Append(QQmlListProperty<SplitViewLayout> *, SplitViewLayout*); |
4182 | + static int layout_Count(QQmlListProperty<SplitViewLayout> *); |
4183 | + static SplitViewLayout *layout_At(QQmlListProperty<SplitViewLayout> *, int); |
4184 | + static void layout_Clear(QQmlListProperty<SplitViewLayout> *); |
4185 | +}; |
4186 | + |
4187 | +UT_NAMESPACE_END |
4188 | + |
4189 | +#endif // SPLITVIEW_P_P_H |
4190 | + |
4191 | |
4192 | === added file 'src/Ubuntu/UbuntuToolkit/splitviewlayout.cpp' |
4193 | --- src/Ubuntu/UbuntuToolkit/splitviewlayout.cpp 1970-01-01 00:00:00 +0000 |
4194 | +++ src/Ubuntu/UbuntuToolkit/splitviewlayout.cpp 2016-08-31 08:57:26 +0000 |
4195 | @@ -0,0 +1,252 @@ |
4196 | +/* Copyright 2016 Canonical Ltd. |
4197 | + * |
4198 | + * This program is free software; you can redistribute it and/or modify |
4199 | + * it under the terms of the GNU Lesser General Public License as published by |
4200 | + * the Free Software Foundation; version 3. |
4201 | + * |
4202 | + * This program is distributed in the hope that it will be useful, |
4203 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
4204 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
4205 | + * GNU Lesser General Public License for more details. |
4206 | + * |
4207 | + * You should have received a copy of the GNU Lesser General Public License |
4208 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
4209 | + * |
4210 | + * Author: Zsombor Egri <zsombor.egri@canonical.com> |
4211 | + */ |
4212 | + |
4213 | +#include <QtQuick/private/qquickitem_p.h> |
4214 | +#include <QtQuick/private/qquickanchors_p.h> |
4215 | +#include <QtQml/QQmlInfo> |
4216 | + |
4217 | +#include "splitview_p.h" |
4218 | +#include "splitview_p_p.h" |
4219 | +#include "ucmathutils_p.h" |
4220 | + |
4221 | +UT_NAMESPACE_BEGIN |
4222 | + |
4223 | +/****************************************************************************** |
4224 | + * ViewColumn configuration object |
4225 | + */ |
4226 | +/*! |
4227 | + * \qmltype ViewColumn |
4228 | + * \inmodule Ubuntu.Components.Labs |
4229 | + * \ingroup ubuntu-labs |
4230 | + * \brief View column metrics configuration for SplitView. |
4231 | + * |
4232 | + * The component provides width metrics configuration for SplitView layout |
4233 | + * columns. The values are applied on columns by an active SplitViewLayout. |
4234 | + * On resizing, the values are preserved for the entire lifetime of the |
4235 | + * component, even when the active layout is changed into an other one. |
4236 | + * When changed back, the previous values will be used. |
4237 | + */ |
4238 | +ViewColumn::ViewColumn(QObject *parent) |
4239 | + : QObject(*(new ViewColumnPrivate), parent) |
4240 | +{ |
4241 | +} |
4242 | + |
4243 | +void ViewColumn::componentComplete() |
4244 | +{ |
4245 | + // check preferredWidth, its value may need fixing |
4246 | + Q_D(ViewColumn); |
4247 | + d->setPreferredWidth(d->preferredWidth); |
4248 | + d->completed = true; |
4249 | +} |
4250 | + |
4251 | +void ViewColumnPrivate::recalculateLayoutContent() |
4252 | +{ |
4253 | + SplitViewLayout *layout = qobject_cast<SplitViewLayout*>(parent); |
4254 | + if (!layout) { |
4255 | + qFatal("ViewColumn declared outside of SplitViewLayout"); |
4256 | + } |
4257 | + SplitView *view = qobject_cast<SplitView*>(SplitViewLayoutPrivate::get(layout)->parent); |
4258 | + if (!view) { |
4259 | + qFatal("SplitViewLayout not used in any SplitView"); |
4260 | + } |
4261 | + |
4262 | + SplitViewPrivate *dView = SplitViewPrivate::get(view); |
4263 | + if (dView->activeLayout == layout) { |
4264 | + dView->recalculateWidths(SplitViewPrivate::RecalculateAll); |
4265 | + } |
4266 | +} |
4267 | + |
4268 | +/*! |
4269 | + * \qmlproperty real ViewColumn::minimumWidth |
4270 | + * Specifies the minimum width of the column. The number must be a positive value |
4271 | + * and less or equal than the maximumWidth value. |
4272 | + */ |
4273 | +void ViewColumnPrivate::setMinimumWidth(qreal width) |
4274 | +{ |
4275 | + if (qFuzzyCompare(minimumWidth, width)) { |
4276 | + return; |
4277 | + } |
4278 | + if (width < 0.0) { |
4279 | + qmlInfo(q_func()) << "minimumWidth cannot be a negative value"; |
4280 | + return; |
4281 | + } |
4282 | + if (width > maximumWidth) { |
4283 | + qmlInfo(q_func()) << "minimumWidth is greater than maximumWidth"; |
4284 | + return; |
4285 | + } |
4286 | + minimumWidth = width; |
4287 | + Q_EMIT q_func()->minimumWidthChanged(); |
4288 | + // clamp preferredWidth if needed |
4289 | + setPreferredWidth(preferredWidth); |
4290 | + recalculateLayoutContent(); |
4291 | +} |
4292 | + |
4293 | +/*! |
4294 | + * \qmlproperty real ViewColumn::maximumWidth |
4295 | + * Specifies the maximum width of the column. The number must be a positive value |
4296 | + * and bigger than the minimumWidth value. |
4297 | + */ |
4298 | +void ViewColumnPrivate::setMaximumWidth(qreal width) |
4299 | +{ |
4300 | + if (qFuzzyCompare(maximumWidth, width)) { |
4301 | + return; |
4302 | + } |
4303 | + if (width < 0.0) { |
4304 | + qmlInfo(q_func()) << "maximumWidth cannot be a negative value"; |
4305 | + return; |
4306 | + } |
4307 | + if (width < minimumWidth) { |
4308 | + qmlInfo(q_func()) << "maximumWidth is smaller than minimumWidth"; |
4309 | + return; |
4310 | + } |
4311 | + maximumWidth = width; |
4312 | + Q_EMIT q_func()->maximumWidthChanged(); |
4313 | + setPreferredWidth(preferredWidth); |
4314 | + recalculateLayoutContent(); |
4315 | +} |
4316 | + |
4317 | +/*! |
4318 | + * \qmlproperty real ViewColumn::preferredWidth |
4319 | + * The property holds the preferred width of the column. The value must be situated |
4320 | + * in between minimumWidth and maximumWidth. In case fillWidth is set, the value will |
4321 | + * hold the actual width of the column, but setting its value will not affect the |
4322 | + * width of the column. |
4323 | + */ |
4324 | +void ViewColumnPrivate::setPreferredWidth(qreal width, bool notify) |
4325 | +{ |
4326 | + // clamp |
4327 | + width = UCMathUtils::clamp(width, minimumWidth, maximumWidth); |
4328 | + if (qFuzzyCompare(preferredWidth, width)) { |
4329 | + return; |
4330 | + } |
4331 | + preferredWidth = width; |
4332 | + if (notify) { |
4333 | + Q_EMIT q_func()->preferredWidthChanged(); |
4334 | + recalculateLayoutContent(); |
4335 | + } |
4336 | +} |
4337 | + |
4338 | +/*! |
4339 | + * \qmlproperty bool ViewColumn::fillWidth |
4340 | + * If set, the column width will take the space available after all the other |
4341 | + * columns with non-fill width are configured. This means that if there is more |
4342 | + * than one column configured to fill width, the reminder width is split evenly |
4343 | + * between these columns. If all columns are configured to fill width, the colum |
4344 | + * widths will be split evenly between all the columns. |
4345 | + * \note When a column configured with fillWidth is resized, the properties will |
4346 | + * not be altered, but the fillWidth wioll no longer be taken into account. Instead, |
4347 | + * the preferredWidth will drive the width of that column from that point on. |
4348 | + */ |
4349 | +void ViewColumnPrivate::setFillWidth(bool fill) |
4350 | +{ |
4351 | + if (fill == fillWidth) { |
4352 | + return; |
4353 | + } |
4354 | + fillWidth = fill; |
4355 | + Q_EMIT q_func()->fillWidthChanged(); |
4356 | + recalculateLayoutContent(); |
4357 | +} |
4358 | + |
4359 | +bool ViewColumn::resize(qreal delta) |
4360 | +{ |
4361 | + Q_D(ViewColumn); |
4362 | + // apply limits |
4363 | + qreal newWidth = d->preferredWidth + delta; |
4364 | + // clamp |
4365 | + newWidth = UCMathUtils::clamp(newWidth, d->minimumWidth, d->maximumWidth); |
4366 | + if (newWidth != d->preferredWidth) { |
4367 | + d->resized = true; |
4368 | + d->setPreferredWidth(newWidth); |
4369 | + return true; |
4370 | + } |
4371 | + return false; |
4372 | +} |
4373 | + |
4374 | +/****************************************************************************** |
4375 | + * SplitViewLayout layouts configuration |
4376 | + */ |
4377 | +/*! |
4378 | + * \qmltype SplitViewLayout |
4379 | + * \inmodule Ubuntu.Components.Labs |
4380 | + * \ingroup ubuntu-labs |
4381 | + * \brief Layout configuration for SplitView. |
4382 | + * |
4383 | + * SplitViewLayout defines a layout configuration and the condition when the layout is |
4384 | + * expected to be applied. In case multiple layout conditions evaluate to true, the first |
4385 | + * one in the list will be activated. |
4386 | + */ |
4387 | +SplitViewLayout::SplitViewLayout(QObject *parent) |
4388 | + : QObject(*(new SplitViewLayoutPrivate), parent) |
4389 | +{ |
4390 | +} |
4391 | + |
4392 | +void SplitViewLayoutPrivate::columns_Append(QQmlListProperty<ViewColumn> *list, ViewColumn* data) |
4393 | +{ |
4394 | + SplitViewLayout *layout = static_cast<SplitViewLayout*>(list->object); |
4395 | + SplitViewLayoutPrivate *d = SplitViewLayoutPrivate::get(layout); |
4396 | + ViewColumnPrivate::get(data)->column = d->columnData.size(); |
4397 | + // make sure ViewColumn is parented to the layout definition |
4398 | + data->setParent(layout); |
4399 | + d->columnData.append(data); |
4400 | + Q_EMIT layout->columnsChanged(); |
4401 | +} |
4402 | +int SplitViewLayoutPrivate::columns_Count(QQmlListProperty<ViewColumn> *list) |
4403 | +{ |
4404 | + SplitViewLayout *layout = static_cast<SplitViewLayout*>(list->object); |
4405 | + SplitViewLayoutPrivate *d = SplitViewLayoutPrivate::get(layout); |
4406 | + return d->columnData.size(); |
4407 | +} |
4408 | +ViewColumn *SplitViewLayoutPrivate::columns_At(QQmlListProperty<ViewColumn> *list, int index) |
4409 | +{ |
4410 | + SplitViewLayout *layout = static_cast<SplitViewLayout*>(list->object); |
4411 | + SplitViewLayoutPrivate *d = SplitViewLayoutPrivate::get(layout); |
4412 | + return d->columnData.at(index); |
4413 | +} |
4414 | +void SplitViewLayoutPrivate::columns_Clear(QQmlListProperty<ViewColumn> *list) |
4415 | +{ |
4416 | + SplitViewLayout *layout = static_cast<SplitViewLayout*>(list->object); |
4417 | + SplitViewLayoutPrivate *d = SplitViewLayoutPrivate::get(layout); |
4418 | + qDeleteAll(d->columnData); |
4419 | + d->columnData.clear(); |
4420 | + Q_EMIT layout->columnsChanged(); |
4421 | +} |
4422 | + |
4423 | +/*! |
4424 | + * \qmlproperty list<ViewColumn> SplitViewLayout::columns |
4425 | + * \default |
4426 | + * The property holds the column configurations for the layout. If the view has more |
4427 | + * columns than the configuration specifies, the extra columns will be hidden (their |
4428 | + * visible property will be set to false!). Also, the hidden column sizes may change, |
4429 | + * therefore size-sensitive content must be aware of this. |
4430 | + */ |
4431 | +QQmlListProperty<UT_PREPEND_NAMESPACE(ViewColumn)> SplitViewLayoutPrivate::columns() |
4432 | +{ |
4433 | + Q_Q(SplitViewLayout); |
4434 | + return QQmlListProperty<UT_PREPEND_NAMESPACE(ViewColumn)>(q, &columnData, |
4435 | + &columns_Append, |
4436 | + &columns_Count, |
4437 | + &columns_At, |
4438 | + &columns_Clear); |
4439 | +} |
4440 | + |
4441 | +/*! |
4442 | + * \qmlproperty bool SplitViewLayout::when |
4443 | + * Specifies the condition when to apply the layout. Usually holds a binding which |
4444 | + * evaluates to true or false to activate the layout. |
4445 | + */ |
4446 | + |
4447 | +UT_NAMESPACE_END |
4448 | |
4449 | === modified file 'src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp' |
4450 | --- src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-08-06 00:55:32 +0000 |
4451 | +++ src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-08-31 08:57:26 +0000 |
4452 | @@ -89,6 +89,13 @@ |
4453 | #include <privates/ucpagewrapper_p.h> |
4454 | #include <privates/appheaderbase_p.h> |
4455 | #include <privates/ucscrollbarutils_p.h> |
4456 | +#include <actionlist_p.h> |
4457 | +#include <exclusivegroup_p.h> |
4458 | +#include <menu_p.h> |
4459 | +#include <menubar_p.h> |
4460 | +#include <menugroup_p.h> |
4461 | + |
4462 | +#include <splitview_p.h> |
4463 | |
4464 | // styles |
4465 | #include <ucbottomedgestyle_p.h> |
4466 | @@ -374,6 +381,8 @@ |
4467 | qmlRegisterType<UCPageTreeNode>(uri, 1, 3, "PageTreeNode"); |
4468 | qmlRegisterType<UCPopupContext>(uri, 1, 3, "PopupContext"); |
4469 | qmlRegisterType<UCMainViewBase>(uri, 1, 3, "MainViewBase"); |
4470 | + qmlRegisterType<ActionList>(uri, 1, 3, "ActionList"); |
4471 | + qmlRegisterType<ExclusiveGroup>(uri, 1, 3, "ExclusiveGroup"); |
4472 | } |
4473 | |
4474 | void UbuntuToolkitModule::undefineModule() |
4475 | @@ -404,15 +413,20 @@ |
4476 | */ |
4477 | void UbuntuLabsModule::initializeModule(QQmlEngine *engine, QQmlExtensionPlugin *plugin) |
4478 | { |
4479 | - Q_UNUSED(engine); |
4480 | Q_UNUSED(plugin); |
4481 | + |
4482 | + // qmlplugindump requires that quickutils is initialized. |
4483 | + QuickUtils::instance(engine); |
4484 | } |
4485 | |
4486 | void UbuntuLabsModule::defineModule(const char *uri) |
4487 | { |
4488 | - Q_UNUSED(uri); |
4489 | - // a fake component so we can have the module types file created |
4490 | - qmlRegisterType<QObject>(uri, 1, 0, "ZiObject"); |
4491 | + qmlRegisterType<SplitView>(uri, 1, 0, "SplitView"); |
4492 | + qmlRegisterType<SplitViewLayout>(uri, 1, 0, "SplitViewLayout"); |
4493 | + qmlRegisterType<ViewColumn>(uri, 1, 0, "ViewColumn"); |
4494 | + qmlRegisterType<Menu>(uri, 1, 0, "Menu"); |
4495 | + qmlRegisterType<MenuBar>(uri, 1, 0, "MenuBar"); |
4496 | + qmlRegisterType<MenuGroup>(uri, 1, 0, "MenuGroup"); |
4497 | } |
4498 | |
4499 | void UbuntuLabsModule::undefineModule() |
4500 | |
4501 | === modified file 'src/Ubuntu/UbuntuToolkit/ucaction.cpp' |
4502 | --- src/Ubuntu/UbuntuToolkit/ucaction.cpp 2016-07-07 07:21:48 +0000 |
4503 | +++ src/Ubuntu/UbuntuToolkit/ucaction.cpp 2016-08-31 08:57:26 +0000 |
4504 | @@ -17,6 +17,7 @@ |
4505 | #include "ucaction_p.h" |
4506 | #include "quickutils_p.h" |
4507 | #include "ucactioncontext_p.h" |
4508 | +#include "exclusivegroup_p.h" |
4509 | |
4510 | #include <QtDebug> |
4511 | #include <QtQml/QQmlInfo> |
4512 | @@ -149,6 +150,18 @@ |
4513 | * text: "&Call" |
4514 | * } |
4515 | * \endqml |
4516 | + * |
4517 | + * \section2 Checkable property |
4518 | + * Since Ubuntu.Components 1.3 Action supports the checkable/checked properties. |
4519 | + * \qml |
4520 | + * Button { |
4521 | + * action: Action { |
4522 | + * checkable: true |
4523 | + * checked: false |
4524 | + * } |
4525 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.red |
4526 | + * } |
4527 | + * \endqml |
4528 | */ |
4529 | |
4530 | /*! |
4531 | @@ -160,6 +173,14 @@ |
4532 | */ |
4533 | |
4534 | /*! |
4535 | + * \qmlsignal Action::toggled(bool value) |
4536 | + * Signal called when the action's checked property changes. |
4537 | + * \note The toggled signal should be used for checkable actions rather than the |
4538 | + * triggered signal. |
4539 | + * \sa Action::checkable, Action::checked, ExclusiveGroup |
4540 | + */ |
4541 | + |
4542 | +/*! |
4543 | * \qmlproperty string Action::description |
4544 | * User visible secondary description for the action. Description is more verbose |
4545 | * than the \l text and should describe the Action with couple of words. |
4546 | @@ -250,51 +271,17 @@ |
4547 | * \endqml |
4548 | */ |
4549 | |
4550 | -/*! |
4551 | - * \qmlproperty enum Action::parameterType |
4552 | - * Type of the parameter passed to \l trigger and \l triggered. |
4553 | - * Type is an enumeration: |
4554 | - * \list |
4555 | - * \li \b Action.None: No paramater. (default) |
4556 | - * \li \b Action.String: String parameter. |
4557 | - * \li \b Action.Integer: Integer parameter. |
4558 | - * \li \b Action.Bool: Boolean parameter. |
4559 | - * \li \b Action.Real: Single precision floating point parameter. |
4560 | - * \li \b Action.Object: The parameter is an object. |
4561 | - * \endlist |
4562 | - * \qml |
4563 | - * Action { |
4564 | - * id: action |
4565 | - * parameterType: Action.String |
4566 | - * onTriggered: { |
4567 | - * // value arguments now contain strings |
4568 | - * console.log(value); |
4569 | - * } |
4570 | - * Component.onCompleted: action.trigger("Hello World") |
4571 | - * } |
4572 | - * \endqml |
4573 | - */ |
4574 | - |
4575 | -/*! |
4576 | - * \qmlproperty bool Action::enabled |
4577 | - * If set to false the action can not be triggered. Components visualizing the |
4578 | - * action migth either hide the action or make it insensitive. However visibility |
4579 | - * can be controlled separately using the \l visible property. |
4580 | - */ |
4581 | - |
4582 | -/*! |
4583 | - * \qmlproperty bool Action::visible |
4584 | - * Specifies whether the action is visible to the user. Defaults to true. |
4585 | - */ |
4586 | - |
4587 | UCAction::UCAction(QObject *parent) |
4588 | : QObject(parent) |
4589 | + , m_exclusiveGroup(Q_NULLPTR) |
4590 | , m_itemHint(Q_NULLPTR) |
4591 | , m_parameterType(None) |
4592 | , m_factoryIconSource(true) |
4593 | , m_enabled(true) |
4594 | , m_visible(true) |
4595 | , m_published(false) |
4596 | + , m_checkable(false) |
4597 | + , m_checked(false) |
4598 | { |
4599 | generateName(); |
4600 | // FIXME: we need QInputDeviceInfo to detect the keyboard attechment |
4601 | @@ -436,6 +423,163 @@ |
4602 | Q_EMIT shortcutChanged(); |
4603 | } |
4604 | |
4605 | +/*! |
4606 | + * \qmlproperty bool Action::visible |
4607 | + * Specifies whether the action is visible to the user. Defaults to true. |
4608 | + */ |
4609 | +void UCAction::setVisible(bool visible) |
4610 | +{ |
4611 | + if (m_visible == visible) { |
4612 | + return; |
4613 | + } |
4614 | + m_visible = visible; |
4615 | + Q_EMIT visibleChanged(); |
4616 | +} |
4617 | + |
4618 | +/*! |
4619 | + * \qmlproperty bool Action::enabled |
4620 | + * If set to false the action can not be triggered. Components visualizing the |
4621 | + * action migth either hide the action or make it insensitive. However visibility |
4622 | + * can be controlled separately using the \l visible property. |
4623 | + */ |
4624 | +void UCAction::setEnabled(bool enabled) |
4625 | +{ |
4626 | + if (m_enabled == enabled) { |
4627 | + return; |
4628 | + } |
4629 | + m_enabled = enabled; |
4630 | + Q_EMIT enabledChanged(); |
4631 | + |
4632 | +} |
4633 | + |
4634 | +/*! |
4635 | + * \qmlproperty enum Action::parameterType |
4636 | + * Type of the parameter passed to \l trigger and \l triggered. |
4637 | + * Type is an enumeration: |
4638 | + * \list |
4639 | + * \li \b Action.None: No paramater. (default) |
4640 | + * \li \b Action.String: String parameter. |
4641 | + * \li \b Action.Integer: Integer parameter. |
4642 | + * \li \b Action.Bool: Boolean parameter. |
4643 | + * \li \b Action.Real: Single precision floating point parameter. |
4644 | + * \li \b Action.Object: The parameter is an object. |
4645 | + * \endlist |
4646 | + * \qml |
4647 | + * Action { |
4648 | + * id: action |
4649 | + * parameterType: Action.String |
4650 | + * onTriggered: { |
4651 | + * // value arguments now contain strings |
4652 | + * console.log(value); |
4653 | + * } |
4654 | + * Component.onCompleted: action.trigger("Hello World") |
4655 | + * } |
4656 | + * \endqml |
4657 | + */ |
4658 | +void UCAction::setParameterType(UCAction::Type type) |
4659 | +{ |
4660 | + if (m_parameterType == type) { |
4661 | + return; |
4662 | + } |
4663 | + m_parameterType = type; |
4664 | + Q_EMIT parameterTypeChanged(); |
4665 | +} |
4666 | + |
4667 | +/*! |
4668 | + * \qmlproperty bool Action::checkable |
4669 | + * \since Ubuntu.Components 1.3 |
4670 | + * Whether the action can be checked. Defaults to false. |
4671 | + * \sa Action::checked, Action::toggled, ExclusiveGroup |
4672 | + */ |
4673 | +void UCAction::setCheckable(bool checkable) |
4674 | +{ |
4675 | + if (m_checkable == checkable) { |
4676 | + return; |
4677 | + } |
4678 | + m_checkable = checkable; |
4679 | + Q_EMIT checkableChanged(); |
4680 | + |
4681 | + // If the Action is already checked, assert the check state. |
4682 | + if (m_checked) |
4683 | + Q_EMIT toggled(m_checkable); |
4684 | +} |
4685 | + |
4686 | +/*! |
4687 | + * \qmlproperty bool Action::checked |
4688 | + * \since Ubuntu.Components 1.3 |
4689 | + * If the action is checkable, this property reflects its checked state. Defaults to false. |
4690 | + * Its value is also false while checkable is false. |
4691 | + * \sa Action::checkable, Action::toggled, ExclusiveGroup |
4692 | + */ |
4693 | +void UCAction::setChecked(bool checked) |
4694 | +{ |
4695 | + if (m_checked == checked) { |
4696 | + return; |
4697 | + } |
4698 | + m_checked = checked; |
4699 | + |
4700 | + if (m_checkable) { |
4701 | + Q_EMIT toggled(checked); |
4702 | + } |
4703 | +} |
4704 | + |
4705 | +/*! |
4706 | + * \qmlproperty ExclusiveGroup Action::exclusiveGroup |
4707 | + * \since Ubuntu.Components 1.3 |
4708 | + * The \l ExclusiveGroup associated with this action. |
4709 | + * An exclusive group allows the \l checked property to belinked to other actions, |
4710 | + * as in radio controls. |
4711 | + * \qml |
4712 | + * Column { |
4713 | + * ExclusiveGroup { |
4714 | + * Action { |
4715 | + * id: action1 |
4716 | + * checkable: true |
4717 | + * checked: true |
4718 | + * } |
4719 | + * Action { |
4720 | + * id: action2 |
4721 | + * checkable: true |
4722 | + * } |
4723 | + * Action { |
4724 | + * id: action3 |
4725 | + * checkable: true |
4726 | + * } |
4727 | + * } |
4728 | + * |
4729 | + * Button { |
4730 | + * action: action1 |
4731 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.red |
4732 | + * } |
4733 | + * Button { |
4734 | + * action: action2 |
4735 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.red |
4736 | + * } |
4737 | + * Button { |
4738 | + * action: action3 |
4739 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.grey |
4740 | + * } |
4741 | + * } |
4742 | + * \endqml |
4743 | + */ |
4744 | +void UCAction::setExclusiveGroup(ExclusiveGroup *exclusiveGroup) |
4745 | +{ |
4746 | + if (m_exclusiveGroup == exclusiveGroup) { |
4747 | + return; |
4748 | + } |
4749 | + |
4750 | + if (m_exclusiveGroup) { |
4751 | + m_exclusiveGroup->unbindCheckable(this); |
4752 | + } |
4753 | + |
4754 | + m_exclusiveGroup = exclusiveGroup; |
4755 | + |
4756 | + if (m_exclusiveGroup) { |
4757 | + m_exclusiveGroup->bindCheckable(this); |
4758 | + } |
4759 | + Q_EMIT exclusiveGroupChanged(); |
4760 | +} |
4761 | + |
4762 | bool UCAction::event(QEvent *event) |
4763 | { |
4764 | if (event->type() != QEvent::Shortcut) |
4765 | @@ -475,6 +619,11 @@ |
4766 | if (!m_enabled) { |
4767 | return; |
4768 | } |
4769 | + |
4770 | + if (m_checkable && !(m_checked && m_exclusiveGroup)) { |
4771 | + setChecked(!m_checked); |
4772 | + } |
4773 | + |
4774 | if (!isValidType(value.type())) { |
4775 | Q_EMIT triggered(QVariant()); |
4776 | } else { |
4777 | |
4778 | === modified file 'src/Ubuntu/UbuntuToolkit/ucaction_p.h' |
4779 | --- src/Ubuntu/UbuntuToolkit/ucaction_p.h 2016-07-07 07:21:48 +0000 |
4780 | +++ src/Ubuntu/UbuntuToolkit/ucaction_p.h 2016-08-31 08:57:26 +0000 |
4781 | @@ -53,7 +53,7 @@ |
4782 | } |
4783 | } |
4784 | |
4785 | -class UCActionAttached; |
4786 | +class ExclusiveGroup; |
4787 | class UBUNTUTOOLKIT_EXPORT UCAction : public QObject |
4788 | { |
4789 | Q_OBJECT |
4790 | @@ -62,19 +62,23 @@ |
4791 | Q_ENUMS(Type) |
4792 | Q_PROPERTY(QString name MEMBER m_name WRITE setName NOTIFY nameChanged) |
4793 | Q_PROPERTY(QString text READ text WRITE setText RESET resetText NOTIFY textChanged) |
4794 | - Q_PROPERTY(QString iconName MEMBER m_iconName WRITE setIconName NOTIFY iconNameChanged) |
4795 | + Q_PROPERTY(QString iconName READ iconName WRITE setIconName NOTIFY iconNameChanged) |
4796 | Q_PROPERTY(QString description MEMBER m_description NOTIFY descriptionChanged) |
4797 | Q_PROPERTY(QString keywords MEMBER m_keywords NOTIFY keywordsChanged) |
4798 | - Q_PROPERTY(bool enabled MEMBER m_enabled NOTIFY enabledChanged) |
4799 | - Q_PROPERTY(Type parameterType MEMBER m_parameterType NOTIFY parameterTypeChanged) |
4800 | + Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled NOTIFY enabledChanged) |
4801 | + Q_PROPERTY(Type parameterType READ parameterType WRITE setParameterType NOTIFY parameterTypeChanged) |
4802 | + |
4803 | + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged REVISION 1) |
4804 | + Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY toggled REVISION 1) |
4805 | + Q_PROPERTY(ExclusiveGroup* exclusiveGroup READ exclusiveGroup WRITE setExclusiveGroup NOTIFY exclusiveGroupChanged REVISION 1) |
4806 | |
4807 | // Toolkit Actions API |
4808 | - Q_PROPERTY(QUrl iconSource MEMBER m_iconSource WRITE setIconSource NOTIFY iconSourceChanged) |
4809 | - Q_PROPERTY(bool visible MEMBER m_visible NOTIFY visibleChanged) |
4810 | + Q_PROPERTY(QUrl iconSource READ iconSource WRITE setIconSource NOTIFY iconSourceChanged) |
4811 | + Q_PROPERTY(bool visible READ visible WRITE setVisible NOTIFY visibleChanged) |
4812 | Q_PROPERTY(QQmlComponent *itemHint MEMBER m_itemHint WRITE setItemHint) |
4813 | |
4814 | // QtQuickControls.Action |
4815 | - Q_PROPERTY(QVariant shortcut MEMBER m_shortcut WRITE setShortcut RESET resetShortcut NOTIFY shortcutChanged REVISION 1) |
4816 | + Q_PROPERTY(QVariant shortcut READ shortcut WRITE setShortcut RESET resetShortcut NOTIFY shortcutChanged REVISION 1) |
4817 | public: |
4818 | enum Type { |
4819 | None, |
4820 | @@ -108,11 +112,26 @@ |
4821 | QString text(); |
4822 | void setText(const QString &text); |
4823 | void resetText(); |
4824 | + QString iconName() const { return m_iconName; } |
4825 | void setIconName(const QString &name); |
4826 | + QUrl iconSource() const { return m_iconSource; } |
4827 | void setIconSource(const QUrl &url); |
4828 | void setItemHint(QQmlComponent *); |
4829 | + QVariant shortcut() const { return m_shortcut; } |
4830 | void setShortcut(const QVariant&); |
4831 | void resetShortcut(); |
4832 | + bool visible() const { return m_visible; } |
4833 | + void setVisible(bool visible); |
4834 | + void setEnabled(bool enabled); |
4835 | + void setParameterType(Type type); |
4836 | + Type parameterType() const { return m_parameterType; } |
4837 | + void setCheckable(bool checkable); |
4838 | + bool isCheckable() const { return m_checkable; } |
4839 | + void setChecked(bool checked); |
4840 | + bool isChecked() const { return m_checkable && m_checked; } |
4841 | + |
4842 | + ExclusiveGroup *exclusiveGroup() const { return m_exclusiveGroup; } |
4843 | + void setExclusiveGroup(ExclusiveGroup *exclusiveGroup); |
4844 | |
4845 | Q_SIGNALS: |
4846 | void nameChanged(); |
4847 | @@ -125,13 +144,18 @@ |
4848 | void iconSourceChanged(); |
4849 | void visibleChanged(); |
4850 | void shortcutChanged(); |
4851 | + Q_REVISION(1) void checkableChanged(); |
4852 | + Q_REVISION(1) void exclusiveGroupChanged(); |
4853 | + |
4854 | void triggered(const QVariant &value); |
4855 | + Q_REVISION(1) void toggled(bool value); |
4856 | |
4857 | public Q_SLOTS: |
4858 | void trigger(const QVariant &value = QVariant()); |
4859 | |
4860 | private: |
4861 | QPODVector<QQuickItem*, 4> m_owningItems; |
4862 | + ExclusiveGroup *m_exclusiveGroup; |
4863 | QString m_name; |
4864 | QString m_text; |
4865 | QString m_iconName; |
4866 | @@ -146,6 +170,8 @@ |
4867 | bool m_enabled:1; |
4868 | bool m_visible:1; |
4869 | bool m_published:1; |
4870 | + bool m_checkable:1; |
4871 | + bool m_checked:1; |
4872 | |
4873 | friend class UCActionContext; |
4874 | friend class UCActionItem; |
4875 | |
4876 | === modified file 'src/Ubuntu/UbuntuToolkit/uclistitem.cpp' |
4877 | --- src/Ubuntu/UbuntuToolkit/uclistitem.cpp 2016-07-14 07:41:06 +0000 |
4878 | +++ src/Ubuntu/UbuntuToolkit/uclistitem.cpp 2016-08-31 08:57:26 +0000 |
4879 | @@ -537,7 +537,7 @@ |
4880 | * there will be no highlight happening if the click happens on the active component. |
4881 | * \qml |
4882 | * import QtQuick 2.4 |
4883 | - * import Ubuntu.Components 1.2 |
4884 | + * import Ubuntu.Components 1.3 |
4885 | * |
4886 | * MainView { |
4887 | * width: units.gu(40) |
4888 | @@ -572,20 +572,29 @@ |
4889 | * onClicked: console.log("clicked on ListItem with trailingActions set") |
4890 | * } |
4891 | * ListItem { |
4892 | - * Label { |
4893 | - * text: "onClicked implemented" |
4894 | + * // shall specify the height when Using ListItemLayout inside ListItem |
4895 | + * height: clickImplementedLayout.height + (divider.visible ? divider.height : 0) |
4896 | + * ListItemLayout { |
4897 | + * id: clickImplementedLayout |
4898 | + * title.text: "onClicked implemented" |
4899 | * } |
4900 | * onClicked: console.log("clicked on ListItem with onClicked implemented") |
4901 | * } |
4902 | * ListItem { |
4903 | - * Label { |
4904 | - * text: "onPressAndHold implemented" |
4905 | + * // shall specify the height when Using ListItemLayout inside ListItem |
4906 | + * height: pressHoldLayout.height + (divider.visible ? divider.height : 0) |
4907 | + * ListItemLayout { |
4908 | + * id: pressHoldLayout |
4909 | + * title.text: "onPressAndHold implemented" |
4910 | * } |
4911 | * onPressAndHold: console.log("long-pressed on ListItem with onPressAndHold implemented") |
4912 | * } |
4913 | * ListItem { |
4914 | - * Label { |
4915 | - * text: "No highlight" |
4916 | + * // shall specify the height when Using ListItemLayout inside ListItem |
4917 | + * height: noHighlightLayout.height + (divider.visible ? divider.height : 0) |
4918 | + * ListItemLayout { |
4919 | + * id: noHighlightLayout |
4920 | + * title.text: "No highlight" |
4921 | * } |
4922 | * } |
4923 | * } |
4924 | @@ -671,7 +680,7 @@ |
4925 | * to the view itself. |
4926 | * \qml |
4927 | * import QtQuick 2.4 |
4928 | - * import Ubuntu.Components 1.2 |
4929 | + * import Ubuntu.Components 1.3 |
4930 | * |
4931 | * Flickable { |
4932 | * width: units.gu(40) |
4933 | @@ -735,7 +744,7 @@ |
4934 | * Example of live drag implementation: |
4935 | * \qml |
4936 | * import QtQuick 2.4 |
4937 | - * import Ubuntu.Components 1.2 |
4938 | + * import Ubuntu.Components 1.3 |
4939 | * |
4940 | * ListView { |
4941 | * model: ListModel { |
4942 | @@ -746,8 +755,11 @@ |
4943 | * } |
4944 | * } |
4945 | * delegate: ListItem { |
4946 | - * Label { |
4947 | - * text: modelData |
4948 | + * // shall specify the height when Using ListItemLayout inside ListItem |
4949 | + * height: modelLayout.height + (divider.visible ? divider.height : 0) |
4950 | + * ListItemLayout { |
4951 | + * id: modelLayout |
4952 | + * title.text: modelData |
4953 | * } |
4954 | * color: dragMode ? "lightblue" : "lightgray" |
4955 | * onPressAndHold: ListView.view.ViewItems.dragMode = |
4956 | @@ -769,7 +781,7 @@ |
4957 | * Example of drag'n'drop implementation: |
4958 | * \qml |
4959 | * import QtQuick 2.4 |
4960 | - * import Ubuntu.Components 1.2 |
4961 | + * import Ubuntu.Components 1.3 |
4962 | * |
4963 | * ListView { |
4964 | * model: ListModel { |
4965 | @@ -780,8 +792,11 @@ |
4966 | * } |
4967 | * } |
4968 | * delegate: ListItem { |
4969 | - * Label { |
4970 | - * text: modelData |
4971 | + * // shall specify the height when Using ListItemLayout inside ListItem |
4972 | + * height: modelLayout.height + (divider.visible ? divider.height : 0) |
4973 | + * ListItemLayout { |
4974 | + * id: modelLayout |
4975 | + * title.text: modelData |
4976 | * } |
4977 | * color: dragMode ? "lightblue" : "lightgray" |
4978 | * onPressAndHold: ListView.view.ViewItems.dragMode = |
4979 | @@ -815,13 +830,16 @@ |
4980 | * can still be implemented with these kind of lists as well. |
4981 | * \qml |
4982 | * import QtQuick 2.4 |
4983 | - * import Ubuntu.Components 1.2 |
4984 | + * import Ubuntu.Components 1.3 |
4985 | * |
4986 | * ListView { |
4987 | * model: ["plum", "peach", "pomegrenade", "pear", "banana"] |
4988 | * delegate: ListItem { |
4989 | - * Label { |
4990 | - * text: modelData |
4991 | + * // shall specify the height when Using ListItemLayout inside ListItem |
4992 | + * height: modelLayout.height + (divider.visible ? divider.height : 0) |
4993 | + * ListItemLayout { |
4994 | + * id: modelLayout |
4995 | + * title.text: modelData |
4996 | * } |
4997 | * color: dragMode ? "lightblue" : "lightgray" |
4998 | * onPressAndHold: ListView.view.ViewItems.dragMode = |
4999 | @@ -849,14 +867,17 @@ |
5000 | * \l ViewItems::dragUpdated signal handler. |
The diff has been truncated for viewing.