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
270 background: Maui.ShadowedRectangle
271 {
272 color: (checked || down ? Maui.Theme.highlightColor : ( hovered ? Maui.Theme.hoverColor : Maui.Theme.backgroundColor))
273 corners
274 {
275 topLeftRadius: index === 0 ? Maui.Style.radiusV : 0
276 topRightRadius: index === _repeater.count - 1 ? Maui.Style.radiusV : 0
277 bottomLeftRadius: index === 0 ? Maui.Style.radiusV : 0
278 bottomRightRadius: index === _repeater.count - 1 ? Maui.Style.radiusV : 0
279 }
280
281 Behavior on color
282 {
283 Maui.ColorTransition{}
284 }
285
286 Behavior on border.color
287 {
288 Maui.ColorTransition{}
289 }
290
291 border.color: statusColor(_actionButton)
292
293 function statusColor(control)
294 {
295 if(control.Maui.Controls.status)
296 {
297 switch(control.Maui.Controls.status)
298 {
299 case Maui.Controls.Positive: return control.Maui.Theme.positiveBackgroundColor
300 case Maui.Controls.Negative: return control.Maui.Theme.negativeBackgroundColor
301 case Maui.Controls.Neutral: return control.Maui.Theme.neutralBackgroundColor
302 case Maui.Controls.Normal:
303 default:
304 return "red"
305 }
306 }
307
308 return "red"
309 }
310 }
311 }
312 }
313 }
314 }
315
316 Component
317 {
318 id: _toolButtonMenuComponent
319
320 Maui.ToolButtonMenu
321 {
322 Maui.Controls.status: control.Maui.Controls.status
323 flat: false
324 property Action m_action
325 hoverEnabled: true
326 display: control.display
327 icon.name: m_action ? m_action.icon.name : control.defaultIconName
328 text: m_action ? m_action.text: ""
329
330 Component.onCompleted:
331 {
332 m_action = buttonAction()
333 }
334
335 Repeater
336 {
337 model: control.autoExclusive && control.canCyclic ? undefined : control.actions
338
339 delegate: MenuItem
340 {
341 action: modelData
342 enabled: modelData.enabled
343 autoExclusive: control.autoExclusive
344 checkable: control.checkable || action.checkable
345 }
346 }
347
348 Row
349 {
350 visible: false
351 Repeater
352 {
353 model: control.actions
354 delegate: Item
355 {
356 property bool checked : modelData.checked
357 onCheckedChanged: m_action = buttonAction()
358 }
359 }
360 }
361
362 function buttonAction()
363 {
364 if(control.autoExclusive)
365 {
366 var currentAction
367 var actionIndex = -1
368 for(var i in control.actions)
369 {
370 console.log("Checking current action", i)
371 if(control.actions[i].checked)
372 {
373 actionIndex = i
374 currentAction = control.actions[actionIndex]
375 console.log("Found current action", i, actionIndex)
376 return currentAction
377 }
378 }
379 }
380
381 return null
382 }
383 }
384 }
385
386 Component
387 {
388 id: _buttonComponent
389
390 Button
391 {
392 id: _defaultButtonIcon
393 Maui.Controls.status: control.Maui.Controls.status
394
395 property Action m_action
396
397 function buttonAction()
398 {
399 if(control.autoExclusive)
400 {
401 var currentAction
402 var actionIndex = -1
403 for(var i in control.actions)
404 {
405 console.log("Checking current action", i)
406 if(control.actions[i].checked)
407 {
408 actionIndex = i
409 currentAction = control.actions[actionIndex]
410 console.log("Found current action", i, actionIndex)
411 }
412 }
413
414 if(control.canCyclic)
415 {
416 actionIndex++
417
418 let m_index = actionIndex >= control.actions.length ? 0 : actionIndex
419 //
420 console.log("Setting current action at", m_index)
421 if(control.actions[m_index].enabled)
422 {
423 return control.actions[m_index];
424 }
425 }
426
427 return currentAction
428 }
429
430 return null
431 }
432
433 Row
434 {
435 visible: false
436 Repeater
437 {
438 model: control.actions
439 delegate: Item
440 {
441 property bool checked : modelData.checked
442 onCheckedChanged: _defaultButtonIcon.m_action = _defaultButtonIcon.buttonAction()
443 }
444 }
445 }
446
447 onClicked:
448 {
449 if(_defaultButtonIcon.m_action && control.canCyclic)
450 {
451 console.log("Trigger next cyclic action", _defaultButtonIcon.m_action.icon.name)
452 // var previousAction = _defaultButtonIcon.action
453 _defaultButtonIcon.m_action.triggered()
454 _defaultButtonIcon.m_action = _defaultButtonIcon.buttonAction()
455 }
456 }
457
458 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
459
460 icon.name: m_action ? m_action.icon.name : control.defaultIconName
461 text: m_action ? m_action.text: ""
462
463 enabled: m_action ? m_action.enabled : true
464
465 Component.onCompleted:
466 {
467 _defaultButtonIcon.m_action = _defaultButtonIcon.buttonAction()
468 }
469
470 display: control.display
471
472 checkable: control.checkable && (action ? action.checkable : false)
473 }
474 }
475}
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 Jan 31 2025 12:11:16 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.