MauiKit Controls

ToolActions.qml
1import QtQuick
2import QtQuick.Controls
3import QtQuick.Layouts
4import QtQml
5
6import org.mauikit.controls as Maui
7import "private" as Private
8
9/**
10 * @inherit QtQuick.Controls.Control
11 * @brief A set of grouped action visually joined together.
12 *
13 * <a href="https://doc.qt.io/qt-6/qml-qtquick-controls-control.html">This control inherits from QQC2 Control, to checkout its inherited properties refer to the Qt Docs.</a>
14 *
15 * The set actions can be checkable and auto-exclusive or not.
16 *
17 * @image html Misc/toolactions.png "[1] Non-checkable. [2] Checkable non-auto-exclusive. [3] Checkable and autoexclusive"
18 *
19 * @section features Features
20 * This control supports checkable and non-checkable actions. Also auto-exclusive and non-auto-exclusive actions.
21 *
22 * When enabling the `autoExclusive` property, then only one action in the group can be marked as checked at the time.
23 *
24 * There is also the option to collapse the actions into a single button with a popup menu where the actions are listed, this is useful when the available space changes and the control needs to be made more compact to save space.
25 *
26 * @image html Misc/toolactions2.png "The collapsed actions into a menu"
27 *
28 * If only two actions are added and marked as auto-exclusive, then this control has the option to enable a `cyclic` behavior, which means that toggling one button will activate the next action in line and cyclic around.
29 * @see canCyclic
30 * @see cyclic
31 *
32 * Heres a example of how to achieve such behavior:
33 * @code
34 * Maui.ToolActions
35 * {
36 * id: _actions
37 * checkable: true
38 * autoExclusive: true
39 * cyclic: true //enable the cyclic behavior
40 * expanded: false //the cyclic behavior needs to be in the collapsed mode
41 *
42 * property int currentAction: 0 //here we keep the state for the current action checked
43 *
44 * Action
45 * {
46 * id: _action1
47 * icon.name: "view-list-details"
48 * checked: _actions.currentAction === 0
49 * onTriggered:
50 * {
51 * _actions.currentAction = 0
52 * }
53 * }
54 *
55 * Action
56 * {
57 * id: _action2
58 * icon.name: "view-list-icons"
59 * checked: _actions.currentAction === 1
60 * onTriggered:
61 * {
62 * _actions.currentAction = 1
63 * }
64 * }
65 * }
66 * @endcode
67 *
68 * @code
69 * Maui.ToolActions
70 * {
71 * checkable: true
72 * autoExclusive: true
73 *
74 * Action
75 * {
76 * text: "Pick"
77 * }
78 *
79 * Action
80 * {
81 * text: "Only"
82 * }
83 *
84 * Action
85 * {
86 * text: "One"
87 * }
88 * }
89 * @endcode
90 *
91 * <a href="https://invent.kde.org/maui/mauikit/-/blob/qt6-2/examples/ToolActions.qml">You can find a more complete example at this link.</a>
92 */
93Control
94{
95 id: control
96
97 implicitWidth: _loader.implicitWidth + leftPadding + rightPadding
98 implicitHeight: _loader.implicitHeight + topPadding + bottomPadding
99
100 opacity: enabled ? 1 : 0.5
101
102 spacing: 2
103 padding: 0
104
105 Maui.Theme.colorSet: Maui.Theme.Button
106 Maui.Theme.inherit: false
107
108 /**
109 * @brief The list of QQC2 Action to be listed. These can be declared a children elements of this control.
110 */
111 default property list<Action> actions
112
113 /**
114 * @brief Whether this control should only allow one action to be checked at the time.
115 * By default this is set to `true`
116 */
117 property bool autoExclusive: true
118
119 /**
120 * @brief Whether the action button can be checked. If enabled, then the state will be styled accordingly.
121 * @By default this is set to `true`.
122 */
123 property bool checkable: true
124
125 /**
126 * @brief Options on how to display the button text and icon.
127 * Available options are:
128 * - ToolButton.IconOnly
129 * - ToolButton.TextBesideIcon
130 * - ToolButton.TextOnly
131 * - ToolButton.TextUnderIcon
132 * By default this is set to `ToolButton.TextBesideIcon`
133 */
134 property int display: ToolButton.TextBesideIcon
135
136 /**
137 * @brief Whether two actions can be triggered in a cyclic manner. So one press will activate the next action and then cycle around to the first one again.
138 * @note For this to work only two actions can be added
139 * @see canCyclic
140 * By default this is set to `false`
141 */
142 property bool cyclic: false
143
144 /**
145 * @brief Whether the `cyclic` behavior can be activated.
146 * For it to be possible, the conditions are: only two actions and those must be auto-exclusive.
147 * @see cyclic
148 * @see autoExclusive
149 * @see count
150 */
151 readonly property bool canCyclic : control.cyclic && control.count === 2 && control.autoExclusive
152
153 /**
154 * @brief Whether the style of this control should be styled as flat.
155 * By default this is set to `false`.
156 */
157 property bool flat : false
158
159 /**
160 * @brief The total amount of actions declared.
161 */
162 readonly property int count : actions.length
163
165 /**
166 * @brief Whether the control should display all the actions as buttons in a row, or to collapse them into a popup menu.
167 * By default this is set to `true`.
168 */
169 property bool expanded : true
170
171 /**
172 * @brief The icon name to be used in the button that opens the menu popup, when the view is collapsed.
173 * By default this is set to `application-menu`.
174 */
175 property string defaultIconName: "application-menu"
176
177 /**
178 * @brief Forces to uncheck all the actions except the one action sent as the argument.
179 * @param except the action that should not be unchecked.
180 */
181 function uncheck(except)
182 {
183 for(var i in control.actions)
184 {
185 if(control.actions[i] === except)
186 {
187 continue
188 }
189
190 control.actions[i].checked = false
191 }
192 }
193
194 Behavior on implicitWidth
195 {
196 NumberAnimation
197 {
198 duration: Maui.Style.units.shortDuration
199 easing.type: Easing.InQuad
200 }
201 }
202
203 contentItem: Loader
204 {
205 id: _loader
206 asynchronous: true
207 sourceComponent: control.expanded ? _rowComponent : (control.canCyclic ? _buttonComponent : _toolButtonMenuComponent)
208 }
209
210 background: null
211
212 Component
213 {
214 id: _rowComponent
215
216 Row
217 {
218 id: _row
219 property int biggerHeight : 0
220 spacing: control.spacing
221
222 Behavior on width
223 {
224 enabled: Maui.Style.enableEffects
225
226 NumberAnimation
227 {
228 duration: Maui.Style.units.shortDuration
229 easing.type: Easing.InOutQuad
230 }
231 }
232
233 function calculateBiggerHeight()
234 {
235 var value = 0
236 for(var i in _row.children)
237 {
238 const height = _row.children[i].implicitHeight
239 if(height > value)
240 {
241 value = height
242 }
243 }
244
245 return value
246 }
247
248 Repeater
249 {
250 id: _repeater
251 model: control.actions
252
253 ToolButton
254 {
255 id: _actionButton
256 action : modelData
257 Maui.Controls.status: control.Maui.Controls.status
258
259 checkable: control.checkable || action.checkable
260
261 height: Math.max(implicitHeight, _row.biggerHeight)
262
263 onImplicitHeightChanged: _row.biggerHeight = _row.calculateBiggerHeight()
264
265 autoExclusive: control.autoExclusive
266 enabled: action.enabled
267
268 display: control.display
269 font: control.font
270
271 background: Maui.ShadowedRectangle
272 {
273 color: (checked || down ? Maui.Theme.highlightColor : ( hovered ? Maui.Theme.hoverColor : Maui.Theme.backgroundColor))
274 corners
275 {
276 topLeftRadius: index === 0 ? Maui.Style.radiusV : 0
277 topRightRadius: index === _repeater.count - 1 ? Maui.Style.radiusV : 0
278 bottomLeftRadius: index === 0 ? Maui.Style.radiusV : 0
279 bottomRightRadius: index === _repeater.count - 1 ? Maui.Style.radiusV : 0
280 }
281
282 Behavior on color
283 {
284 Maui.ColorTransition{}
285 }
286
287 Behavior on border.color
288 {
289 Maui.ColorTransition{}
290 }
291
292 border.color: statusColor(_actionButton)
293
294 function statusColor(control)
295 {
296 if(control.Maui.Controls.status)
297 {
298 switch(control.Maui.Controls.status)
299 {
300 case Maui.Controls.Positive: return control.Maui.Theme.positiveBackgroundColor
301 case Maui.Controls.Negative: return control.Maui.Theme.negativeBackgroundColor
302 case Maui.Controls.Neutral: return control.Maui.Theme.neutralBackgroundColor
303 case Maui.Controls.Normal:
304 default:
305 return "red"
306 }
307 }
308
309 return "red"
310 }
311 }
312 }
313 }
314 }
315 }
316
317 Component
318 {
319 id: _toolButtonMenuComponent
320
321 Maui.ToolButtonMenu
322 {
323 Maui.Controls.status: control.Maui.Controls.status
324 flat: false
325 property Action m_action
326 hoverEnabled: true
327 display: control.display
328 icon.name: m_action ? m_action.icon.name : control.defaultIconName
329 text: m_action ? m_action.text: ""
330
331 Component.onCompleted:
332 {
333 m_action = buttonAction()
334 }
335
336 Repeater
337 {
338 model: control.autoExclusive && control.canCyclic ? undefined : control.actions
339
340 delegate: MenuItem
341 {
342 action: modelData
343 enabled: modelData.enabled
344 autoExclusive: control.autoExclusive
345 checkable: control.checkable || action.checkable
346 }
347 }
348
349 Row
350 {
351 visible: false
352 Repeater
353 {
354 model: control.actions
355 delegate: Item
356 {
357 property bool checked : modelData.checked
358 onCheckedChanged: m_action = buttonAction()
359 }
360 }
361 }
362
363 function buttonAction()
364 {
365 if(control.autoExclusive)
366 {
367 var currentAction
368 var actionIndex = -1
369 for(var i in control.actions)
370 {
371 console.log("Checking current action", i)
372 if(control.actions[i].checked)
373 {
374 actionIndex = i
375 currentAction = control.actions[actionIndex]
376 console.log("Found current action", i, actionIndex)
377 return currentAction
378 }
379 }
380 }
381
382 return null
383 }
384 }
385 }
386
387 Component
388 {
389 id: _buttonComponent
390
391 Button
392 {
393 id: _defaultButtonIcon
394 Maui.Controls.status: control.Maui.Controls.status
395
396 property Action m_action
397
398 function buttonAction()
399 {
400 if(control.autoExclusive)
401 {
402 var currentAction
403 var actionIndex = -1
404 for(var i in control.actions)
405 {
406 console.log("Checking current action", i)
407 if(control.actions[i].checked)
408 {
409 actionIndex = i
410 currentAction = control.actions[actionIndex]
411 console.log("Found current action", i, actionIndex)
412 }
413 }
414
415 if(control.canCyclic)
416 {
417 actionIndex++
418
419 let m_index = actionIndex >= control.actions.length ? 0 : actionIndex
420 //
421 console.log("Setting current action at", m_index)
422 if(control.actions[m_index].enabled)
423 {
424 return control.actions[m_index];
425 }
426 }
427
428 return currentAction
429 }
430
431 return null
432 }
433
434 Row
435 {
436 visible: false
437 Repeater
438 {
439 model: control.actions
440 delegate: Item
441 {
442 property bool checked : modelData.checked
443 onCheckedChanged: _defaultButtonIcon.m_action = _defaultButtonIcon.buttonAction()
444 }
445 }
446 }
447
448 onClicked:
449 {
450 if(_defaultButtonIcon.m_action && control.canCyclic)
451 {
452 console.log("Trigger next cyclic action", _defaultButtonIcon.m_action.icon.name)
453 // var previousAction = _defaultButtonIcon.action
454 _defaultButtonIcon.m_action.triggered()
455 _defaultButtonIcon.m_action = _defaultButtonIcon.buttonAction()
456 }
457 }
458
459 icon.color: m_action ? (m_action.icon.color && m_action.icon.color.length ? m_action.icon.color : (pressed || checked ? control.Maui.Theme.highlightedTextColor : control.Maui.Theme.textColor)) : control.Maui.Theme.textColor
460
461 icon.name: m_action ? m_action.icon.name : control.defaultIconName
462 text: m_action ? m_action.text: ""
463
464 enabled: m_action ? m_action.enabled : true
465
466 Component.onCompleted:
467 {
468 _defaultButtonIcon.m_action = _defaultButtonIcon.buttonAction()
469 }
470
471 display: control.display
472
473 checkable: control.checkable && (action ? action.checkable : false)
474 }
475 }
476}
list< Action > actions
The list of QQC2 Action to be listed.
bool canCyclic
Whether the cyclic behavior can be activated.
void uncheck(except)
Forces to uncheck all the actions except the one action sent as the argument.
int display
Options on how to display the button text and icon.
bool flat
Whether the style of this control should be styled as flat.
bool checkable
Whether the action button can be checked.
bool autoExclusive
Whether this control should only allow one action to be checked at the time.
bool cyclic
Whether two actions can be triggered in a cyclic manner.
bool expanded
Whether the control should display all the actions as buttons in a row, or to collapse them into a po...
int count
The total amount of actions declared.
string defaultIconName
The icon name to be used in the button that opens the menu popup, when the view is collapsed.
QString name(StandardAction id)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Apr 18 2025 12:16:12 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.