From e8f4ee41a6559de7b82ed8a42e373a8822ab8d1e Mon Sep 17 00:00:00 2001 From: Mae PUGIN <48982737+mornepousse@users.noreply.github.com> Date: Tue, 7 Apr 2026 11:06:32 +0200 Subject: [PATCH] feat: KO creation with checkboxes (matching egui) - Replace ModComboBox with Ctrl/Shift/Alt checkboxes for KO mods - Build HID bitmask from checkboxes (Ctrl=0x01, Shift=0x02, Alt=0x04) - Add DarkCheckbox component (Dracula theme) - Descriptive label: "When you press [From] key, output [To] key instead" - Read all KO fields from properties (no args in callback) Co-Authored-By: Claude Opus 4.6 (1M context) --- src/main.rs | 21 ++++++++------- ui/components/dark_checkbox.slint | 45 +++++++++++++++++++++++++++++++ ui/globals.slint | 10 ++++--- ui/tabs/tab_advanced.slint | 18 ++++++++----- 4 files changed, 75 insertions(+), 19 deletions(-) create mode 100644 ui/components/dark_checkbox.slint diff --git a/src/main.rs b/src/main.rs index 1ebeaf8..3cf06b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1083,13 +1083,18 @@ fn main() { let tx = bg_tx.clone(); let window_weak = window.as_weak(); - window.global::().on_create_ko(move |trig_code, trig_mod_idx, result_code, res_mod_idx| { + window.global::().on_create_ko(move || { let Some(w) = window_weak.upgrade() else { return }; - let trig = trig_code as u8; - let trig_mod = mod_idx_to_byte(trig_mod_idx); - let result = result_code as u8; - let res_mod = mod_idx_to_byte(res_mod_idx); - let next_idx = w.global::().get_key_overrides().row_count() as u8; + let adv = w.global::(); + let trig = adv.get_new_ko_trigger_code() as u8; + let trig_mod = (adv.get_new_ko_trig_ctrl() as u8) + | ((adv.get_new_ko_trig_shift() as u8) << 1) + | ((adv.get_new_ko_trig_alt() as u8) << 2); + let result = adv.get_new_ko_result_code() as u8; + let res_mod = (adv.get_new_ko_res_ctrl() as u8) + | ((adv.get_new_ko_res_shift() as u8) << 1) + | ((adv.get_new_ko_res_alt() as u8) << 2); + let next_idx = adv.get_key_overrides().row_count() as u8; let cmd = logic::protocol::cmd_koset(next_idx, trig, trig_mod, result, res_mod); let serial = serial.clone(); let tx = tx.clone(); @@ -1100,9 +1105,7 @@ fn main() { let lines = ser.query_command("KO?").unwrap_or_default(); let _ = tx.send(BgMsg::TextLines("ko".into(), lines)); }); - if let Some(w) = window_weak.upgrade() { - w.global::().set_status_text("Creating key override...".into()); - } + w.global::().set_status_text("Creating key override...".into()); }); } diff --git a/ui/components/dark_checkbox.slint b/ui/components/dark_checkbox.slint new file mode 100644 index 0000000..03955ee --- /dev/null +++ b/ui/components/dark_checkbox.slint @@ -0,0 +1,45 @@ +import { Theme } from "../theme.slint"; + +export component DarkCheckbox inherits Rectangle { + in property text; + in-out property checked: false; + + height: 24px; + min-width: box.width + label.preferred-width + 10px; + + HorizontalLayout { + spacing: 4px; + alignment: start; + + box := Rectangle { + width: 16px; + height: 16px; + y: (parent.height - self.height) / 2; + border-radius: 3px; + border-width: 1px; + border-color: root.checked ? Theme.accent-purple : Theme.button-border; + background: root.checked ? Theme.accent-purple : Theme.button-bg; + + Text { + text: root.checked ? "v" : ""; + color: #282a36; + font-size: 11px; + font-weight: 700; + horizontal-alignment: center; + vertical-alignment: center; + } + } + + label := Text { + text: root.text; + color: Theme.fg-primary; + font-size: 11px; + vertical-alignment: center; + } + } + + TouchArea { + clicked => { root.checked = !root.checked; } + mouse-cursor: pointer; + } +} diff --git a/ui/globals.slint b/ui/globals.slint index 47e77a2..bf74f85 100644 --- a/ui/globals.slint +++ b/ui/globals.slint @@ -171,10 +171,14 @@ export global AdvancedBridge { // KO creation: keycodes for trigger and result in-out property new-ko-trigger-code: 0; in-out property new-ko-trigger-name: "Pick..."; - in-out property new-ko-trig-mod-idx: 0; // 0=None,1=Ctrl,2=Shift,... + in-out property new-ko-trig-ctrl: false; + in-out property new-ko-trig-shift: false; + in-out property new-ko-trig-alt: false; in-out property new-ko-result-code: 0; in-out property new-ko-result-name: "Pick..."; - in-out property new-ko-res-mod-idx: 0; + in-out property new-ko-res-ctrl: false; + in-out property new-ko-res-shift: false; + in-out property new-ko-res-alt: false; // Leader creation in-out property new-leader-seq0-code: 0; in-out property new-leader-seq0-name: ""; @@ -200,7 +204,7 @@ export global AdvancedBridge { callback set-trilayer(int, int, int); callback bt-switch(int); callback create-combo(); // reads r1,c1,r2,c2,result from properties - callback create-ko(int, int, int, int); // trigger_code, trig_mod_idx, result_code, res_mod_idx + callback create-ko(); // reads all fields from properties callback create-leader(int, int); // result_code, result_mod_idx (sequence comes from seq0-3 properties) // TAMA in property tama-status: ""; diff --git a/ui/tabs/tab_advanced.slint b/ui/tabs/tab_advanced.slint index 38a0e4b..408a22e 100644 --- a/ui/tabs/tab_advanced.slint +++ b/ui/tabs/tab_advanced.slint @@ -2,6 +2,7 @@ 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 { DarkCheckbox } from "../components/dark_checkbox.slint"; import { AdvancedBridge, AppState, ConnectionState, KeymapBridge } from "../globals.slint"; component SectionHeader inherits Text { @@ -141,25 +142,28 @@ export component TabAdvanced inherits Rectangle { padding: 10px; spacing: 8px; Text { text: "Add Key Override"; color: Theme.fg-secondary; font-size: 12px; font-weight: 600; } + Text { text: "When you press [From] key, output [To] key instead"; color: Theme.fg-secondary; font-size: 10px; } HorizontalLayout { spacing: 8px; alignment: start; - Text { text: "Trigger:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } + Text { text: "From:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } PickButton { label: AdvancedBridge.new-ko-trigger-name; target: "ko-trigger"; } - Text { text: "Mod:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } - ModComboBox { current-index <=> AdvancedBridge.new-ko-trig-mod-idx; } + DarkCheckbox { text: "Ctrl"; checked <=> AdvancedBridge.new-ko-trig-ctrl; } + DarkCheckbox { text: "Shift"; checked <=> AdvancedBridge.new-ko-trig-shift; } + DarkCheckbox { text: "Alt"; checked <=> AdvancedBridge.new-ko-trig-alt; } } HorizontalLayout { spacing: 8px; alignment: start; - Text { text: "Result:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } + Text { text: "To:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } PickButton { label: AdvancedBridge.new-ko-result-name; target: "ko-result"; } - Text { text: "Mod:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; } - ModComboBox { current-index <=> AdvancedBridge.new-ko-res-mod-idx; } + DarkCheckbox { text: "Ctrl"; checked <=> AdvancedBridge.new-ko-res-ctrl; } + DarkCheckbox { text: "Shift"; checked <=> AdvancedBridge.new-ko-res-shift; } + DarkCheckbox { text: "Alt"; checked <=> AdvancedBridge.new-ko-res-alt; } } DarkButton { text: "Add Key Override"; - clicked => { AdvancedBridge.create-ko(AdvancedBridge.new-ko-trigger-code, AdvancedBridge.new-ko-trig-mod-idx, AdvancedBridge.new-ko-result-code, AdvancedBridge.new-ko-res-mod-idx); } + clicked => { AdvancedBridge.create-ko(); } } } }