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
156
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 checked: controlRoot.currentIndex === index
204
205 Layout.topMargin: index == 0 ? Math.round(Kirigami.Units.smallSpacing / 2) : 0
207 onClicked: {
208 controlRoot.currentIndex = index;
209 controlRoot.activated(index);
210 controlRoot.closeDialog();
211 }
213
214 /**
215 * @brief This property holds the current status message type of
216 * the text field.
217 *
218 * This consists of an inline message with a colorful background
219 * and an appropriate icon.
220 *
221 * The status property will affect the color of ::statusMessage used.
222 *
223 * Accepted values:
224 * - `Kirigami.MessageType.Information` (blue color)
225 * - `Kirigami.MessageType.Positive` (green color)
226 * - `Kirigami.MessageType.Warning` (orange color)
227 * - `Kirigami.MessageType.Error` (red color)
228 *
229 * default: `Kirigami.MessageType.Information` if ::statusMessage is set,
230 * nothing otherwise.
231 *
232 * @see Kirigami.MessageType
233 * @since 1.5.0
234 */
235 property var status: Kirigami.MessageType.Information
236
237 /**
238 * @brief This property holds the current status message of
239 * the text field.
240 *
241 * If this property is not set, no ::status will be shown.
242 *
243 * @since 1.5.0
244 */
245 property string statusMessage: ""
246
247 /**
248 * @brief Closes the dialog or layer.
249 *
250 * This function can be used when reimplementing the ::page or ::dialog.
251 */
252 function closeDialog() {
253 if (_selectionPageItem) {
254 _selectionPageItem.closeDialog();
255 _selectionPageItem = null;
256 }
257
258 if (dialog) {
259 dialog.close();
260 }
261 }
262
263 property var _selectionPageItem: 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 const dialogObject = controlRoot.dialog.createObject();
277 dialogObject.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: applicationWindow().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 }
352 }
353
354 Kirigami.Separator {
355 visible: controlRoot.editable
356 Layout.fillWidth: true
357 }
358
359 QQC2.TextField {
360 visible: controlRoot.editable
361 onTextChanged: controlRoot.editText = text;
362 Layout.fillWidth: true
363 }
364 }
365 }
366
367 /**
368 * @brief The page component used for the combobox, if applicable.
369 *
370 * This property allows to override the internal
371 * Kirigami.ScrollablePage with a custom component.
372 */
373 property Component page: Kirigami.ScrollablePage {
374 title: controlRoot.text
375
376 ListView {
377 spacing: 0
378 model: controlRoot.model
379 delegate: controlRoot.dialogDelegate
380
381 footer: QQC2.TextField {
382 visible: controlRoot.editable
383 onTextChanged: controlRoot.editText = text;
384 Layout.fillWidth: true
385 }
386 }
387 }
388
389 function indexOfValue(value) {
390 return combobox.indexOfValue(value);
391 }
392
393 focusPolicy: Qt.StrongFocus
394 Accessible.description: description
395 Accessible.onPressAction: controlRoot.clicked()
396
397 // Only have the mouse hover feedback if the combobox is the whole delegate itself
398 background: displayMode === FormComboBoxDelegate.ComboBox ? null : selectableBackground
399 FormDelegateBackground { id: selectableBackground; control: controlRoot }
400
401 contentItem: ColumnLayout {
402 spacing: Kirigami.Units.smallSpacing
403
404 RowLayout {
405 Layout.fillWidth: true
406 spacing: Kirigami.Units.smallSpacing
407
408 QQC2.Label {
409 Layout.fillWidth: true
410 text: controlRoot.text
411 elide: Text.ElideRight
412 color: controlRoot.enabled ? Kirigami.Theme.textColor : Kirigami.Theme.disabledTextColor
413 wrapMode: Text.Wrap
414 maximumLineCount: 2
415 Accessible.ignored: true
416 }
417
418 QQC2.Label {
419 Layout.alignment: Qt.AlignRight
420 Layout.rightMargin: Kirigami.Units.smallSpacing
421 color: Kirigami.Theme.disabledTextColor
422 text: controlRoot.displayText
423 visible: controlRoot.displayMode === FormComboBoxDelegate.Dialog || controlRoot.displayMode === FormComboBoxDelegate.Page
424 }
425
426 FormArrow {
427 Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
428 direction: Qt.DownArrow
429 visible: controlRoot.displayMode === FormComboBoxDelegate.Dialog || controlRoot.displayMode === FormComboBoxDelegate.Page
430 }
431 }
432
433 QQC2.ComboBox {
434 id: combobox
435 focusPolicy: Qt.NoFocus // provided by parent
436 model: controlRoot.model
437 visible: controlRoot.displayMode == FormComboBoxDelegate.ComboBox
438 delegate: controlRoot.comboBoxDelegate
439 currentIndex: controlRoot.currentIndex
440 onActivated: index => controlRoot.activated(index)
441 onAccepted: controlRoot.accepted()
442 popup.contentItem.clip: true
443 Layout.fillWidth: true
444 }
445
446 QQC2.Label {
447 visible: controlRoot.description !== ""
448 Layout.fillWidth: true
449 text: controlRoot.description
450 color: Kirigami.Theme.disabledTextColor
451 wrapMode: Text.Wrap
452 Accessible.ignored: true
453 }
454
455 Kirigami.InlineMessage {
456 visible: controlRoot.statusMessage.length > 0
457 Layout.topMargin: visible ? Kirigami.Units.smallSpacing : 0
458 Layout.fillWidth: true
459 text: controlRoot.statusMessage
460 type: controlRoot.status
461 }
462 }
463}
464
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:03:50 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.