Kirigami-addons

FormComboBoxDelegate.qml
1/*
2 * Copyright 2022 Devin Lin <devin@kde.org>
3 * SPDX-License-Identifier: LGPL-2.0-or-later
4 */
5
6import QtQuick
7import QtQuick.Controls as QQC2
8import QtQuick.Layouts
9
10import org.kde.kirigami as Kirigami
11import org.kde.kirigamiaddons.delegates as Delegates
12import org.kde.kirigamiaddons.components as Components
13
14/**
15 * @brief A Form delegate that corresponds to a combobox.
16 *
17 * This component is used for individual settings that can have multiple
18 * possible values shown in a vertical list, typically defined in a ::model.
19 *
20 * Many of its properties require familiarity with QtQuick.Controls.ComboBox.
21 *
22 * Use the inherited QtQuick.Controls.AbstractButton.text property to define
23 * the main text of the combobox.
24 *
25 * If you need a purely on/off toggle, use a FormSwitchDelegate instead.
26 *
27 * If you need an on/off/tristate toggle, use a FormCheckDelegate instead.
28 *
29 * If you need multiple toggles instead of multiple values for the same
30 * setting, consider using a FormRadioDelegate.
31 *
32 * @since KirigamiAddons 0.11.0
33 *
34 * @see QtQuick.Controls.AbstractButton
35 * @see FormSwitchDelegate
36 * @see FormCheckDelegate
37 * @see FormRadioDelegate
38 *
39 * @inherit AbstractFormDelegate
40 */
41AbstractFormDelegate {
42 id: controlRoot
43
44 /**
45 * @brief This signal is emitted when the item at @p index is activated
46 * by the user.
47 */
48 signal activated(int index)
49
50 /**
51 * @brief This signal is emitted when the Return or Enter key is pressed
52 * while an editable combo box is focused.
53 *
54 * @see editable
55 */
56 signal accepted()
57
58 /**
59 * @brief A label that contains secondary text that appears under the
60 * inherited text property.
61 *
62 * This provides additional information shown in a faint gray color.
63 *
64 * This is supposed to be a short text and the API user should avoid
65 * making it longer than two lines.
66 */
67 property string description: ""
68
69 /**
70 * @brief This property holds the value of the current item in the combobox.
71 */
72 property alias currentValue: combobox.currentValue
73
74 /**
75 * @brief This property holds the text of the current item in the combobox.
76 *
77 * @see displayText
78 */
79 property alias currentText: combobox.currentText
80
81 /**
82 * @brief This property holds the model providing data for the combobox.
83 *
84 * @see displayText
85 * @see QtQuick.Controls.ComboBox.model
86 * @see <a href="https://doc.qt.io/qt-6/qtquick-modelviewsdata-modelview.html">Models and Views in QtQuick</a>
87 */
88 property var model
89
90 /**
91 * @brief This property holds the `count` of the internal combobox.
92 *
93 * @see QtQuick.Controls.ComboBox.count
94 * @since Kirigami Addons 1.4.0
95 */
96 property alias count: combobox.count
97
98 /**
99 * @brief This property holds the `textRole` of the internal combobox.
100 *
101 * @see QtQuick.Controls.ComboBox.textRole
102 */
103 property alias textRole: combobox.textRole
104
105 /**
106 * @brief This property holds the `valueRole` of the internal combobox.
107 *
108 * @see QtQuick.Controls.ComboBox.valueRole
109 */
110 property alias valueRole: combobox.valueRole
111
112 /**
113 * @brief This property holds the `currentIndex` of the internal combobox.
114 *
115 * default: `-1` when the ::model has no data, `0` otherwise
116 *
117 * @see QtQuick.Controls.ComboBox.currentIndex
118 */
119 property alias currentIndex: combobox.currentIndex
121 /**
122 * @brief This property holds the `highlightedIndex` of the internal combobox.
123 *
124 * @see QtQuick.Controls.ComboBox.highlightedIndex
125 */
126 property alias highlightedIndex: combobox.highlightedIndex
127
128 /**
129 * @brief This property holds the `displayText` of the internal combobox.
130 *
131 * This can be used to slightly modify the text to be displayed in the combobox, for instance, by adding a string with the ::currentText.
133 * @see QtQuick.Controls.ComboBox.displayText
134 */
135 property alias displayText: combobox.displayText
136
137 /**
138 * @brief This property holds the `editable` property of the internal combobox.
139 *
140 * This turns the combobox editable, allowing the user to specify
141 * existing values or add new ones.
142 *
143 * Use this only when ::displayMode is set to
144 * FormComboBoxDelegate.ComboBox.
145 *
146 * @see QtQuick.Controls.ComboBox.editable
147 */
148 property alias editable: combobox.editable
149
150 /**
151 * @brief This property holds the `editText` property of the internal combobox.
152 *
153 * @see QtQuick.Controls.ComboBox.editText
154 */
155 property alias editText: combobox.editText
157 /** @brief The enum used to determine the ::displayMode. **/
158 enum DisplayMode {
159 /**
160 * A standard combobox component containing a vertical list of values.
161 */
162 ComboBox,
163 /**
164 * A button with similar appearance to a combobox that, when clicked,
165 * shows a Kirigami.OverlaySheet at the middle of the window
166 * containing a vertical list of values.
167 */
168 Dialog,
169 /**
170 * A button with similar appearance to a combobox that, when clicked,
171 * shows a Kirigami.ScrollablePage in a new window containing a
172 * vertical list of values.
173 */
174 Page
176
177 /**
178 * @brief This property holds what display mode the delegate should show as.
179 *
180 * Set this property to the desired ::DisplayMode.
181 *
182 * default: `FormComboBoxDelegate.ComboBox`
183 *
184 * @see DisplayMode
185 */
186 property int displayMode: Kirigami.Settings.isMobile ? FormComboBoxDelegate.Dialog : FormComboBoxDelegate.ComboBox
187
188 /**
189 * @brief The delegate component to use as entries in the combobox display mode.
190 */
191 property Component comboBoxDelegate: Delegates.RoundedItemDelegate {
192 implicitWidth: ListView.view ? ListView.view.width : Kirigami.Units.gridUnit * 16
193 text: controlRoot.textRole ? (Array.isArray(controlRoot.model) ? modelData[controlRoot.textRole] : model[controlRoot.textRole]) : modelData
194 highlighted: controlRoot.highlightedIndex === index
195 }
196
197 /**
198 * @brief The delegate component to use as entries for each value in the dialog and page display mode.
199 */
200 property Component dialogDelegate: Delegates.RoundedItemDelegate {
201 implicitWidth: ListView.view ? ListView.view.width : Kirigami.Units.gridUnit * 16
202 text: controlRoot.textRole ? (Array.isArray(controlRoot.model) ? modelData[controlRoot.textRole] : model[controlRoot.textRole]) : modelData
203
204 Layout.topMargin: index == 0 ? Math.round(Kirigami.Units.smallSpacing / 2) : 0
205
206 onClicked: {
207 controlRoot.currentIndex = index;
208 controlRoot.activated(index);
209 controlRoot.closeDialog();
210 }
211 }
213 /**
214 * @brief This property holds the current status message type of
215 * the text field.
216 *
217 * This consists of an inline message with a colorful background
218 * and an appropriate icon.
219 *
220 * The status property will affect the color of ::statusMessage used.
221 *
222 * Accepted values:
223 * - `Kirigami.MessageType.Information` (blue color)
224 * - `Kirigami.MessageType.Positive` (green color)
225 * - `Kirigami.MessageType.Warning` (orange color)
226 * - `Kirigami.MessageType.Error` (red color)
227 *
228 * default: `Kirigami.MessageType.Information` if ::statusMessage is set,
229 * nothing otherwise.
231 * @see Kirigami.MessageType
232 * @since 1.5.0
233 */
234 property var status: Kirigami.MessageType.Information
235
236 /**
237 * @brief This property holds the current status message of
238 * the text field.
239 *
240 * If this property is not set, no ::status will be shown.
241 *
242 * @since 1.5.0
243 */
244 property string statusMessage: ""
245
246 /**
247 * @brief Closes the dialog or layer.
248 *
249 * This function can be used when reimplementing the ::page or ::dialog.
250 */
251 function closeDialog() {
252 if (_selectionPageItem) {
253 _selectionPageItem.closeDialog();
254 _selectionPageItem = null;
255 }
256
257 if (_selectionDialogItem) {
258 _selectionDialogItem.close();
259 }
260 }
261
262 property var _selectionPageItem: null
263 property var _selectionDialogItem: null
264 property real __indicatorMargin: controlRoot.indicator && controlRoot.indicator.visible && controlRoot.indicator.width > 0 ? controlRoot.spacing + indicator.width + controlRoot.spacing : 0
265
266 leftPadding: horizontalPadding + (!controlRoot.mirrored ? 0 : __indicatorMargin)
267 rightPadding: horizontalPadding + (controlRoot.mirrored ? 0 : __indicatorMargin)
268
269
270 // use connections instead of onClicked on root, so that users can supply
271 // their own behaviour.
272 Connections {
273 target: controlRoot
274 function onClicked() {
275 if (controlRoot.displayMode === FormComboBoxDelegate.Dialog) {
276 controlRoot._selectionDialogItem = controlRoot.dialog.createObject();
277 controlRoot._selectionDialogItem.open();
278 } else if (controlRoot.displayMode === FormComboBoxDelegate.Page) {
279 controlRoot._selectionPageItem = controlRoot.QQC2.ApplicationWindow.window.pageStack.pushDialogLayer(page)
280 } else {
281 combobox.popup.open();
282 combobox.forceActiveFocus(Qt.PopupFocusReason);
283 }
284 }
285 }
286
287 /**
288 * @brief The dialog component used for the combobox.
289 *
290 * This property allows to override the internal dialog
291 * with a custom component.
292 */
293 property Component dialog: QQC2.Dialog {
294 id: dialog
295
296 x: Math.round((parent.width - width) / 2)
297 y: Math.round((parent.height - height) / 2)
298 z: Kirigami.OverlayZStacking.z
299
300 title: controlRoot.text
301 implicitWidth: Math.min(parent.width - Kirigami.Units.gridUnit * 2, Kirigami.Units.gridUnit * 22)
302 parent: controlRoot.QQC2.Overlay.overlay
303 background: Components.DialogRoundedBackground {}
304
305 implicitHeight: Math.min(
306 Math.max(implicitBackgroundHeight + topInset + bottomInset,
307 contentHeight + topPadding + bottomPadding
308 + (implicitHeaderHeight > 0 ? implicitHeaderHeight + spacing : 0)
309 + (implicitFooterHeight > 0 ? implicitFooterHeight + spacing : 0)),
310 parent.height - Kirigami.Units.gridUnit * 2)
311
312 onClosed: destroy();
313
314 modal: true
315 focus: true
316 padding: 0
317
318 header: Kirigami.Heading {
319 text: dialog.title
320 elide: QQC2.Label.ElideRight
321 leftPadding: Kirigami.Units.largeSpacing
322 rightPadding: Kirigami.Units.largeSpacing
323 topPadding: Kirigami.Units.largeSpacing
324 bottomPadding: Kirigami.Units.largeSpacing
325 }
326
327 contentItem: ColumnLayout {
328 spacing: 0
329
330 Kirigami.Separator {
331 visible: !listView.atYBeginning
332 Layout.fillWidth: true
333 }
334
335 QQC2.ScrollView {
336 Layout.fillWidth: true
337 Layout.fillHeight: true
338
339 Layout.margins: 2
340
341 Component.onCompleted: if (background) {
342 background.visible = false;
343 }
344
345 ListView {
346 id: listView
347
348 clip: true
349 model: controlRoot.model
350 delegate: controlRoot.dialogDelegate
351 currentIndex: controlRoot.currentIndex
352 onCurrentIndexChanged: controlRoot.currentIndex = currentIndex
353 }
354 }
355
356 Kirigami.Separator {
357 visible: controlRoot.editable
358 Layout.fillWidth: true
359 }
360
361 QQC2.TextField {
362 visible: controlRoot.editable
363 onTextChanged: controlRoot.editText = text;
364 Layout.fillWidth: true
365 }
366 }
367 }
368
369 /**
370 * @brief The page component used for the combobox, if applicable.
371 *
372 * This property allows to override the internal
373 * Kirigami.ScrollablePage with a custom component.
374 */
375 property Component page: Kirigami.ScrollablePage {
376 title: controlRoot.text
377
378 ListView {
379 spacing: 0
380 model: controlRoot.model
381 delegate: controlRoot.dialogDelegate
382
383 footer: QQC2.TextField {
384 visible: controlRoot.editable
385 onTextChanged: controlRoot.editText = text;
386 Layout.fillWidth: true
387 }
388 }
389 }
390
391 function indexOfValue(value) {
392 return combobox.indexOfValue(value);
393 }
394
395 focusPolicy: Qt.StrongFocus
396 Accessible.description: description
397 Accessible.onPressAction: controlRoot.clicked()
398
399 // Only have the mouse hover feedback if the combobox is the whole delegate itself
400 background: displayMode === FormComboBoxDelegate.ComboBox ? null : selectableBackground
401 FormDelegateBackground { id: selectableBackground; control: controlRoot }
402
403 contentItem: ColumnLayout {
404 spacing: Kirigami.Units.smallSpacing
405
406 RowLayout {
407 Layout.fillWidth: true
408 spacing: Kirigami.Units.smallSpacing
409
410 QQC2.Label {
411 Layout.fillWidth: true
412 text: controlRoot.text
413 elide: Text.ElideRight
414 color: controlRoot.enabled ? Kirigami.Theme.textColor : Kirigami.Theme.disabledTextColor
415 wrapMode: Text.Wrap
416 maximumLineCount: 2
417 Accessible.ignored: true
418 }
419
420 QQC2.Label {
421 Layout.alignment: Qt.AlignRight
422 Layout.rightMargin: Kirigami.Units.smallSpacing
423 color: Kirigami.Theme.disabledTextColor
424 text: controlRoot.displayText
425 visible: controlRoot.displayMode === FormComboBoxDelegate.Dialog || controlRoot.displayMode === FormComboBoxDelegate.Page
426 }
427
428 FormArrow {
429 Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
430 direction: Qt.DownArrow
431 visible: controlRoot.displayMode === FormComboBoxDelegate.Dialog || controlRoot.displayMode === FormComboBoxDelegate.Page
432 }
433 }
434
435 QQC2.ComboBox {
436 id: combobox
437 focusPolicy: Qt.NoFocus // provided by parent
438 model: controlRoot.model
439 visible: controlRoot.displayMode == FormComboBoxDelegate.ComboBox
440 delegate: controlRoot.comboBoxDelegate
441 currentIndex: controlRoot.currentIndex
442 onActivated: index => controlRoot.activated(index)
443 onAccepted: controlRoot.accepted()
444 popup.contentItem.clip: true
445 Layout.fillWidth: true
446 }
447
448 QQC2.Label {
449 visible: controlRoot.description !== ""
450 Layout.fillWidth: true
451 text: controlRoot.description
452 color: Kirigami.Theme.disabledTextColor
453 wrapMode: Text.Wrap
454 Accessible.ignored: true
455 }
456
457 Kirigami.InlineMessage {
458 visible: controlRoot.statusMessage.length > 0
459 Layout.topMargin: visible ? Kirigami.Units.smallSpacing : 0
460 Layout.fillWidth: true
461 text: controlRoot.statusMessage
462 type: controlRoot.status
463 }
464 }
465}
466
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.