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