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 /**
165 * This property holds whether the popup is fully open.
166 *
167 * Note: Setting this property yourself does nothing. You must open the popup using popup().
168 */
169 property bool opened
170
171 signal closed
172
173 function popup(parent = null, position = null): void {
174 if (Kirigami.Settings.isMobile) {
175 if (parent) {
176 const item = mobileMenu.createObject(parent);
177 item.open();
178 } else {
179 const item = mobileMenu.createObject(root);
180 item.open();
181 }
182 } else {
183 if (position && parent) {
184 const item = desktopMenu.createObject(parent);
185 item.popup(position);
186 } else if (parent) {
187 const item = desktopMenu.createObject(parent);
188 item.popup();
189 } else {
190 const item = desktopMenu.createObject(root);
191 item.popup();
192 }
193 }
194 root.opened = true;
195 }
196
197 Component {
198 id: desktopMenu
199
200 P.ActionsMenu {
201 actions: root.actions
202 submenuComponent: P.ActionsMenu { }
203 modal: true
204 onClosed: {
205 root.opened = false;
206 root.closed();
207 destroy();
208 }
209 }
210 }
211
212 Component {
213 id: mobileMenu
214
215 KirigamiComponents.BottomDrawer {
216 id: drawer
217
218 onClosed: {
219 root.opened = false;
220 root.closed();
221 destroy();
222 }
223
224 headerContentItem: ColumnLayout {
225 children: if (stackViewMenu.depth > 1 || root.headerContentItem === null) {
226 return nestedHeader;
227 } else {
228 return root.headerContentItem;
229 }
230 }
231
232 property Item nestedHeader: RowLayout {
233 Layout.fillWidth: true
234 spacing: Kirigami.Units.smallSpacing
235
236 enabled: stackViewMenu.currentItem?.title.length > 0
237
238 QQC2.ToolButton {
239 icon.name: 'draw-arrow-back-symbolic'
240 text: i18ndc("kirigami-addons6", "@action:button", "Go Back")
241 display: QQC2.ToolButton.IconOnly
242 onClicked: stackViewMenu.pop();
243
244 QQC2.ToolTip.visible: hovered
245 QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
246 QQC2.ToolTip.text: text
247 }
248
250 level: 2
251 text: stackViewMenu.currentItem?.title
252 elide: Text.ElideRight
253 Layout.fillWidth: true
254 }
255 }
256
257 QQC2.StackView {
258 id: stackViewMenu
259
260 implicitHeight: currentItem?.implicitHeight
261 implicitWidth: currentItem?.implicitWidth
262
263 initialItem: P.ContextMenuPage {
264 stackView: stackViewMenu
265 actions: root.actions
266 drawer: drawer
267 }
268 }
269 }
270 }
271}
listTAction actions
This property holds the list of actions.
bool opened
This property holds whether the popup is fully open.
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 Feb 28 2025 11:51:42 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.