/* SPDX-FileCopyrightText: 2024 Kristen McWilliam SPDX-FileCopyrightText: 2024 Jakob Petsovits 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; } } }