File: ComboBoxWithCustomValue.qml

package info (click to toggle)
kscreenlocker 6.5.4-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,172 kB
  • sloc: cpp: 5,698; xml: 88; sh: 32; makefile: 5
file content (144 lines) | stat: -rw-r--r-- 5,121 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
    SPDX-FileCopyrightText: 2024 Kristen McWilliam <kmcwilliampublic@gmail.com>
    SPDX-FileCopyrightText: 2024 Jakob Petsovits <jpetso@petsovits.com>

    SPDX-License-Identifier: LGPL-2.1-or-later

    Originating from kcm_screenlocker. Upstream any changes there until it goes into Kirigami (Addons).
*/

import QtQuick
import QtQuick.Controls as QQC2
import org.kde.kirigami as Kirigami

/**
 * A specialized ComboBox that assists in displaying a list of preset options plus a "Custom" option,
 * which invokes a parent-defined flow to let the user input a custom value. One or more
 * custom values can then be added to the `model` by the parent.
 *
 * In addition to the standard ComboBox `model` property, this component wants its user to set
 * two value properties that refer to one of the elements in `model`:
 *
 * - `configuredValue`, if set, will automatically update the ComboBox index to the corresponding
 *   option whenever this value itself or the `model` changes. If no corresponding option exists,
 *   the `configuredValueOptionMissing` signal will be emitted so the component user can easily
 *   add one to the `model`.
 * - `customRequesterValue`, if set, will provide additional signals named `regularValueActivated`
 *    and `customRequest` to conveniently handle simple selection updates vs. custom value selection
 *    respectively.
 *
 * Both of these properties are optional, but together they provide a convenient pattern to
 * implement item selection with custom value options.
 */
QQC2.ComboBox {
    id: root

    /**
     * The latest valid value that has been selected. Set this instead of `currentIndex`.
     *
     * Changing this value will select the corresponding index. If no corresponding index exists,
     * the `configuredValueOptionMissing` signal will be emitted.
     *
     * @see configuredValueOptionMissing
     */
    property var configuredValue: undefined

    /**
     * Emitted when `configuredValue` was not found in any element of `model`. To ensure that a
     * corresponding option exists, add an element with `configuredValue` in the `valueRole` role.
     *
     * @see configuredValue
     */
    signal configuredValueOptionMissing()

    /**
     * The value corresponding to the custom requester option which will emit the `customRequest`
     * signal when activated.
     *
     * This value is not usually persisted to configuration.
     * Activation of any other option will emit `regularValueActivated` instead.
     *
     * @see customRequest
     * @see regularValueActivated
     */
    property var customRequesterValue: undefined

    /**
     * Emitted when a regular value option (i.e. any other than the custom requester option)
     * has been activated.
     *
     * You can use this instead of the `activated` signal without manually checking against
     * `customRequesterValue`.
     *
     * @see customRequesterValue
     */
    signal regularValueActivated()

    /**
     * Emitted when the custom value requester option was activated.
     *
     * Handle this by letting the user select the custom value (e.g. via modal dialog).
     * If the user selects a custom option, assign it to `configuredValue`. Always call
     * `customRequestCompleted()` at the end to ensure the correct index is set.
     *
     * @see customRequesterValue
     * @see customRequestCompleted
     */
    signal customRequest()

    function customRequestCompleted(): void {
        updateIndex();
    }

    /** private */
    property bool __initialized: false
    /** private */
    property bool __allowSignal: true

    Component.onCompleted: {
        __initialized = true;
        updateIndex();
    }
    onModelChanged: { updateIndex(); }
    onCustomRequesterValueChanged: { updateIndex(); }
    onConfiguredValueChanged: { updateIndex(); }

    onActivated: {
        if (customRequesterValue === undefined || currentIndex < 0) {
            return;
        }

        if (currentValue === customRequesterValue) {
            customRequest();
        } else {
            regularValueActivated();
        }
    }

    /**
     * Try to set currentIndex to the option with `configuredValue` in its value role.
     * If no such option exists in the current `model`, the `configuredValueOptionMissing` signal
     * is emitted instead.
     *
     * You shouldn't usually have to call this function manually, but it is available for cases
     * when this component does not pick up on certain changes in the model.
     */
    function updateIndex(): void {
        if (!__initialized) {
            return;
        }
        if (configuredValue === undefined || configuredValue === currentValue || configuredValue === customRequesterValue) {
            return;
        }

        const idx = indexOfValue(configuredValue);
        if (idx !== -1) {
            currentIndex = idx;
        } else if (__allowSignal) {
            // Avoid recursion for `configuredValueOptionMissing` signals.
            __allowSignal = false;
            configuredValueOptionMissing();
            __allowSignal = true;
        }
    }
}