diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml index 2f391dd1f..18add2cc3 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml @@ -1,15 +1,208 @@ +pragma ComponentBehavior: Bound import QtQuick -import QtQuick.Controls.Material import QtQuick.Controls +import QtQuick.Layouts +import qs.services import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions ComboBox { id: root - - Material.theme: Material.System - Material.accent: Appearance.m3colors.m3primary - Material.primary: Appearance.m3colors.m3primary - Material.background: Appearance.m3colors.m3surface - Material.foreground: Appearance.m3colors.m3onSurface - Material.containerStyle: Material.Outlined + + property string buttonIcon: "" + property real buttonRadius: height / 2 + property color colBackground: Appearance.colors.colSecondaryContainer + property color colBackgroundHover: Appearance.colors.colSecondaryContainerHover + property color colBackgroundActive: Appearance.colors.colSecondaryContainerActive + + implicitHeight: 40 + Layout.fillWidth: true + + background: Rectangle { + radius: root.buttonRadius + color: (root.down && !root.popup.visible) ? root.colBackgroundActive : root.hovered ? root.colBackgroundHover : root.colBackground + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + } + } + + indicator: MaterialSymbol { + x: root.width - width - 16 + y: root.height / 2 - height / 2 + text: "keyboard_arrow_down" + iconSize: Appearance.font.pixelSize.larger + color: Appearance.colors.colOnSecondaryContainer + + rotation: root.popup.visible ? 180 : 0 + Behavior on rotation { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + } + + contentItem: Item { + implicitWidth: buttonLayout.implicitWidth + implicitHeight: buttonLayout.implicitHeight + + RowLayout { + id: buttonLayout + anchors.fill: parent + spacing: 8 + anchors.leftMargin: 16 + anchors.rightMargin: 16 + + Loader { + Layout.alignment: Qt.AlignVCenter + active: root.buttonIcon.length > 0 || (root.currentIndex >= 0 && typeof root.model[root.currentIndex] === 'object' && root.model[root.currentIndex]?.icon) + visible: active + sourceComponent: MaterialSymbol { + text: { + if (root.currentIndex >= 0 && typeof root.model[root.currentIndex] === 'object' && root.model[root.currentIndex]?.icon) { + return root.model[root.currentIndex].icon; + } + return root.buttonIcon; + } + iconSize: Appearance.font.pixelSize.larger + color: Appearance.colors.colOnSecondaryContainer + } + } + + StyledText { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + color: Appearance.colors.colOnSecondaryContainer + text: root.displayText + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + } + } + + delegate: ItemDelegate { + id: itemDelegate + width: ListView.view ? ListView.view.width : root.width + implicitHeight: 40 + + required property var model + required property int index + property color color: { + if (root.currentIndex === itemDelegate.index) { + if (itemDelegate.down) return Appearance.colors.colSecondaryContainerActive; + if (itemDelegate.hovered) return Appearance.colors.colSecondaryContainerHover; + return Appearance.colors.colSecondaryContainer; + } else { + if (itemDelegate.down) return Appearance.colors.colLayer3Active; + if (itemDelegate.hovered) return Appearance.colors.colLayer3Hover; + return ColorUtils.transparentize(Appearance.colors.colLayer3); + } + } + property color colText: (root.currentIndex === itemDelegate.index) ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer3 + + background: Rectangle { + anchors.fill: parent + radius: Appearance.rounding.small + color: itemDelegate.color + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + } + } + + contentItem: RowLayout { + spacing: 8 + anchors.leftMargin: 12 + anchors.rightMargin: 12 + + Loader { + Layout.alignment: Qt.AlignVCenter + Layout.preferredHeight: Appearance.font.pixelSize.larger + active: typeof itemDelegate.model === 'object' && itemDelegate.model?.icon?.length > 0 + visible: active + + sourceComponent: Item { + implicitWidth: icon.implicitWidth + implicitHeight: Appearance.font.pixelSize.larger + + MaterialSymbol { + id: icon + anchors.centerIn: parent + text: itemDelegate.model?.icon ?? "" + iconSize: Appearance.font.pixelSize.larger + color: itemDelegate.colText + } + } + } + + StyledText { + Layout.fillWidth: true + Layout.preferredHeight: Appearance.font.pixelSize.larger + color: itemDelegate.colText + text: itemDelegate.model[root.textRole] + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + } + } + } + + popup: Popup { + y: root.height + 4 + width: root.width + height: Math.min(listView.contentHeight + topPadding + bottomPadding, 300) + padding: 8 + + enter: Transition { + PropertyAnimation { + properties: "opacity" + to: 1 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + + exit: Transition { + PropertyAnimation { + properties: "opacity" + to: 0 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + + background: Item { + StyledRectangularShadow { + target: popupBackground + } + + Rectangle { + id: popupBackground + anchors.fill: parent + radius: Appearance.rounding.normal + color: Appearance.colors.colSurfaceContainerHigh + } + } + + contentItem: StyledListView { + id: listView + clip: true + implicitHeight: contentHeight + spacing: 2 + model: root.popup.visible ? root.delegateModel : null + currentIndex: root.highlightedIndex + } + } } diff --git a/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml b/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml index c2ea1f930..6b83f9cf9 100644 --- a/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/GeneralConfig.qml @@ -128,7 +128,7 @@ ContentPage { } } } - + ContentSection { icon: "language" title: Translation.tr("Language") @@ -137,13 +137,12 @@ ContentPage { title: Translation.tr("Interface Language") tooltip: Translation.tr("Select the language for the user interface.\n\"Auto\" will use your system's locale.") - ConfigSelectionArray { + StyledComboBox { id: languageSelector - currentValue: Config.options.language.ui - onSelected: newValue => { - Config.options.language.ui = newValue; - } - options: [ + buttonIcon: "language" + textRole: "displayName" + + model: [ { displayName: Translation.tr("Auto (System)"), value: "auto" @@ -153,14 +152,22 @@ ContentPage { displayName: lang, value: lang }; - }) - ] + })] + + currentIndex: { + const index = model.findIndex(item => item.value === Config.options.language.ui); + return index !== -1 ? index : 0; + } + + onActivated: index => { + Config.options.language.ui = model[index].value; + } } } ContentSubsection { title: Translation.tr("Generate translation with Gemini") tooltip: Translation.tr("You'll need to enter your Gemini API key first.\nType /key on the sidebar for instructions.") - + ConfigRow { MaterialTextArea { id: localeInput @@ -195,7 +202,7 @@ ContentPage { ContentSubsectionLabel { text: Translation.tr("AI") } - + ConfigSelectionArray { currentValue: Config.options.policies.ai onSelected: newValue => { @@ -278,7 +285,7 @@ ContentPage { } } } - + ContentSection { icon: "nest_clock_farsight_analog" title: Translation.tr("Time") @@ -309,7 +316,6 @@ ContentPage { } Config.options.time.format = newValue; - } options: [ {