Merge lp:~zeller-benjamin/ubuntu-ui-toolkit/trusty-builds into lp:ubuntu-ui-toolkit

Proposed by Benjamin Zeller
Status: Superseded
Proposed branch: lp:~zeller-benjamin/ubuntu-ui-toolkit/trusty-builds
Merge into: lp:ubuntu-ui-toolkit
Diff against target: 7454 lines (+5770/-511)
69 files modified
apicheck/apicheck.pro (+2/-4)
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 (+13/-13)
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)
ubuntu-ui-toolkit-launcher/ubuntu-ui-toolkit-launcher.pro (+2/-3)
To merge this branch: bzr merge lp:~zeller-benjamin/ubuntu-ui-toolkit/trusty-builds
Reviewer Review Type Date Requested Status
Zoltan Balogh Pending
Review via email: mp+304850@code.launchpad.net

This proposal has been superseded by a proposal from 2016-09-03.

Commit message

- Some out of source build fixes
- Implicit casting of QPointer<QObject> to QObject* does not work on trusty

Description of the change

- Some out of source build fixes
- Implicit casting of QPointer<QObject> to QObject* does not work on trusty

To post a comment you must log in.

Unmerged revisions

2090. By Benjamin Zeller

Implicit casting of QPointer<QObject> to QObject* does not work on trusty

2089. By Benjamin Zeller

Fix out of source builds

Preview Diff

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

Subscribers

People subscribed via source and target branches

to status/vote changes: