Kirigami-addons

ConvergentContextMenu.qml
1// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: LGPL-2.0-or-later
3
4pragma ComponentBehavior: Bound
5
6import QtQuick
7import QtQuick.Controls as QQC2
8import QtQuick.Controls as T
9import QtQuick.Layouts
10import Qt.labs.qmlmodels
11
12import org.kde.kirigami as Kirigami
13import org.kde.kirigamiaddons.components as KirigamiComponents
14import org.kde.kirigamiaddons.formcard as FormCard
15
16import './private' as P
17
18/**
19 * Menu popup that appears as a tradional menu on desktop and as a bottom
20 * drawer mobile.
21 *
22 * ConvergentContextMenu uses abstract Controls.Action and Kirigami.Action to build
23 * the traditional menu on desktopn and the bottom drawer on mobile. Most properties
24 * of Kirigami.Action are supported including nested actions.
25 *
26 * \code{qml}
27 * import QtQuick.Controls as Controls
28 * import org.kde.kirigami as Kirigami
29 * import org.kde.kirigamiaddons.components as Components
30 * import org.kde.kirigamiaddons.formcard as FormCard
31 *
32 * Components.ConvergentContextMenu {
33 * id: root
34 *
35 * headerContentItem: RowLayout {
36 * spacing: Kirigami.Units.smallSpacing
37 * Kirigami.Avatar { ... }
38 *
39 * Kirigami.Heading {
40 * level: 2
41 * text: "Room Name"
42 * }
43 * }
44 *
45 * Controls.Action {
46 * text: i18nc("@action:inmenu", "Simple Action")
47 * }
48 *
49 * Kirigami.Action {
50 * text: i18nc("@action:inmenu", "Nested Action")
51 *
52 * Controls.Action { ... }
53 *
54 * Controls.Action { ... }
55 *
56 * Controls.Action { ... }
57 * }
58 *
59 * Kirigami.Action {
60 * text: i18nc("@action:inmenu", "Nested Action with Multiple Choices")
61 *
62 * Kirigami.Action {
63 * text: i18nc("@action:inmenu", "Follow Global Settings")
64 * checkable: true
65 * autoExclusive: true // Since KF 6.10
66 * }
67 *
68 * Kirigami.Action {
69 * text: i18nc("@action:inmenu", "Enabled")
70 * checkable: true
71 * autoExclusive: true // Since KF 6.10
72 * }
73 *
74 * Kirigami.Action {
75 * text: i18nc("@action:inmenu", "Disabled")
76 * checkable: true
77 * autoExclusive: true // Since KF 6.10
78 * }
79 * }
80 *
81 * // custom FormCard delegate only supported on mobile
82 * Kirigami.Action {
83 * visible: Kirigami.Settings.isMobile
84 * displayComponent: FormCard.FormButtonDelegate { ... }
85 * }
86 * }
87 * \endcode{qml}
88 *
89 * When creating a menu for a ListView, avoid creating a ConvergentContextMenu for each delegate
90 * and instead create a global ConvergentContextMenu for the ListView or use a Component and dynamically
91 * instanciate the context menu on demand:
92 *
93 * \code{qml}
94 * import QtQuick
95 * import QtQuick.Controls as Controls
96 * import org.kde.kirigamiaddons.components as Addons
97 *
98 * ListView {
99 * model: 10
100 * delegate: Controls.ItemDelegate {
101 * text: index
102 *
103 * function openContextMenu(): void {
104 * const item = menu.createObject(Controls.Overlay.overlay, {
105 * index,
106 * });
107 * item.popup();
108 * }
109 *
110 * onPressAndHold: openContextMenu()
111 *
112 * // Since Qt 6.9
113 * Controls.ContextMenu.onRequested: (position) => openContextMenu()
114 *
115 * // Before Qt 6.9
116 * TapHandler {
117 * acceptedButtons: Qt.RightButton
118 * onSingleTapped: (eventPoint, button) => {
119 * openContextMenu();
120 * }
121 * }
122 * }
123 *
124 * Component {
125 * id: menu
126 *
127 * Addons.ConvergentContextMenu {
128 * required property int index
129 *
130 * Controls.Action {
131 * text: i18nc("@action:inmenu", "Action 1")
132 * }
133 *
134 * Kirigami.Action {
135 * text: i18nc("@action:inmenu", "Action 2")
136 *
137 * Controls.Action {
138 * text: i18nc("@action:inmenu", "Sub-action")
139 * }
140 * }
141 * }
142 * }
143 * }
144 * \endcode{qml}
145 *
146 * \since 1.7.0.
147 */
148Item {
149 id: root
150
151 /**
152 * This property holds the list of actions. This can be either tradional
153 * action from QtQuick.Controls or a Kirigami.Action with sub actions.
154 */
155 default property list<T.Action> actions
156
157 /**
158 * Optional item which will be displayed as header of the internal ButtonDrawer.
159 *
160 * Note: This is only displayed on the first level of the ContextMenu mobile mode.
161 */
162 property Item headerContentItem
163
164 signal closed
165
166 function popup(parent = null, position = null): void {
167 if (Kirigami.Settings.isMobile) {
168 if (parent) {
169 const item = mobileMenu.createObject(parent);
170 item.open();
171 } else {
172 const item = mobileMenu.createObject(root);
173 item.open();
174 }
175 } else {
176 if (position && parent) {
177 const item = desktopMenu.createObject(parent);
178 item.popup(position);
179 } else if (parent) {
180 const item = desktopMenu.createObject(parent);
181 item.popup();
182 } else {
183 const item = desktopMenu.createObject(root);
184 item.popup();
185 }
186 }
187 }
188
189 Component {
190 id: desktopMenu
191
192 P.ActionsMenu {
193 actions: root.actions
194 submenuComponent: P.ActionsMenu { }
195 modal: true
196 onClosed: {
197 root.closed();
198 destroy();
199 }
200 }
201 }
202
203 Component {
204 id: mobileMenu
205
206 KirigamiComponents.BottomDrawer {
207 id: drawer
208
209 onClosed: {
210 root.closed();
211 destroy();
212 }
213
214 headerContentItem: ColumnLayout {
215 children: if (stackViewMenu.depth > 1 || root.headerContentItem === null) {
216 return nestedHeader;
217 } else {
218 return root.headerContentItem;
219 }
220 }
221
222 property Item nestedHeader: RowLayout {
223 Layout.fillWidth: true
224 spacing: Kirigami.Units.smallSpacing
225
226 enabled: stackViewMenu.currentItem?.title.length > 0
227
228 QQC2.ToolButton {
229 icon.name: 'draw-arrow-back-symbolic'
230 text: i18ndc("kirigami-addons6", "@action:button", "Go Back")
231 display: QQC2.ToolButton.IconOnly
232 onClicked: stackViewMenu.pop();
233
234 QQC2.ToolTip.visible: hovered
235 QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
236 QQC2.ToolTip.text: text
237 }
238
240 level: 2
241 text: stackViewMenu.currentItem?.title
242 elide: Text.ElideRight
243 Layout.fillWidth: true
244 }
245 }
246
247 QQC2.StackView {
248 id: stackViewMenu
249
250 implicitHeight: currentItem?.implicitHeight
251 implicitWidth: currentItem?.implicitWidth
252
253 initialItem: P.ContextMenuPage {
254 stackView: stackViewMenu
255 actions: root.actions
256 drawer: drawer
257 }
258 }
259 }
260 }
261}
listTAction actions
This property holds the list of actions.
Item headerContentItem
Optional item which will be displayed as header of the internal ButtonDrawer.
QString i18ndc(const char *domain, const char *context, const char *text, const TYPE &arg...)
QStringView level(QStringView ifopt)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:49:11 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.