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