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) <noreply@anthropic.com>
This commit is contained in:
Mae PUGIN 2026-04-07 08:46:39 +02:00
parent 28fbf10b79
commit 4e93f4fd97
8 changed files with 166 additions and 44 deletions

View file

@ -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";

View file

@ -0,0 +1,83 @@
import { Theme } from "../theme.slint";
export component DarkComboBox inherits Rectangle {
in property <[string]> model;
in-out property <int> current-index: 0;
in-out property <string> 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;
}
}
}
}
}
}

View file

@ -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;

View file

@ -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 <string> title;
in property <bool> 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,36 +52,42 @@ export component MainWindow inherits Window {
min-height: 450px;
background: Theme.bg-primary;
in-out property <int> current-tab: 0;
VerticalLayout {
ConnectionBar { }
TabWidget {
vertical-stretch: 1;
// Tab bar
Rectangle {
height: 34px;
background: Theme.bg-primary;
Tab {
title: "Keymap";
TabKeymap { }
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 {
title: "Advanced";
TabAdvanced { }
}
Tab {
title: "Macros";
TabMacros { }
}
Tab {
title: "Stats";
TabStats { }
}
Tab {
title: "Settings";
TabSettings { }
}
Tab {
title: "Flash";
TabFlasher { }
}
// 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 { }

View file

@ -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); } }
}
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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;