From 3f3a13d2548f907f5cab5efea1225a1a6e82d3bd Mon Sep 17 00:00:00 2001 From: quadbyte Date: Fri, 15 Aug 2025 08:19:18 -0400 Subject: [PATCH] NComBox: refactored to follow the QML way, should help with bindings. --- Modules/Demo/DemoPanel.qml | 40 ++++++-- Modules/Settings/Tabs/AudioTab.qml | 12 ++- Modules/Settings/Tabs/ScreenRecorderTab.qml | 105 +++++++++++++++++--- Modules/Settings/Tabs/WallpaperTab.qml | 80 ++++++++++++++- Services/ColorSchemes.qml | 2 +- Services/Settings.qml | 2 - Services/Wallpapers.qml | 4 +- Widgets/NComboBox.qml | 87 +++++++++------- 8 files changed, 267 insertions(+), 65 deletions(-) diff --git a/Modules/Demo/DemoPanel.qml b/Modules/Demo/DemoPanel.qml index 9d3eaaf..0a5c748 100644 --- a/Modules/Demo/DemoPanel.qml +++ b/Modules/Demo/DemoPanel.qml @@ -221,12 +221,40 @@ NLoader { NComboBox { label: "Animal" - description: "What's your favorite" - optionsKeys: ["cat", "dog", "bird", "monkey", "fish", "turtle", "elephant", "tiger"] - optionsLabels: ["Cat", "Dog", "Bird", "Monkey", "Fish", "Turtle", "Elephant", "Tiger"] - currentKey: "cat" - onSelected: function (value) { - console.log("[DemoPanel] NComboBox: selected ", value) + description: "What's your favorite?" + model: ListModel { + ListElement { + key: "cat" + name: "Cat" + } + ListElement { + key: "dog" + name: "Dog" + } + ListElement { + key: "bird" + name: "Bird" + } + ListElement { + key: "fish" + name: "Fish" + } + ListElement { + key: "turtle" + name: "Turtle" + } + ListElement { + key: "elephant" + name: "Elephant" + } + ListElement { + key: "tiger" + name: "Tiger" + } + } + currentKey: "dog" + onSelected: function (key) { + console.log("[DemoPanel] NComboBox: selected ", key) } } diff --git a/Modules/Settings/Tabs/AudioTab.qml b/Modules/Settings/Tabs/AudioTab.qml index 39aaa64..3314c4e 100644 --- a/Modules/Settings/Tabs/AudioTab.qml +++ b/Modules/Settings/Tabs/AudioTab.qml @@ -256,8 +256,16 @@ ColumnLayout { id: audioVisualizerCombo label: "Visualization Type" description: "Choose a visualization type for media playback" - optionsKeys: ["none", "linear"] - optionsLabels: ["None", "Linear"] + model: ListModel { + ListElement { + key: "none" + name: "None" + } + ListElement { + key: "linear" + name: "Linear" + } + } currentKey: Settings.data.audio.visualizerType onSelected: function (key) { Settings.data.audio.visualizerType = key diff --git a/Modules/Settings/Tabs/ScreenRecorderTab.qml b/Modules/Settings/Tabs/ScreenRecorderTab.qml index 6240ae1..2ecce19 100644 --- a/Modules/Settings/Tabs/ScreenRecorderTab.qml +++ b/Modules/Settings/Tabs/ScreenRecorderTab.qml @@ -95,8 +95,24 @@ ColumnLayout { NComboBox { label: "Frame Rate" description: "Target frame rate for screen recordings (default: 60)" - optionsKeys: ["30", "60", "120", "240"] - optionsLabels: ["30 FPS", "60 FPS", "120 FPS", "240 FPS"] + model: ListModel { + ListElement { + key: "30" + name: "30 FPS" + } + ListElement { + key: "60" + name: "60 FPS" + } + ListElement { + key: "120" + name: "120 FPS" + } + ListElement { + key: "240" + name: "240 FPS" + } + } currentKey: Settings.data.screenRecorder.frameRate onSelected: function (key) { Settings.data.screenRecorder.frameRate = key @@ -107,8 +123,24 @@ ColumnLayout { NComboBox { label: "Video Quality" description: "Higher quality results in larger file sizes" - optionsKeys: ["medium", "high", "very_high", "ultra"] - optionsLabels: ["Medium", "High", "Very High", "Ultra"] + model: ListModel { + ListElement { + key: "medium" + name: "Medium" + } + ListElement { + key: "high" + name: "High" + } + ListElement { + key: "very_high" + name: "Very High" + } + ListElement { + key: "ultra" + name: "Ultra" + } + } currentKey: Settings.data.screenRecorder.quality onSelected: function (key) { Settings.data.screenRecorder.quality = key @@ -119,8 +151,28 @@ ColumnLayout { NComboBox { label: "Video Codec" description: "Different codecs offer different compression and compatibility" - optionsKeys: ["h264", "hevc", "av1", "vp8", "vp9"] - optionsLabels: ["H264", "HEVC", "AV1", "VP8", "VP9"] + model: ListModel { + ListElement { + key: "h264" + name: "H264" + } + ListElement { + key: "hevc" + name: "HEVC" + } + ListElement { + key: "av1" + name: "AV1" + } + ListElement { + key: "vp8" + name: "VP8" + } + ListElement { + key: "vp9" + name: "VP9" + } + } currentKey: Settings.data.screenRecorder.videoCodec onSelected: function (key) { Settings.data.screenRecorder.videoCodec = key @@ -131,8 +183,16 @@ ColumnLayout { NComboBox { label: "Color Range" description: "Limited is recommended for better compatibility" - optionsKeys: ["limited", "full"] - optionsLabels: ["Limited", "Full"] + model: ListModel { + ListElement { + key: "limited" + name: "Limited" + } + ListElement { + key: "full" + name: "Full" + } + } currentKey: Settings.data.screenRecorder.colorRange onSelected: function (key) { Settings.data.screenRecorder.colorRange = key @@ -163,8 +223,20 @@ ColumnLayout { NComboBox { label: "Audio Source" description: "Audio source to capture during recording" - optionsKeys: ["default_output", "default_input", "both"] - optionsLabels: ["System Audio", "Microphone", "System Audio + Microphone"] + model: ListModel { + ListElement { + key: "default_output" + name: "System Output" + } + ListElement { + key: "default_input" + name: "Microphone Input" + } + ListElement { + key: "both" + name: "System Output + Microphone Input" + } + } currentKey: Settings.data.screenRecorder.audioSource onSelected: function (key) { Settings.data.screenRecorder.audioSource = key @@ -175,8 +247,17 @@ ColumnLayout { NComboBox { label: "Audio Codec" description: "Opus is recommended for best performance and smallest audio size" - optionsKeys: ["opus", "aac"] - optionsLabels: ["OPUS", "AAC"] + model: ListModel { + ListElement { + key: "opus" + name: "Opus" + } + ListElement { + key: "aac" + name: "AAC" + } + } + currentKey: Settings.data.screenRecorder.audioCodec onSelected: function (key) { Settings.data.screenRecorder.audioCodec = key diff --git a/Modules/Settings/Tabs/WallpaperTab.qml b/Modules/Settings/Tabs/WallpaperTab.qml index d657b1b..41288d9 100644 --- a/Modules/Settings/Tabs/WallpaperTab.qml +++ b/Modules/Settings/Tabs/WallpaperTab.qml @@ -183,8 +183,24 @@ ColumnLayout { NComboBox { label: "Resize Mode" description: "How SWWW should resize wallpapers to fit the screen" - optionsKeys: ["no", "crop", "fit", "stretch"] - optionsLabels: ["No", "Crop", "Fit", "Stretch"] + model: ListModel { + ListElement { + key: "no" + name: "No" + } + ListElement { + key: "crop" + name: "Crop" + } + ListElement { + key: "fit" + name: "Fit" + } + ListElement { + key: "stretch" + name: "Stretch" + } + } currentKey: Settings.data.wallpaper.swww.resizeMethod onSelected: function (key) { Settings.data.wallpaper.swww.resizeMethod = key @@ -195,8 +211,64 @@ ColumnLayout { NComboBox { label: "Transition Type" description: "Animation type when switching between wallpapers" - optionsKeys: ["none", "simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer", "random"] - optionsLabels: ["None", "Simple", "Fade", "Left", "Right", "Top", "Bottom", "Wipe", "Wave", "Grow", "Center", "Any", "Outer", "Random"] + model: ListModel { + ListElement { + key: "none" + name: "None" + } + ListElement { + key: "simple" + name: "Simple" + } + ListElement { + key: "fade" + name: "Fade" + } + ListElement { + key: "left" + name: "Left" + } + ListElement { + key: "right" + name: "Right" + } + ListElement { + key: "top" + name: "Top" + } + ListElement { + key: "bottom" + name: "Bottom" + } + ListElement { + key: "wipe" + name: "Wipe" + } + ListElement { + key: "wave" + name: "Wave" + } + ListElement { + key: "grow" + name: "Grow" + } + ListElement { + key: "center" + name: "Center" + } + ListElement { + key: "any" + name: "Any" + } + ListElement { + key: "outer" + name: "Outer" + } + ListElement { + key: "random" + name: "Random" + } + } currentKey: Settings.data.wallpaper.swww.transitionType onSelected: function (key) { Settings.data.wallpaper.swww.transitionType = key diff --git a/Services/ColorSchemes.qml b/Services/ColorSchemes.qml index d8c3638..2075510 100644 --- a/Services/ColorSchemes.qml +++ b/Services/ColorSchemes.qml @@ -9,7 +9,7 @@ Singleton { id: root Component.onCompleted: { - console.log("[ColorSchemes] Service initialized") + console.log("[ColorSchemes] Service started") loadColorSchemes() } diff --git a/Services/Settings.qml b/Services/Settings.qml index 5e2d4f4..5f79da6 100644 --- a/Services/Settings.qml +++ b/Services/Settings.qml @@ -70,7 +70,6 @@ Singleton { property bool showActiveWindowIcon: false property bool showSystemInfo: false property bool showMedia: false - // New: optional taskbar visibility in bar property bool showTaskbar: false property list monitors: [] } @@ -107,7 +106,6 @@ Singleton { property string quality: "very_high" property string colorRange: "limited" property bool showCursor: true - // New: optional audio source selection (default: system output) property string audioSource: "default_output" } diff --git a/Services/Wallpapers.qml b/Services/Wallpapers.qml index 82e9e38..a8d9847 100644 --- a/Services/Wallpapers.qml +++ b/Services/Wallpapers.qml @@ -9,7 +9,7 @@ Singleton { id: root Component.onCompleted: { - console.log("[Wallpapers] Service initialized") + console.log("[Wallpapers] Service started") loadWallpapers() // Wallpaper is set when the settings are loaded. @@ -23,7 +23,7 @@ Singleton { // SWWW property string transitionType: Settings.data.wallpaper.swww.transitionType property var randomChoices: ["simple", "fade", "left", "right", "top", "bottom", "wipe", "wave", "grow", "center", "any", "outer"] - + function loadWallpapers() { console.log("[Wallpapers] Load Wallpapers") scanning = true diff --git a/Widgets/NComboBox.qml b/Widgets/NComboBox.qml index f489011..0c36869 100644 --- a/Widgets/NComboBox.qml +++ b/Widgets/NComboBox.qml @@ -12,8 +12,9 @@ ColumnLayout { property string label: "" property string description: "" - property list optionsKeys: [] - property list optionsLabels: [] + property ListModel model: { + + } property string currentKey: '' signal selected(string key) @@ -24,14 +25,12 @@ ColumnLayout { ColumnLayout { spacing: Style.marginTiniest * scaling Layout.fillWidth: true - NText { text: label font.pointSize: Style.fontSizeMedium * scaling font.weight: Style.fontWeightBold color: Colors.mOnSurface } - NText { text: description font.pointSize: Style.fontSizeSmall * scaling @@ -40,19 +39,25 @@ ColumnLayout { } } + function findIndexByKey(key) { + for (var i = 0; i < root.model.count; i++) { + if (root.model.get(i).key === key) { + return i + } + } + return -1 + } + ComboBox { id: combo - Layout.fillWidth: true Layout.preferredHeight: height - - model: optionsKeys - currentIndex: model.indexOf(currentKey) + model: model + currentIndex: findIndexByKey(currentKey) onActivated: { - root.selected(model[combo.currentIndex]) + root.selected(model.get(combo.currentIndex).key) } - // Rounded background background: Rectangle { implicitWidth: 120 * scaling implicitHeight: preferredHeight @@ -62,18 +67,16 @@ ColumnLayout { radius: Style.radiusMedium * scaling } - // Label (currently selected) contentItem: NText { leftPadding: Style.marginLarge * scaling rightPadding: combo.indicator.width + Style.marginLarge * scaling font.pointSize: Style.fontSizeMedium * scaling verticalAlignment: Text.AlignVCenter elide: Text.ElideRight - text: (combo.currentIndex >= 0 - && combo.currentIndex < root.optionsLabels.length) ? root.optionsLabels[combo.currentIndex] : "" + text: (combo.currentIndex >= 0 && combo.currentIndex < root.model.count) ? root.model.get( + combo.currentIndex).name : "" } - // Drop down indicator indicator: NText { x: combo.width - width - Style.marginMedium * scaling y: combo.topPadding + (combo.availableHeight - height) / 2 @@ -89,11 +92,43 @@ ColumnLayout { padding: Style.marginMedium * scaling contentItem: ListView { + property var comboBoxRoot: root clip: true implicitHeight: contentHeight - model: combo.popup.visible ? combo.delegateModel : null - currentIndex: combo.highlightedIndex + model: combo.popup.visible ? root.model : null ScrollIndicator.vertical: ScrollIndicator {} + + delegate: ItemDelegate { + width: combo.width + hoverEnabled: true + highlighted: ListView.view.currentIndex === index + + onHoveredChanged: { + if (hovered) { + ListView.view.currentIndex = index + } + } + + onClicked: { + ListView.view.comboBoxRoot.selected(ListView.view.comboBoxRoot.model.get(index).key) + combo.currentIndex = index + combo.popup.close() + } + + contentItem: NText { + text: name + font.pointSize: Style.fontSizeMedium * scaling + color: highlighted ? Colors.mSurface : Colors.mOnSurface + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + width: combo.width - Style.marginMedium * scaling * 3 + color: highlighted ? Colors.mTertiary : "transparent" + radius: Style.radiusSmall * scaling + } + } } background: Rectangle { @@ -103,25 +138,5 @@ ColumnLayout { radius: Style.radiusMedium * scaling } } - - delegate: ItemDelegate { - width: combo.width - highlighted: combo.highlightedIndex === index - - contentItem: NText { - text: (combo.model.indexOf(modelData) >= 0 && combo.model.indexOf( - modelData) < root.optionsLabels.length) ? root.optionsLabels[combo.model.indexOf(modelData)] : "" - font.pointSize: Style.fontSizeMedium * scaling - color: highlighted ? Colors.mSurface : Colors.mOnSurface - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - } - - background: Rectangle { - width: combo.width - Style.marginMedium * scaling * 3 - color: highlighted ? Colors.mTertiary : "transparent" - radius: Style.radiusSmall * scaling - } - } } }