Merge lp:~nick-dedekind/ubuntu-ui-toolkit/exclusiveGroup into lp:ubuntu-ui-toolkit/staging
- exclusiveGroup
- Merge into staging
Status: | Merged |
---|---|
Approved by: | Zoltan Balogh |
Approved revision: | 1353 |
Merged at revision: | 2077 |
Proposed branch: | lp:~nick-dedekind/ubuntu-ui-toolkit/exclusiveGroup |
Merge into: | lp:ubuntu-ui-toolkit/staging |
Diff against target: |
1025 lines (+720/-56) 13 files modified
components.api (+14/-3) src/Ubuntu/Components/1.3/ActionList.qml (+0/-47) src/Ubuntu/Components/ComponentModule.pro (+0/-1) src/Ubuntu/Components/qmldir (+0/-1) src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro (+6/-2) 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/ubuntutoolkitmodule.cpp (+4/-0) src/Ubuntu/UbuntuToolkit/ucaction.cpp (+124/-0) src/Ubuntu/UbuntuToolkit/ucaction_p.h (+19/-1) tests/unit/components/tst_action.qml (+107/-1) |
To merge this branch: | bzr merge lp:~nick-dedekind/ubuntu-ui-toolkit/exclusiveGroup |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
ubuntu-sdk-build-bot | continuous-integration | Approve | |
Zoltan Balogh | Approve | ||
Zsombor Egri | Approve | ||
PS Jenkins bot | continuous-integration | Pending | |
Review via email: mp+297921@code.launchpad.net |
This proposal supersedes a proposal from 2016-06-07.
Commit message
Introduced Action states & ExclusiveGroup action list
Description of the change
Added Action::state.
Introduced ExclusiveGroup action list.
Moved ActionList to cpp.
Zsombor Egri (zsombi) wrote : Posted in a previous version of this proposal | # |
Zoltan Balogh (bzoltan) wrote : Posted in a previous version of this proposal | # |
Please retarget to the staging branch, thank you
Zsombor Egri (zsombi) wrote : | # |
There are few things to get sorted out still, the code looks good in general.
We are also planning to move all our code to UbuntuToolkit library and UbuntuTookit NS (see https:/
Nick Dedekind (nick-dedekind) wrote : | # |
> There are few things to get sorted out still, the code looks good in general.
>
> We are also planning to move all our code to UbuntuToolkit library and
> UbuntuTookit NS (see https:/
> toolkit/
> your stuff under the lib.
I think I've addressed all the above and comments.
I've left the action::checked as a boolean rather than a tristate. On reflection, I believe that tristate should be handled in the implementer (ie Checkbox). Can't really get my head around tristate with Exclusive groups etc, as I don't think it makes much sense.
The mixed state doesn't really involve a "trigger", since it's not selectable.
Feel free to disagree :)
Zsombor Egri (zsombi) wrote : | # |
Sorry Nick, forgot to mention that UC prefix is no longer mandated. Let's get rid of it here too.
And with that change, we're all done! Thanks a lot!
Nick Dedekind (nick-dedekind) wrote : | # |
> Sorry Nick, forgot to mention that UC prefix is no longer mandated. Let's get
> rid of it here too.
>
> And with that change, we're all done! Thanks a lot!
I've removed the UC prefix.
Zsombor Egri (zsombi) wrote : | # |
All cool now!!! Thanks Nick!
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
None: https:/
SUCCESS: https:/
Timo Jyrinki (timo-jyrinki) wrote : | # |
This MP seems to have a real failure, not failing only on the armhf where there was faulty phone device doing the CI.
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1345
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1347
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1352
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
Zoltan Balogh (bzoltan) : | # |
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1352
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
FAILED: Autolanding.
More details in the following jenkins job:
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
None: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
ubuntu-sdk-build-bot (ubuntu-sdk-build-bot) wrote : | # |
PASSED: Continuous integration, rev:1353
https:/
Executed test runs:
None: https:/
Click here to trigger a rebuild:
https:/
Preview Diff
1 | === modified file 'components.api' |
2 | --- components.api 2016-08-09 03:48:53 +0000 |
3 | +++ components.api 2016-08-22 08:24:13 +0000 |
4 | @@ -10,13 +10,17 @@ |
5 | readonly property bool pressed |
6 | readonly property UCMargins sensingMargins |
7 | Ubuntu.Components.Action 1.3 1.0 0.1 UCAction: QtObject |
8 | + property bool checkable 1.3 |
9 | + property bool checked 1.3 |
10 | property string description |
11 | property bool enabled |
12 | + property ExclusiveGroup exclusiveGroup 1.3 |
13 | property string iconName |
14 | property url iconSource |
15 | property Component itemHint |
16 | property string keywords |
17 | signal triggered(var value) |
18 | + signal toggled(bool value) 1.3 |
19 | function trigger(var value) |
20 | function trigger() |
21 | property string name |
22 | @@ -59,9 +63,12 @@ |
23 | Ubuntu.Components.ActionList 1.0 0.1: QtObject |
24 | property list<Action> actions |
25 | default property list<Action> children |
26 | -Ubuntu.Components.ActionList 1.3: QtObject |
27 | - property list<Action> actions |
28 | - default property list<Action> children |
29 | +Ubuntu.Components.ActionList 1.3 ActionList: QtObject |
30 | + default property list<Action> actions |
31 | + signal added(Action action) |
32 | + signal removed(Action action) |
33 | + function addAction(Action action) |
34 | + function removeAction(Action action) |
35 | Ubuntu.Components.ActionManager 1.0 0.1 UCActionManager: QtObject |
36 | default property list<Action> actions |
37 | readonly property ActionContext globalContext |
38 | @@ -458,6 +465,10 @@ |
39 | OperationPending |
40 | Ubuntu.Metrics.Event: Enum |
41 | UserInterfaceReady |
42 | +Ubuntu.Components.ExclusiveGroup 1.3 ExclusiveGroup: ActionList |
43 | + readonly property QtObject current |
44 | + function bindCheckable(QtObject object) |
45 | + function unbindCheckable(QtObject object) |
46 | Ubuntu.Components.ListItems.Expandable 1.0 0.1: Empty |
47 | property bool collapseOnClick |
48 | property double collapsedHeight |
49 | |
50 | === removed file 'src/Ubuntu/Components/1.3/ActionList.qml' |
51 | --- src/Ubuntu/Components/1.3/ActionList.qml 2016-05-25 12:48:10 +0000 |
52 | +++ src/Ubuntu/Components/1.3/ActionList.qml 1970-01-01 00:00:00 +0000 |
53 | @@ -1,47 +0,0 @@ |
54 | -/* |
55 | - * Copyright 2012 Canonical Ltd. |
56 | - * |
57 | - * This program is free software; you can redistribute it and/or modify |
58 | - * it under the terms of the GNU Lesser General Public License as published by |
59 | - * the Free Software Foundation; version 3. |
60 | - * |
61 | - * This program is distributed in the hope that it will be useful, |
62 | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
63 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
64 | - * GNU Lesser General Public License for more details. |
65 | - * |
66 | - * You should have received a copy of the GNU Lesser General Public License |
67 | - * along with this program. If not, see <http://www.gnu.org/licenses/>. |
68 | - */ |
69 | - |
70 | -import QtQuick 2.4 |
71 | -import Ubuntu.Components 1.3 |
72 | - |
73 | -/*! |
74 | - \qmltype ActionList |
75 | - \inqmlmodule Ubuntu.Components |
76 | - \ingroup ubuntu |
77 | - \brief List of \l Action items |
78 | -*/ |
79 | - |
80 | -QtObject { |
81 | - id: list |
82 | - // internal objects using nested elements, |
83 | - // which isn't allowed by QtObject; this fix makes this possible |
84 | - /*! |
85 | - Default property to allow adding of children. |
86 | - \qmlproperty list<Action> children |
87 | - \default |
88 | - */ |
89 | - default property alias children: list.actions |
90 | - |
91 | - /*! |
92 | - List of already defined actions when not defining them as children of the ActionList. |
93 | - Note that when you set this property, the children of the ActionList will be ignored, |
94 | - so do not set the list and define children. |
95 | - |
96 | - The advantage of setting actions over using the children is that the same |
97 | - \l Action items can be used in several sets of actions. |
98 | - */ |
99 | - property list<Action> actions |
100 | -} |
101 | |
102 | === modified file 'src/Ubuntu/Components/ComponentModule.pro' |
103 | --- src/Ubuntu/Components/ComponentModule.pro 2016-07-29 13:21:05 +0000 |
104 | +++ src/Ubuntu/Components/ComponentModule.pro 2016-08-22 08:24:13 +0000 |
105 | @@ -78,7 +78,6 @@ |
106 | |
107 | #1.3 |
108 | QML_FILES += 1.3/ActionBar.qml \ |
109 | - 1.3/ActionList.qml \ |
110 | 1.3/ActivityIndicator.qml \ |
111 | 1.3/AdaptivePageLayout.qml \ |
112 | 1.3/AnimatedItem.qml \ |
113 | |
114 | === modified file 'src/Ubuntu/Components/qmldir' |
115 | --- src/Ubuntu/Components/qmldir 2016-07-29 13:21:05 +0000 |
116 | +++ src/Ubuntu/Components/qmldir 2016-08-22 08:24:13 +0000 |
117 | @@ -96,7 +96,6 @@ |
118 | ################################################# |
119 | #version 1.3 |
120 | ActionBar 1.3 1.3/ActionBar.qml |
121 | -ActionList 1.3 1.3/ActionList.qml |
122 | AdaptivePageLayout 1.3 1.3/AdaptivePageLayout.qml |
123 | PageColumnsLayout 1.3 1.3/PageColumnsLayout.qml |
124 | PageColumn 1.3 1.3/PageColumn.qml |
125 | |
126 | === modified file 'src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro' |
127 | --- src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro 2016-08-04 17:25:09 +0000 |
128 | +++ src/Ubuntu/UbuntuToolkit/UbuntuToolkit.pro 2016-08-22 08:24:13 +0000 |
129 | @@ -149,7 +149,9 @@ |
130 | privates/appheaderbase_p.h \ |
131 | label_p.h \ |
132 | ucbottomedgeregion_p_p.h \ |
133 | - privates/ucscrollbarutils_p.h |
134 | + privates/ucscrollbarutils_p.h \ |
135 | + actionlist_p.h \ |
136 | + exclusivegroup_p.h |
137 | |
138 | SOURCES += \ |
139 | uctheme.cpp \ |
140 | @@ -226,7 +228,9 @@ |
141 | privates/ucpagewrapper.cpp \ |
142 | privates/ucpagewrapperincubator.cpp \ |
143 | privates/appheaderbase.cpp \ |
144 | - privates/ucscrollbarutils.cpp |
145 | + privates/ucscrollbarutils.cpp \ |
146 | + actionlist.cpp \ |
147 | + exclusivegroup.cpp |
148 | |
149 | # adapters |
150 | SOURCES += $$PWD/adapters/alarmsadapter_organizer.cpp |
151 | |
152 | === added file 'src/Ubuntu/UbuntuToolkit/actionlist.cpp' |
153 | --- src/Ubuntu/UbuntuToolkit/actionlist.cpp 1970-01-01 00:00:00 +0000 |
154 | +++ src/Ubuntu/UbuntuToolkit/actionlist.cpp 2016-08-22 08:24:13 +0000 |
155 | @@ -0,0 +1,158 @@ |
156 | +/* |
157 | + * Copyright 2016 Canonical Ltd. |
158 | + * |
159 | + * This program is free software; you can redistribute it and/or modify |
160 | + * it under the terms of the GNU Lesser General Public License as published by |
161 | + * the Free Software Foundation; version 3. |
162 | + * |
163 | + * This program is distributed in the hope that it will be useful, |
164 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
165 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
166 | + * GNU Lesser General Public License for more details. |
167 | + * |
168 | + * You should have received a copy of the GNU Lesser General Public License |
169 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
170 | + */ |
171 | + |
172 | +#include "actionlist_p.h" |
173 | +#include "ucaction_p.h" |
174 | + |
175 | +UT_NAMESPACE_BEGIN |
176 | + |
177 | +/*! |
178 | + * \qmltype ActionList |
179 | + * \inqmlmodule Ubuntu.Components |
180 | + * \ingroup ubuntu |
181 | + * \brief List of \l Action items |
182 | + * An ActionList provies a way of grouping actions together. |
183 | + * \qml |
184 | + * ActionList { |
185 | + * Action { |
186 | + * id: action1 |
187 | + * } |
188 | + * Action { |
189 | + * id: action2 |
190 | + * } |
191 | + * } |
192 | + * \endqml |
193 | + */ |
194 | +/*! |
195 | + * \qmlsignal ActionList::added(Action action) |
196 | + * \since Ubuntu.Components 1.3 |
197 | + * Signal called when an action is added to the list |
198 | + */ |
199 | +/*! |
200 | + * \qmlsignal ActionList::removed(Action action) |
201 | + * \since Ubuntu.Components 1.3 |
202 | + * Signal called when an action is removed from the list |
203 | + */ |
204 | +ActionList::ActionList(QObject *parent) |
205 | + : QObject(parent) |
206 | +{ |
207 | +} |
208 | + |
209 | +/*! |
210 | + * \qmlmethod ActionList::addAction(Action action) |
211 | + * \since Ubuntu.Components 1.3 |
212 | + * Adds an Action to the list programatically. |
213 | + * \qml |
214 | + * Item { |
215 | + * Instantiator { |
216 | + * model: 4 |
217 | + * onObjectAdded: actionList.addAction(object) |
218 | + * onObjectRemoved: actionList.removeAction(object) |
219 | + * |
220 | + * Action {} |
221 | + * } |
222 | + * |
223 | + * ActionList { |
224 | + * id: actionList |
225 | + * } |
226 | + * } |
227 | + * \endqml |
228 | + * \sa ActionList::removeAction |
229 | + */ |
230 | +void ActionList::addAction(UCAction *action) |
231 | +{ |
232 | + if (m_actions.contains(action)) { |
233 | + return; |
234 | + } |
235 | + m_actions.append(action); |
236 | + Q_EMIT added(action); |
237 | +} |
238 | + |
239 | +/*! |
240 | + * \qmlmethod ActionList::removeAction(Action action) |
241 | + * \since Ubuntu.Components 1.3 |
242 | + * Removes an action from the list programatically. |
243 | + * \sa ActionList::addAction |
244 | + */ |
245 | +void ActionList::removeAction(UCAction *action) |
246 | +{ |
247 | + if (!action) { |
248 | + return; |
249 | + } |
250 | + if (m_actions.removeOne(action)) { |
251 | + Q_EMIT removed(action); |
252 | + } |
253 | +} |
254 | + |
255 | +/*! |
256 | + * \qmlproperty list<Action> ActionList::actions |
257 | + * \default |
258 | + * List of Actions in this ActionList |
259 | + * Note that when you set this property, the children of the ActionList will be ignored, |
260 | + * so do not set the list and define children. |
261 | + * |
262 | + * The advantage of setting actions over using the children is that the same |
263 | + * \l Action items can be used in several sets of actions. |
264 | + */ |
265 | +QQmlListProperty<UCAction> ActionList::actions() |
266 | +{ |
267 | + return QQmlListProperty<UCAction>(this, 0, |
268 | + ActionList::append, |
269 | + ActionList::count, |
270 | + ActionList::at, |
271 | + ActionList::clear); |
272 | +} |
273 | + |
274 | +const QList<UCAction*> &ActionList::list() const |
275 | +{ |
276 | + return m_actions; |
277 | +} |
278 | + |
279 | +void ActionList::append(QQmlListProperty<UCAction> *list, UCAction *action) |
280 | +{ |
281 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
282 | + if (actionList) { |
283 | + actionList->addAction(action); |
284 | + } |
285 | +} |
286 | + |
287 | +void ActionList::clear(QQmlListProperty<UCAction> *list) |
288 | +{ |
289 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
290 | + if (actionList) { |
291 | + actionList->m_actions.clear(); |
292 | + } |
293 | +} |
294 | + |
295 | +UCAction* ActionList::at(QQmlListProperty<UCAction> *list, int index) |
296 | +{ |
297 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
298 | + if (actionList) { |
299 | + return actionList->m_actions.value(index, nullptr); |
300 | + } |
301 | + return Q_NULLPTR; |
302 | +} |
303 | + |
304 | +int ActionList::count(QQmlListProperty<UCAction> *list) |
305 | +{ |
306 | + ActionList *actionList = qobject_cast<ActionList*>(list->object); |
307 | + if (actionList) { |
308 | + return actionList->m_actions.count(); |
309 | + } |
310 | + return 0; |
311 | +} |
312 | + |
313 | +UT_NAMESPACE_END |
314 | |
315 | === added file 'src/Ubuntu/UbuntuToolkit/actionlist_p.h' |
316 | --- src/Ubuntu/UbuntuToolkit/actionlist_p.h 1970-01-01 00:00:00 +0000 |
317 | +++ src/Ubuntu/UbuntuToolkit/actionlist_p.h 2016-08-22 08:24:13 +0000 |
318 | @@ -0,0 +1,58 @@ |
319 | +/* |
320 | + * Copyright 2016 Canonical Ltd. |
321 | + * |
322 | + * This program is free software; you can redistribute it and/or modify |
323 | + * it under the terms of the GNU Lesser General Public License as published by |
324 | + * the Free Software Foundation; version 3. |
325 | + * |
326 | + * This program is distributed in the hope that it will be useful, |
327 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
328 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
329 | + * GNU Lesser General Public License for more details. |
330 | + * |
331 | + * You should have received a copy of the GNU Lesser General Public License |
332 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
333 | + */ |
334 | + |
335 | +#ifndef ACTIONLIST_H |
336 | +#define ACTIONLIST_H |
337 | + |
338 | +#include <QObject> |
339 | +#include <QtQml/QQmlListProperty> |
340 | +#include "ubuntutoolkitglobal.h" |
341 | + |
342 | +UT_NAMESPACE_BEGIN |
343 | + |
344 | +class UCAction; |
345 | +class UBUNTUTOOLKIT_EXPORT ActionList : public QObject |
346 | +{ |
347 | + Q_OBJECT |
348 | + Q_PROPERTY(QQmlListProperty<UT_PREPEND_NAMESPACE(UCAction)> actions READ actions) |
349 | + Q_CLASSINFO("DefaultProperty", "actions") |
350 | +public: |
351 | + explicit ActionList(QObject *parent = 0); |
352 | + |
353 | + QQmlListProperty<UCAction> actions(); |
354 | + |
355 | + const QList<UCAction*> &list() const; |
356 | + |
357 | +public Q_SLOTS: |
358 | + void addAction(UT_PREPEND_NAMESPACE(UCAction) *action); |
359 | + void removeAction(UT_PREPEND_NAMESPACE(UCAction) *action); |
360 | + |
361 | +Q_SIGNALS: |
362 | + void added(UCAction *action); |
363 | + void removed(UCAction *action); |
364 | + |
365 | +protected: |
366 | + QList<UCAction*> m_actions; |
367 | + |
368 | + static void append(QQmlListProperty<UCAction> *list, UCAction *action); |
369 | + static void clear(QQmlListProperty<UCAction> *list); |
370 | + static UCAction* at(QQmlListProperty<UCAction> *list, int index); |
371 | + static int count(QQmlListProperty<UCAction> *list); |
372 | +}; |
373 | + |
374 | +UT_NAMESPACE_END |
375 | + |
376 | +#endif // ACTIONLIST_H |
377 | |
378 | === added file 'src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp' |
379 | --- src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp 1970-01-01 00:00:00 +0000 |
380 | +++ src/Ubuntu/UbuntuToolkit/exclusivegroup.cpp 2016-08-22 08:24:13 +0000 |
381 | @@ -0,0 +1,168 @@ |
382 | +/* |
383 | + * Copyright 2016 Canonical Ltd. |
384 | + * |
385 | + * This program is free software; you can redistribute it and/or modify |
386 | + * it under the terms of the GNU Lesser General Public License as published by |
387 | + * the Free Software Foundation; version 3. |
388 | + * |
389 | + * This program is distributed in the hope that it will be useful, |
390 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
391 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
392 | + * GNU Lesser General Public License for more details. |
393 | + * |
394 | + * You should have received a copy of the GNU Lesser General Public License |
395 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
396 | + * |
397 | + */ |
398 | + |
399 | +#include "exclusivegroup_p.h" |
400 | +#include "ucaction_p.h" |
401 | + |
402 | +#include <QSignalMapper> |
403 | + |
404 | +#define CHECKED_PROPERTY "checked" |
405 | + |
406 | +UT_NAMESPACE_BEGIN |
407 | + |
408 | +static const char *checkableSignals[] = { |
409 | + "toggled(bool)", |
410 | + 0 |
411 | +}; |
412 | + |
413 | +static bool isChecked(const QObject *o) |
414 | +{ |
415 | + if (!o) return false; |
416 | + QVariant checkedVariant = o->property(CHECKED_PROPERTY); |
417 | + return checkedVariant.isValid() && checkedVariant.toBool(); |
418 | +} |
419 | + |
420 | +/*! |
421 | + * \qmltype ExclusiveGroup |
422 | + * \inqmlmodule Ubuntu.Components |
423 | + * \since Ubuntu.Components 1.3 |
424 | + * \ingroup ubuntu |
425 | + * \inherits ActionList |
426 | + * \brief ExclusiveGroup provides a way to declare several checkable controls as mutually exclusive. |
427 | + * |
428 | + * The ExclusiveGroup will only allow a single object to have it's checkable property set to "true" |
429 | + * at any one time. The exclusive group accepts child Actions, but objects other than Actions can be |
430 | + * used by using the \l bindCheckable function as long as they support one of the required signals, |
431 | + * and a "checked" property. |
432 | + * \qml |
433 | + * ExclusiveGroup { |
434 | + * Action { |
435 | + * parameterType: Action.Bool |
436 | + * state: true |
437 | + * } |
438 | + * Action { |
439 | + * parameterType: Action.Bool |
440 | + * state: false |
441 | + * } |
442 | + * } |
443 | + * \endqml |
444 | + */ |
445 | +ExclusiveGroup::ExclusiveGroup(QObject *parent) |
446 | + : ActionList(parent) |
447 | + , m_signalMapper(new QSignalMapper(this)) |
448 | + , m_entranceGuard(false) |
449 | +{ |
450 | + connect(this, &ActionList::added, this, &ExclusiveGroup::onActionAdded); |
451 | + connect(this, &ActionList::removed, this, &ExclusiveGroup::onActionRemoved); |
452 | + |
453 | + int index = m_signalMapper->metaObject()->indexOfMethod("map()"); |
454 | + m_updateCurrentMethod = m_signalMapper->metaObject()->method(index); |
455 | + connect(m_signalMapper, static_cast<void(QSignalMapper::*)(QObject *)>(&QSignalMapper::mapped), this, [this](QObject *object) { |
456 | + if (isChecked(object)) { |
457 | + setCurrent(object); |
458 | + } |
459 | + }); |
460 | +} |
461 | + |
462 | +void ExclusiveGroup::onActionAdded(UCAction *action) |
463 | +{ |
464 | + action->setExclusiveGroup(this); |
465 | +} |
466 | + |
467 | +void ExclusiveGroup::onActionRemoved(UCAction *action) |
468 | +{ |
469 | + action->setExclusiveGroup(nullptr); |
470 | +} |
471 | + |
472 | +/*! |
473 | + * \qmlproperty Action ExclusiveGroup::current |
474 | + * Returns the currently checked action |
475 | + */ |
476 | +void ExclusiveGroup::setCurrent(QObject *object) |
477 | +{ |
478 | + if (m_current == object) |
479 | + return; |
480 | + |
481 | + if (m_current) |
482 | + m_current->setProperty(CHECKED_PROPERTY, QVariant(false)); |
483 | + m_current = object; |
484 | + if (m_current) |
485 | + m_current->setProperty(CHECKED_PROPERTY, QVariant(true)); |
486 | + Q_EMIT currentChanged(); |
487 | +} |
488 | + |
489 | +QObject *ExclusiveGroup::current() const |
490 | +{ |
491 | + return m_current.data(); |
492 | +} |
493 | + |
494 | +/*! |
495 | + * \qmlmethod void ExclusiveGroup::bindCheckable(object object) |
496 | + * Explicitly bind an objects checkability to this exclusive group. |
497 | + * \note This only works with objects which support the following signals signals: |
498 | + * \list |
499 | + * \li \b toggled(bool) |
500 | + * \endlist |
501 | + * \qml |
502 | + * Item { |
503 | + * ExclusiveGroup { |
504 | + * id: exclusiveGroup |
505 | + * } |
506 | + * Instantiator { |
507 | + * model: 4 |
508 | + * onObjectAdded: exclusiveGroup.bindCheckable(object) |
509 | + * onObjectRemoved: exclusiveGroup.unbindCheckable(object) |
510 | + * |
511 | + * Action { |
512 | + * checkable: true |
513 | + * } |
514 | + * } |
515 | + * } |
516 | + * \endqml |
517 | + * \sa ExclusiveGroup::unbindCheckable |
518 | + */ |
519 | +void ExclusiveGroup::bindCheckable(QObject *object) |
520 | +{ |
521 | + for (const char **signalName = checkableSignals; *signalName; signalName++) { |
522 | + int signalIndex = object->metaObject()->indexOfSignal(*signalName); |
523 | + if (signalIndex != -1) { |
524 | + QMetaMethod signalMethod = object->metaObject()->method(signalIndex); |
525 | + connect(object, signalMethod, m_signalMapper, m_updateCurrentMethod, Qt::UniqueConnection); |
526 | + m_signalMapper->setMapping(object, object); |
527 | + connect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*)), Qt::UniqueConnection); |
528 | + if (!m_current && isChecked(object)) |
529 | + setCurrent(object); |
530 | + break; |
531 | + } |
532 | + } |
533 | +} |
534 | + |
535 | +/*! |
536 | + * \qmlmethod void ExclusiveGroup::unbindCheckable(object object) |
537 | + * Explicitly unbind an objects checkability from this exclusive group. |
538 | + * \sa ExclusiveGroup::bindCheckable |
539 | + */ |
540 | +void ExclusiveGroup::unbindCheckable(QObject *object) |
541 | +{ |
542 | + if (m_current == object) |
543 | + setCurrent(0); |
544 | + |
545 | + disconnect(object, 0, m_signalMapper, 0); |
546 | + disconnect(object, SIGNAL(destroyed(QObject*)), this, SLOT(unbindCheckable(QObject*))); |
547 | +} |
548 | + |
549 | +UT_NAMESPACE_END |
550 | |
551 | === added file 'src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h' |
552 | --- src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h 1970-01-01 00:00:00 +0000 |
553 | +++ src/Ubuntu/UbuntuToolkit/exclusivegroup_p.h 2016-08-22 08:24:13 +0000 |
554 | @@ -0,0 +1,62 @@ |
555 | +/* |
556 | + * Copyright 2016 Canonical Ltd. |
557 | + * |
558 | + * This program is free software; you can redistribute it and/or modify |
559 | + * it under the terms of the GNU Lesser General Public License as published by |
560 | + * the Free Software Foundation; version 3. |
561 | + * |
562 | + * This program is distributed in the hope that it will be useful, |
563 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
564 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
565 | + * GNU Lesser General Public License for more details. |
566 | + * |
567 | + * You should have received a copy of the GNU Lesser General Public License |
568 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
569 | + * |
570 | + */ |
571 | + |
572 | +#ifndef EXCLUSIVEGROUP_H |
573 | +#define EXCLUSIVEGROUP_H |
574 | + |
575 | +#include "actionlist_p.h" |
576 | +#include "ubuntutoolkitglobal.h" |
577 | + |
578 | +#include <QMetaMethod> |
579 | +#include <QPointer> |
580 | + |
581 | +class QSignalMapper; |
582 | + |
583 | +UT_NAMESPACE_BEGIN |
584 | + |
585 | +class UBUNTUTOOLKIT_EXPORT ExclusiveGroup : public ActionList |
586 | +{ |
587 | + Q_OBJECT |
588 | + Q_PROPERTY(QObject* current READ current NOTIFY currentChanged) |
589 | + |
590 | +public: |
591 | + explicit ExclusiveGroup(QObject *parent = 0); |
592 | + |
593 | + QObject* current() const; |
594 | + |
595 | + Q_INVOKABLE void bindCheckable(QObject* object); |
596 | + Q_INVOKABLE void unbindCheckable(QObject* object); |
597 | + |
598 | +Q_SIGNALS: |
599 | + void currentChanged(); |
600 | + |
601 | +protected Q_SLOTS: |
602 | + void onActionAdded(UCAction* action); |
603 | + void onActionRemoved(UCAction* action); |
604 | + |
605 | +private: |
606 | + void setCurrent(QObject* action); |
607 | + |
608 | + QSignalMapper* m_signalMapper; |
609 | + QPointer<QObject> m_current; |
610 | + QMetaMethod m_updateCurrentMethod; |
611 | + bool m_entranceGuard; |
612 | +}; |
613 | + |
614 | +UT_NAMESPACE_END |
615 | + |
616 | +#endif // EXCLUSIVEGROUP_H |
617 | |
618 | === modified file 'src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp' |
619 | --- src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-08-06 00:55:32 +0000 |
620 | +++ src/Ubuntu/UbuntuToolkit/ubuntutoolkitmodule.cpp 2016-08-22 08:24:13 +0000 |
621 | @@ -89,6 +89,8 @@ |
622 | #include <privates/ucpagewrapper_p.h> |
623 | #include <privates/appheaderbase_p.h> |
624 | #include <privates/ucscrollbarutils_p.h> |
625 | +#include <actionlist_p.h> |
626 | +#include <exclusivegroup_p.h> |
627 | |
628 | // styles |
629 | #include <ucbottomedgestyle_p.h> |
630 | @@ -374,6 +376,8 @@ |
631 | qmlRegisterType<UCPageTreeNode>(uri, 1, 3, "PageTreeNode"); |
632 | qmlRegisterType<UCPopupContext>(uri, 1, 3, "PopupContext"); |
633 | qmlRegisterType<UCMainViewBase>(uri, 1, 3, "MainViewBase"); |
634 | + qmlRegisterType<ActionList>(uri, 1, 3, "ActionList"); |
635 | + qmlRegisterType<ExclusiveGroup>(uri, 1, 3, "ExclusiveGroup"); |
636 | } |
637 | |
638 | void UbuntuToolkitModule::undefineModule() |
639 | |
640 | === modified file 'src/Ubuntu/UbuntuToolkit/ucaction.cpp' |
641 | --- src/Ubuntu/UbuntuToolkit/ucaction.cpp 2016-07-07 07:21:48 +0000 |
642 | +++ src/Ubuntu/UbuntuToolkit/ucaction.cpp 2016-08-22 08:24:13 +0000 |
643 | @@ -17,6 +17,7 @@ |
644 | #include "ucaction_p.h" |
645 | #include "quickutils_p.h" |
646 | #include "ucactioncontext_p.h" |
647 | +#include "exclusivegroup_p.h" |
648 | |
649 | #include <QtDebug> |
650 | #include <QtQml/QQmlInfo> |
651 | @@ -149,6 +150,18 @@ |
652 | * text: "&Call" |
653 | * } |
654 | * \endqml |
655 | + * |
656 | + * \section2 Checkable property |
657 | + * Since Ubuntu.Components 1.3 Action supports the checkable/checked properties. |
658 | + * \qml |
659 | + * Button { |
660 | + * action: Action { |
661 | + * checkable: true |
662 | + * checked: false |
663 | + * } |
664 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.red |
665 | + * } |
666 | + * \endqml |
667 | */ |
668 | |
669 | /*! |
670 | @@ -160,6 +173,14 @@ |
671 | */ |
672 | |
673 | /*! |
674 | + * \qmlsignal Action::toggled(bool value) |
675 | + * Signal called when the action's checked property changes. |
676 | + * \note The toggled signal should be used for checkable actions rather than the |
677 | + * triggered signal. |
678 | + * \sa Action::checkable, Action::checked, ExclusiveGroup |
679 | + */ |
680 | + |
681 | +/*! |
682 | * \qmlproperty string Action::description |
683 | * User visible secondary description for the action. Description is more verbose |
684 | * than the \l text and should describe the Action with couple of words. |
685 | @@ -289,12 +310,15 @@ |
686 | |
687 | UCAction::UCAction(QObject *parent) |
688 | : QObject(parent) |
689 | + , m_exclusiveGroup(Q_NULLPTR) |
690 | , m_itemHint(Q_NULLPTR) |
691 | , m_parameterType(None) |
692 | , m_factoryIconSource(true) |
693 | , m_enabled(true) |
694 | , m_visible(true) |
695 | , m_published(false) |
696 | + , m_checkable(false) |
697 | + , m_checked(false) |
698 | { |
699 | generateName(); |
700 | // FIXME: we need QInputDeviceInfo to detect the keyboard attechment |
701 | @@ -436,6 +460,101 @@ |
702 | Q_EMIT shortcutChanged(); |
703 | } |
704 | |
705 | +/*! |
706 | + * \qmlproperty bool Action::checkable |
707 | + * \since Ubuntu.Components 1.3 |
708 | + * Whether the action can be checked. Defaults to false. |
709 | + * \sa Action::checked, Action::toggled, ExclusiveGroup |
710 | + */ |
711 | +void UCAction::setCheckable(bool checkable) |
712 | +{ |
713 | + if (m_checkable == checkable) { |
714 | + return; |
715 | + } |
716 | + m_checkable = checkable; |
717 | + Q_EMIT checkableChanged(); |
718 | + |
719 | + // If the Action is already checked, assert the check state. |
720 | + if (m_checked) |
721 | + Q_EMIT toggled(m_checkable); |
722 | +} |
723 | + |
724 | +/*! |
725 | + * \qmlproperty bool Action::checked |
726 | + * \since Ubuntu.Components 1.3 |
727 | + * If the action is checkable, this property reflects its checked state. Defaults to false. |
728 | + * Its value is also false while checkable is false. |
729 | + * \sa Action::checkable, Action::toggled, ExclusiveGroup |
730 | + */ |
731 | +void UCAction::setChecked(bool checked) |
732 | +{ |
733 | + if (m_checked == checked) { |
734 | + return; |
735 | + } |
736 | + m_checked = checked; |
737 | + |
738 | + if (m_checkable) { |
739 | + Q_EMIT toggled(checked); |
740 | + } |
741 | +} |
742 | + |
743 | +/*! |
744 | + * \qmlproperty ExclusiveGroup Action::exclusiveGroup |
745 | + * \since Ubuntu.Components 1.3 |
746 | + * The \l ExclusiveGroup associated with this action. |
747 | + * An exclusive group allows the \l checked property to belinked to other actions, |
748 | + * as in radio controls. |
749 | + * \qml |
750 | + * Column { |
751 | + * ExclusiveGroup { |
752 | + * Action { |
753 | + * id: action1 |
754 | + * checkable: true |
755 | + * checked: true |
756 | + * } |
757 | + * Action { |
758 | + * id: action2 |
759 | + * checkable: true |
760 | + * } |
761 | + * Action { |
762 | + * id: action3 |
763 | + * checkable: true |
764 | + * } |
765 | + * } |
766 | + * |
767 | + * Button { |
768 | + * action: action1 |
769 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.red |
770 | + * } |
771 | + * Button { |
772 | + * action: action2 |
773 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.red |
774 | + * } |
775 | + * Button { |
776 | + * action: action3 |
777 | + * color: action.checked ? UbuntuColor.green : UbuntuColor.grey |
778 | + * } |
779 | + * } |
780 | + * \endqml |
781 | + */ |
782 | +void UCAction::setExclusiveGroup(ExclusiveGroup *exclusiveGroup) |
783 | +{ |
784 | + if (m_exclusiveGroup == exclusiveGroup) { |
785 | + return; |
786 | + } |
787 | + |
788 | + if (m_exclusiveGroup) { |
789 | + m_exclusiveGroup->unbindCheckable(this); |
790 | + } |
791 | + |
792 | + m_exclusiveGroup = exclusiveGroup; |
793 | + |
794 | + if (m_exclusiveGroup) { |
795 | + m_exclusiveGroup->bindCheckable(this); |
796 | + } |
797 | + Q_EMIT exclusiveGroupChanged(); |
798 | +} |
799 | + |
800 | bool UCAction::event(QEvent *event) |
801 | { |
802 | if (event->type() != QEvent::Shortcut) |
803 | @@ -475,6 +594,11 @@ |
804 | if (!m_enabled) { |
805 | return; |
806 | } |
807 | + |
808 | + if (m_checkable && !(m_checked && m_exclusiveGroup)) { |
809 | + setChecked(!m_checked); |
810 | + } |
811 | + |
812 | if (!isValidType(value.type())) { |
813 | Q_EMIT triggered(QVariant()); |
814 | } else { |
815 | |
816 | === modified file 'src/Ubuntu/UbuntuToolkit/ucaction_p.h' |
817 | --- src/Ubuntu/UbuntuToolkit/ucaction_p.h 2016-07-07 07:21:48 +0000 |
818 | +++ src/Ubuntu/UbuntuToolkit/ucaction_p.h 2016-08-22 08:24:13 +0000 |
819 | @@ -53,7 +53,7 @@ |
820 | } |
821 | } |
822 | |
823 | -class UCActionAttached; |
824 | +class ExclusiveGroup; |
825 | class UBUNTUTOOLKIT_EXPORT UCAction : public QObject |
826 | { |
827 | Q_OBJECT |
828 | @@ -68,6 +68,10 @@ |
829 | Q_PROPERTY(bool enabled MEMBER m_enabled NOTIFY enabledChanged) |
830 | Q_PROPERTY(Type parameterType MEMBER m_parameterType NOTIFY parameterTypeChanged) |
831 | |
832 | + Q_PROPERTY(bool checkable READ isCheckable WRITE setCheckable NOTIFY checkableChanged REVISION 1) |
833 | + Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY toggled REVISION 1) |
834 | + Q_PROPERTY(ExclusiveGroup* exclusiveGroup READ exclusiveGroup WRITE setExclusiveGroup NOTIFY exclusiveGroupChanged REVISION 1) |
835 | + |
836 | // Toolkit Actions API |
837 | Q_PROPERTY(QUrl iconSource MEMBER m_iconSource WRITE setIconSource NOTIFY iconSourceChanged) |
838 | Q_PROPERTY(bool visible MEMBER m_visible NOTIFY visibleChanged) |
839 | @@ -113,6 +117,13 @@ |
840 | void setItemHint(QQmlComponent *); |
841 | void setShortcut(const QVariant&); |
842 | void resetShortcut(); |
843 | + void setCheckable(bool checkable); |
844 | + bool isCheckable() const { return m_checkable; } |
845 | + void setChecked(bool checked); |
846 | + bool isChecked() const { return m_checkable && m_checked; } |
847 | + |
848 | + ExclusiveGroup *exclusiveGroup() const { return m_exclusiveGroup; } |
849 | + void setExclusiveGroup(ExclusiveGroup *exclusiveGroup); |
850 | |
851 | Q_SIGNALS: |
852 | void nameChanged(); |
853 | @@ -125,13 +136,18 @@ |
854 | void iconSourceChanged(); |
855 | void visibleChanged(); |
856 | void shortcutChanged(); |
857 | + Q_REVISION(1) void checkableChanged(); |
858 | + Q_REVISION(1) void exclusiveGroupChanged(); |
859 | + |
860 | void triggered(const QVariant &value); |
861 | + Q_REVISION(1) void toggled(bool value); |
862 | |
863 | public Q_SLOTS: |
864 | void trigger(const QVariant &value = QVariant()); |
865 | |
866 | private: |
867 | QPODVector<QQuickItem*, 4> m_owningItems; |
868 | + ExclusiveGroup *m_exclusiveGroup; |
869 | QString m_name; |
870 | QString m_text; |
871 | QString m_iconName; |
872 | @@ -146,6 +162,8 @@ |
873 | bool m_enabled:1; |
874 | bool m_visible:1; |
875 | bool m_published:1; |
876 | + bool m_checkable:1; |
877 | + bool m_checked:1; |
878 | |
879 | friend class UCActionContext; |
880 | friend class UCActionItem; |
881 | |
882 | === modified file 'tests/unit/components/tst_action.qml' |
883 | --- tests/unit/components/tst_action.qml 2015-12-22 14:42:59 +0000 |
884 | +++ tests/unit/components/tst_action.qml 2016-08-22 08:24:13 +0000 |
885 | @@ -16,7 +16,7 @@ |
886 | |
887 | import QtQuick 2.0 |
888 | import QtTest 1.0 |
889 | -import Ubuntu.Components 1.1 |
890 | +import Ubuntu.Components 1.3 |
891 | |
892 | TestCase { |
893 | name: "ActionAPI" |
894 | @@ -39,6 +39,12 @@ |
895 | triggeredSignalSpy.clear(); |
896 | context1.active = false; |
897 | context2.active = false; |
898 | + |
899 | + checkableAction.checkable = true; |
900 | + checkableAction.checked = false; |
901 | + action1.checked = true; |
902 | + currentActionSpy.clear(); |
903 | + checkableSpy.clear(); |
904 | } |
905 | |
906 | function initTestCase() { |
907 | @@ -166,6 +172,60 @@ |
908 | compare(data.action.invoked, data.invoked); |
909 | } |
910 | |
911 | + function test_checkable() { |
912 | + checkableAction.checkable = true; |
913 | + checkableAction.checked = true; |
914 | + checkableSpy.wait(); |
915 | + compare(checkableAction.checked, true, "Checkable action should be checked"); |
916 | + } |
917 | + |
918 | + function test_not_checkable() { |
919 | + checkableAction.checkable = false; |
920 | + checkableAction.checked = true; |
921 | + compare(checkableAction.checked, false, "Non-checkable action should never be checked"); |
922 | + } |
923 | + |
924 | + function test_actionlist() { |
925 | + verify(actionList.actions.length, 2, "Default actions not added to actionList"); |
926 | + } |
927 | + |
928 | + function test_actionlist_dynamic_actions() { |
929 | + actionList.addAction(dynamicListAction); |
930 | + verify(actionList.actions.length, 3, "Dynamic action not added to actionList"); |
931 | + actionList.removeAction(dynamicListAction); |
932 | + verify(actionList.actions.length, 2, "Dynamic action not remove from actionList"); |
933 | + } |
934 | + |
935 | + function test_exclusive_group() { |
936 | + compare(exclusiveGroup.actions.length, 3, "Incorrect number of actions"); |
937 | + } |
938 | + |
939 | + function test_exclusive_group_activation_data() { |
940 | + return [ |
941 | + {tag: "Activate action2", active: [action2], inactive: [action1, action3], current: action2}, |
942 | + {tag: "Activate action3", active: [action3], inactive: [action1, action2], current: action3}, |
943 | + {tag: "Activate action2, action3", active: [action2, action3], inactive: [action1, action2], current: action3}, |
944 | + ]; |
945 | + } |
946 | + function test_exclusive_group_activation(data) { |
947 | + for (var i = 0; i < data.active.length; i++) { |
948 | + data.active[i].trigger(); |
949 | + compare(data.active[i].checked, true, "Active action checked property should be 'true'"); |
950 | + } |
951 | + for (var i = 0; i < data.inactive.length; i++) { |
952 | + compare(data.inactive[i].checked, false, "Inactive action checked property should be 'false'"); |
953 | + } |
954 | + currentActionSpy.wait(); |
955 | + compare(exclusiveGroup.current, data.current, "Current action in exclusiveGroup does not match"); |
956 | + } |
957 | + |
958 | + function test_always_one_action_selected() { |
959 | + action1.trigger(); |
960 | + compare(action1.checked, true, "Triggering an exclusive group action should check the action"); |
961 | + action1.trigger(); |
962 | + compare(action1.checked, true, "Triggering an exclusive group action again will not uncheck the action."); |
963 | + } |
964 | + |
965 | Action { |
966 | id: action |
967 | } |
968 | @@ -197,6 +257,16 @@ |
969 | target: action |
970 | signalName: "textChanged" |
971 | } |
972 | + SignalSpy { |
973 | + id: checkableSpy |
974 | + target: checkableAction |
975 | + signalName: "toggled" |
976 | + } |
977 | + SignalSpy { |
978 | + id: currentActionSpy |
979 | + target: exclusiveGroup |
980 | + signalName: "currentChanged" |
981 | + } |
982 | |
983 | ActionManager { |
984 | id: manager |
985 | @@ -236,4 +306,40 @@ |
986 | id: testItem |
987 | } |
988 | |
989 | + Action { |
990 | + id: checkableAction |
991 | + checkable: true |
992 | + } |
993 | + |
994 | + ActionList { |
995 | + id: actionList |
996 | + Action { |
997 | + } |
998 | + Action { |
999 | + } |
1000 | + } |
1001 | + |
1002 | + Action { |
1003 | + id: dynamicListAction |
1004 | + } |
1005 | + |
1006 | + ExclusiveGroup { |
1007 | + id: exclusiveGroup |
1008 | + Action { |
1009 | + id: action1 |
1010 | + checkable: true |
1011 | + checked: true |
1012 | + } |
1013 | + Action { |
1014 | + id: action2 |
1015 | + checkable: true |
1016 | + checked: false |
1017 | + } |
1018 | + Action { |
1019 | + id: action3 |
1020 | + checkable: true |
1021 | + checked: false |
1022 | + } |
1023 | + } |
1024 | + |
1025 | } |
Please submit the MR against staging. And add unit tests pls.