From 4e93f4fd9738ef84cfa5a7e3af6dc68ab1605491 Mon Sep 17 00:00:00 2001 From: Mae PUGIN <48982737+mornepousse@users.noreply.github.com> Date: Tue, 7 Apr 2026 08:46:39 +0200 Subject: [PATCH] style: Custom DarkComboBox + DarkTab (Dracula theme throughout) - DarkComboBox: PopupWindow dropdown with Dracula colors, replaces all std ComboBox - DarkTab: custom tab bar with purple underline, replaces std TabWidget - All 7 .slint files updated, zero std-widgets Button/ComboBox/TabWidget remaining Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/components/connection_bar.slint | 1 - ui/components/dark_combo_box.slint | 83 ++++++++++++++++++++++++++++ ui/components/key_selector.slint | 11 ++-- ui/main.slint | 88 +++++++++++++++++++++--------- ui/tabs/tab_advanced.slint | 11 ++-- ui/tabs/tab_flasher.slint | 7 ++- ui/tabs/tab_macros.slint | 5 +- ui/tabs/tab_settings.slint | 4 +- 8 files changed, 166 insertions(+), 44 deletions(-) create mode 100644 ui/components/dark_combo_box.slint diff --git a/ui/components/connection_bar.slint b/ui/components/connection_bar.slint index 40cb8dc..3361a36 100644 --- a/ui/components/connection_bar.slint +++ b/ui/components/connection_bar.slint @@ -1,4 +1,3 @@ -import { ComboBox } from "std-widgets.slint"; import { Theme } from "../theme.slint"; import { DarkButton } from "dark_button.slint"; import { AppState, ConnectionBridge, ConnectionState } from "../globals.slint"; diff --git a/ui/components/dark_combo_box.slint b/ui/components/dark_combo_box.slint new file mode 100644 index 0000000..db55b2a --- /dev/null +++ b/ui/components/dark_combo_box.slint @@ -0,0 +1,83 @@ +import { Theme } from "../theme.slint"; + +export component DarkComboBox inherits Rectangle { + in property <[string]> model; + in-out property current-index: 0; + in-out property current-value: model.length > 0 && current-index >= 0 && current-index < model.length ? model[current-index] : ""; + callback selected(string); + + height: 28px; + min-width: 60px; + border-radius: 4px; + background: ta.has-hover ? Theme.button-hover : Theme.button-bg; + border-width: 1px; + border-color: Theme.bg-primary; + + HorizontalLayout { + padding-left: 8px; + padding-right: 6px; + spacing: 4px; + + Text { + text: root.current-value; + color: Theme.fg-primary; + font-size: 11px; + vertical-alignment: center; + horizontal-stretch: 1; + overflow: elide; + } + + Text { + text: "v"; + color: Theme.fg-secondary; + font-size: 9px; + vertical-alignment: center; + } + } + + ta := TouchArea { + clicked => { popup.show(); } + mouse-cursor: pointer; + } + + popup := PopupWindow { + x: 0; + y: root.height; + width: root.width; + + Rectangle { + background: Theme.bg-primary; + border-radius: 4px; + border-width: 1px; + border-color: Theme.accent-purple; + + VerticalLayout { + padding: 2px; + + for item[idx] in root.model : Rectangle { + height: 26px; + border-radius: 3px; + background: item-ta.has-hover ? Theme.button-hover : idx == root.current-index ? Theme.bg-secondary : transparent; + + Text { + text: item; + color: idx == root.current-index ? Theme.accent-purple : Theme.fg-primary; + font-size: 11px; + vertical-alignment: center; + horizontal-alignment: left; + x: 8px; + } + + item-ta := TouchArea { + clicked => { + root.current-index = idx; + root.current-value = item; + root.selected(item); + } + mouse-cursor: pointer; + } + } + } + } + } +} diff --git a/ui/components/key_selector.slint b/ui/components/key_selector.slint index d6d1ecd..9f76b0e 100644 --- a/ui/components/key_selector.slint +++ b/ui/components/key_selector.slint @@ -1,6 +1,7 @@ -import { LineEdit, ComboBox, ScrollView } from "std-widgets.slint"; +import { LineEdit, ScrollView } from "std-widgets.slint"; import { Theme } from "../theme.slint"; import { DarkButton } from "dark_button.slint"; +import { DarkComboBox } from "dark_combo_box.slint"; import { KeySelectorBridge, KeymapBridge, KeyEntry, KeycapData } from "../globals.slint"; import { KeyButton } from "key_button.slint"; @@ -169,13 +170,13 @@ export component KeySelector inherits Rectangle { spacing: 6px; height: 32px; Text { text: "Mod:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } - mt-mod := ComboBox { + mt-mod := DarkComboBox { width: 80px; model: ["Ctrl", "Shift", "Alt", "GUI", "RCtrl", "RShift", "RAlt", "RGUI"]; current-index: 1; } Text { text: "Key:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } - mt-key := ComboBox { + mt-key := DarkComboBox { width: 70px; model: ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","1","2","3","4","5","6","7","8","9","0","Space","Enter","Esc","Tab","Bksp"]; current-index: 0; @@ -201,13 +202,13 @@ export component KeySelector inherits Rectangle { spacing: 6px; height: 32px; Text { text: "Layer:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } - lt-layer := ComboBox { + lt-layer := DarkComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index: 1; } Text { text: "Key:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } - lt-key := ComboBox { + lt-key := DarkComboBox { width: 80px; model: ["Space","Enter","Esc","Bksp","Tab","A","B","C","D","E"]; current-index: 0; diff --git a/ui/main.slint b/ui/main.slint index 3081653..4fc652f 100644 --- a/ui/main.slint +++ b/ui/main.slint @@ -1,4 +1,3 @@ -import { TabWidget } from "std-widgets.slint"; import { Theme } from "theme.slint"; import { AppState, ActiveTab } from "globals.slint"; import { ConnectionBar } from "components/connection_bar.slint"; @@ -14,6 +13,37 @@ import { TabFlasher } from "tabs/tab_flasher.slint"; export { AppState, Theme } export { ConnectionBridge, KeymapBridge, SettingsBridge, StatsBridge, AdvancedBridge, MacroBridge, FlasherBridge, KeySelectorBridge } from "globals.slint"; +component DarkTab inherits Rectangle { + in property title; + in property active; + callback clicked(); + + height: 32px; + horizontal-stretch: 1; + border-radius: 4px; + background: root.active ? Theme.bg-secondary : transparent; + + Text { + text: root.title; + color: root.active ? Theme.fg-primary : Theme.fg-secondary; + font-size: 12px; + font-weight: root.active ? 600 : 400; + horizontal-alignment: center; + vertical-alignment: center; + } + + Rectangle { + y: parent.height - 2px; + height: 2px; + background: root.active ? Theme.accent-purple : transparent; + } + + TouchArea { + clicked => { root.clicked(); } + mouse-cursor: pointer; + } +} + export component MainWindow inherits Window { title: "KaSe Controller"; preferred-width: 1000px; @@ -22,38 +52,44 @@ export component MainWindow inherits Window { min-height: 450px; background: Theme.bg-primary; + in-out property current-tab: 0; + VerticalLayout { ConnectionBar { } - TabWidget { - vertical-stretch: 1; + // Tab bar + Rectangle { + height: 34px; + background: Theme.bg-primary; - Tab { - title: "Keymap"; - TabKeymap { } - } - Tab { - title: "Advanced"; - TabAdvanced { } - } - Tab { - title: "Macros"; - TabMacros { } - } - Tab { - title: "Stats"; - TabStats { } - } - Tab { - title: "Settings"; - TabSettings { } - } - Tab { - title: "Flash"; - TabFlasher { } + HorizontalLayout { + padding-left: 8px; + padding-right: 8px; + spacing: 2px; + + DarkTab { title: "Keymap"; active: root.current-tab == 0; clicked => { root.current-tab = 0; } } + DarkTab { title: "Advanced"; active: root.current-tab == 1; clicked => { root.current-tab = 1; } } + DarkTab { title: "Macros"; active: root.current-tab == 2; clicked => { root.current-tab = 2; } } + DarkTab { title: "Stats"; active: root.current-tab == 3; clicked => { root.current-tab = 3; } } + DarkTab { title: "Settings"; active: root.current-tab == 4; clicked => { root.current-tab = 4; } } + DarkTab { title: "Flash"; active: root.current-tab == 5; clicked => { root.current-tab = 5; } } } } + // Tab content + Rectangle { + vertical-stretch: 1; + background: Theme.bg-secondary; + border-radius: 4px; + + if root.current-tab == 0 : TabKeymap { } + if root.current-tab == 1 : TabAdvanced { } + if root.current-tab == 2 : TabMacros { } + if root.current-tab == 3 : TabStats { } + if root.current-tab == 4 : TabSettings { } + if root.current-tab == 5 : TabFlasher { } + } + StatusBar { } } diff --git a/ui/tabs/tab_advanced.slint b/ui/tabs/tab_advanced.slint index ef26d28..8c7e2aa 100644 --- a/ui/tabs/tab_advanced.slint +++ b/ui/tabs/tab_advanced.slint @@ -1,6 +1,7 @@ -import { ComboBox, ScrollView } from "std-widgets.slint"; +import { ScrollView } from "std-widgets.slint"; import { Theme } from "../theme.slint"; import { DarkButton } from "../components/dark_button.slint"; +import { DarkComboBox } from "../components/dark_combo_box.slint"; import { AdvancedBridge, AppState, ConnectionState, KeymapBridge } from "../globals.slint"; component SectionHeader inherits Text { @@ -44,7 +45,7 @@ component PickButton inherits Rectangle { } } -component ModComboBox inherits ComboBox { +component ModComboBox inherits DarkComboBox { model: ["None", "Ctrl", "Shift", "Alt", "GUI", "RCtrl", "RShift", "RAlt", "RGUI"]; width: 90px; } @@ -300,11 +301,11 @@ export component TabAdvanced inherits Rectangle { spacing: 8px; alignment: start; Text { text: "L1:"; color: Theme.fg-secondary; font-size: 12px; vertical-alignment: center; } - ComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> AdvancedBridge.tri-l1-idx; } + DarkComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> AdvancedBridge.tri-l1-idx; } Text { text: "L2:"; color: Theme.fg-secondary; font-size: 12px; vertical-alignment: center; } - ComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> AdvancedBridge.tri-l2-idx; } + DarkComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> AdvancedBridge.tri-l2-idx; } Text { text: "-> L3:"; color: Theme.fg-secondary; font-size: 12px; vertical-alignment: center; } - ComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> AdvancedBridge.tri-l3-idx; } + DarkComboBox { width: 50px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> AdvancedBridge.tri-l3-idx; } DarkButton { text: "Set"; clicked => { AdvancedBridge.set-trilayer(AdvancedBridge.tri-l1-idx, AdvancedBridge.tri-l2-idx, AdvancedBridge.tri-l3-idx); } } } } diff --git a/ui/tabs/tab_flasher.slint b/ui/tabs/tab_flasher.slint index 3a499a4..41544b6 100644 --- a/ui/tabs/tab_flasher.slint +++ b/ui/tabs/tab_flasher.slint @@ -1,6 +1,7 @@ -import { ComboBox, LineEdit } from "std-widgets.slint"; +import { LineEdit } from "std-widgets.slint"; import { Theme } from "../theme.slint"; import { DarkButton } from "../components/dark_button.slint"; +import { DarkComboBox } from "../components/dark_combo_box.slint"; import { FlasherBridge } from "../globals.slint"; export component TabFlasher inherits Rectangle { @@ -44,7 +45,7 @@ export component TabFlasher inherits Rectangle { HorizontalLayout { spacing: 8px; - if FlasherBridge.prog-ports.length > 0 : ComboBox { + if FlasherBridge.prog-ports.length > 0 : DarkComboBox { horizontal-stretch: 1; model: FlasherBridge.prog-ports; selected(value) => { @@ -89,7 +90,7 @@ export component TabFlasher inherits Rectangle { font-weight: 600; } - ComboBox { + DarkComboBox { model: ["factory (0x20000)", "ota_0 (0x220000)"]; current-index <=> FlasherBridge.flash-offset-index; } diff --git a/ui/tabs/tab_macros.slint b/ui/tabs/tab_macros.slint index 53498cd..0eb223e 100644 --- a/ui/tabs/tab_macros.slint +++ b/ui/tabs/tab_macros.slint @@ -1,6 +1,7 @@ -import { ComboBox, ScrollView, LineEdit } from "std-widgets.slint"; +import { ScrollView, LineEdit } from "std-widgets.slint"; import { Theme } from "../theme.slint"; import { DarkButton } from "../components/dark_button.slint"; +import { DarkComboBox } from "../components/dark_combo_box.slint"; import { MacroBridge, AppState, ConnectionState, KeymapBridge } from "../globals.slint"; export component TabMacros inherits Rectangle { @@ -38,7 +39,7 @@ export component TabMacros inherits Rectangle { alignment: start; Text { text: "Slot:"; color: Theme.fg-secondary; font-size: 12px; vertical-alignment: center; } - ComboBox { + DarkComboBox { width: 60px; model: ["0","1","2","3","4","5","6","7","8","9"]; current-index <=> MacroBridge.new-slot-idx; diff --git a/ui/tabs/tab_settings.slint b/ui/tabs/tab_settings.slint index 4bbcbef..18f9869 100644 --- a/ui/tabs/tab_settings.slint +++ b/ui/tabs/tab_settings.slint @@ -1,5 +1,5 @@ -import { ComboBox } from "std-widgets.slint"; import { Theme } from "../theme.slint"; +import { DarkComboBox } from "../components/dark_combo_box.slint"; import { SettingsBridge } from "../globals.slint"; export component TabSettings inherits Rectangle { @@ -46,7 +46,7 @@ export component TabSettings inherits Rectangle { VerticalLayout { alignment: center; - ComboBox { + DarkComboBox { width: 200px; model: SettingsBridge.available-layouts; current-index <=> SettingsBridge.selected-layout-index;