2026-04-07 10:04:13 +00:00
|
|
|
import { ScrollView } from "std-widgets.slint";
|
|
|
|
|
import { DarkLineEdit } from "../components/dark_line_edit.slint";
|
2026-04-06 18:40:34 +00:00
|
|
|
import { Theme } from "../theme.slint";
|
2026-04-06 18:52:44 +00:00
|
|
|
import { DarkButton } from "../components/dark_button.slint";
|
2026-04-07 06:46:39 +00:00
|
|
|
import { DarkComboBox } from "../components/dark_combo_box.slint";
|
2026-04-06 18:40:34 +00:00
|
|
|
import { MacroBridge, AppState, ConnectionState, KeymapBridge } from "../globals.slint";
|
|
|
|
|
|
|
|
|
|
export component TabMacros inherits Rectangle {
|
|
|
|
|
background: Theme.bg-primary;
|
|
|
|
|
|
|
|
|
|
VerticalLayout {
|
|
|
|
|
padding: 16px;
|
|
|
|
|
spacing: 12px;
|
|
|
|
|
|
|
|
|
|
// Header
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
spacing: 8px;
|
|
|
|
|
Text { text: "Macros"; color: Theme.fg-primary; font-size: 20px; font-weight: 700; vertical-alignment: center; }
|
|
|
|
|
Rectangle { horizontal-stretch: 1; }
|
2026-04-06 18:52:44 +00:00
|
|
|
DarkButton {
|
2026-04-06 18:40:34 +00:00
|
|
|
text: "Refresh";
|
|
|
|
|
enabled: AppState.connection == ConnectionState.connected;
|
|
|
|
|
clicked => { MacroBridge.refresh-macros(); }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New macro builder
|
|
|
|
|
Rectangle {
|
|
|
|
|
background: Theme.bg-secondary;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
|
|
|
|
VerticalLayout {
|
|
|
|
|
padding: 12px;
|
|
|
|
|
spacing: 10px;
|
|
|
|
|
|
|
|
|
|
Text { text: "Add / Edit Macro"; color: Theme.accent-cyan; font-size: 13px; font-weight: 600; }
|
|
|
|
|
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
spacing: 8px;
|
refactor: Modularize main.rs (2478 → 58 lines), flat architecture
Split monolithic main.rs into domain modules:
- context.rs: AppContext shared state, BgMsg enum, serial_spawn helper
- models.rs: UI model builders, keycap labels, key entries, layout preview
- config.rs: keyboard config export/import via binary protocol
- dispatch.rs: BgMsg handler + WPM/layout timers
- keymap.rs: key selection, layer switch/rename, heatmap toggle
- key_selector.rs: dispatch_keycode router, apply_keycode, hex/MT/LT
- macros.rs: macro CRUD, shortcut presets, step builder
- advanced.rs: combos, KO, leaders, tap dance, BT, tama, autoshift
- settings.rs: OTA flash, config backup, keyboard layout
- flasher.rs: ESP32 bootloader flash
- layout.rs: layout JSON preview, load/export
- connection.rs: serial connect/disconnect, tab auto-refresh
- stats.rs: stats refresh
Rename logic/ → protocol/ with cleaner file names.
Remove unused original-src/ directory.
Fix DarkLineEdit double styling, add rename popup, macro shortcuts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:02:22 +00:00
|
|
|
|
|
|
|
|
Text { text: "Slot:"; color: Theme.fg-secondary; font-size: 12px; vertical-alignment: center; }
|
|
|
|
|
Text { text: "#" + MacroBridge.new-slot-idx; color: Theme.accent-purple; font-size: 12px; font-weight: 600; vertical-alignment: center; }
|
2026-04-06 18:40:34 +00:00
|
|
|
|
|
|
|
|
Text { text: "Name:"; color: Theme.fg-secondary; font-size: 12px; vertical-alignment: center; }
|
2026-04-07 10:04:13 +00:00
|
|
|
DarkLineEdit {
|
2026-04-06 18:40:34 +00:00
|
|
|
horizontal-stretch: 1;
|
|
|
|
|
text <=> MacroBridge.new-name;
|
|
|
|
|
placeholder-text: "macro name";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Steps display
|
|
|
|
|
Text { text: "Steps:"; color: Theme.fg-secondary; font-size: 12px; }
|
|
|
|
|
|
|
|
|
|
// Show added steps as tags
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
spacing: 4px;
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
|
|
|
|
for step in MacroBridge.new-steps : Rectangle {
|
|
|
|
|
width: self.preferred-width + 12px;
|
|
|
|
|
preferred-width: step-text.preferred-width;
|
|
|
|
|
height: 26px;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
background: step.is-delay ? Theme.accent-orange : Theme.accent-purple;
|
|
|
|
|
|
|
|
|
|
step-text := Text {
|
|
|
|
|
text: step.label;
|
|
|
|
|
color: Theme.fg-primary;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
horizontal-alignment: center;
|
|
|
|
|
vertical-alignment: center;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if MacroBridge.new-steps.length == 0 : Text {
|
|
|
|
|
text: "(empty - add keys and delays below)";
|
|
|
|
|
color: Theme.fg-secondary;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
vertical-alignment: center;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Action buttons
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
spacing: 6px;
|
|
|
|
|
alignment: start;
|
|
|
|
|
|
2026-04-06 18:52:44 +00:00
|
|
|
DarkButton {
|
2026-04-06 18:40:34 +00:00
|
|
|
text: "Add Key";
|
|
|
|
|
clicked => {
|
|
|
|
|
KeymapBridge.selector-target = "macro-step";
|
|
|
|
|
KeymapBridge.key-selector-open = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 18:52:44 +00:00
|
|
|
DarkButton { text: "T 50ms"; clicked => { MacroBridge.add-delay-step(50); } }
|
|
|
|
|
DarkButton { text: "T 100ms"; clicked => { MacroBridge.add-delay-step(100); } }
|
|
|
|
|
DarkButton { text: "T 200ms"; clicked => { MacroBridge.add-delay-step(200); } }
|
2026-04-06 18:40:34 +00:00
|
|
|
|
|
|
|
|
Rectangle { horizontal-stretch: 1; }
|
|
|
|
|
|
2026-04-06 18:52:44 +00:00
|
|
|
DarkButton { text: "Undo"; clicked => { MacroBridge.remove-last-step(); } }
|
|
|
|
|
DarkButton { text: "Clear"; clicked => { MacroBridge.clear-steps(); } }
|
2026-04-06 18:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
refactor: Modularize main.rs (2478 → 58 lines), flat architecture
Split monolithic main.rs into domain modules:
- context.rs: AppContext shared state, BgMsg enum, serial_spawn helper
- models.rs: UI model builders, keycap labels, key entries, layout preview
- config.rs: keyboard config export/import via binary protocol
- dispatch.rs: BgMsg handler + WPM/layout timers
- keymap.rs: key selection, layer switch/rename, heatmap toggle
- key_selector.rs: dispatch_keycode router, apply_keycode, hex/MT/LT
- macros.rs: macro CRUD, shortcut presets, step builder
- advanced.rs: combos, KO, leaders, tap dance, BT, tama, autoshift
- settings.rs: OTA flash, config backup, keyboard layout
- flasher.rs: ESP32 bootloader flash
- layout.rs: layout JSON preview, load/export
- connection.rs: serial connect/disconnect, tab auto-refresh
- stats.rs: stats refresh
Rename logic/ → protocol/ with cleaner file names.
Remove unused original-src/ directory.
Fix DarkLineEdit double styling, add rename popup, macro shortcuts.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 10:02:22 +00:00
|
|
|
// Common shortcuts
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
spacing: 6px;
|
|
|
|
|
alignment: start;
|
|
|
|
|
|
|
|
|
|
Text { text: "Shortcuts:"; color: Theme.fg-secondary; font-size: 11px; vertical-alignment: center; }
|
|
|
|
|
|
|
|
|
|
DarkButton { text: "Ctrl+C"; clicked => { MacroBridge.add-shortcut("ctrl+c"); } }
|
|
|
|
|
DarkButton { text: "Ctrl+V"; clicked => { MacroBridge.add-shortcut("ctrl+v"); } }
|
|
|
|
|
DarkButton { text: "Ctrl+X"; clicked => { MacroBridge.add-shortcut("ctrl+x"); } }
|
|
|
|
|
DarkButton { text: "Ctrl+Z"; clicked => { MacroBridge.add-shortcut("ctrl+z"); } }
|
|
|
|
|
DarkButton { text: "Ctrl+Y"; clicked => { MacroBridge.add-shortcut("ctrl+y"); } }
|
|
|
|
|
DarkButton { text: "Ctrl+S"; clicked => { MacroBridge.add-shortcut("ctrl+s"); } }
|
|
|
|
|
DarkButton { text: "Ctrl+A"; clicked => { MacroBridge.add-shortcut("ctrl+a"); } }
|
|
|
|
|
DarkButton { text: "Alt+F4"; clicked => { MacroBridge.add-shortcut("alt+f4"); } }
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-06 18:40:34 +00:00
|
|
|
// Save
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
spacing: 8px;
|
|
|
|
|
alignment: start;
|
|
|
|
|
|
2026-04-06 18:52:44 +00:00
|
|
|
DarkButton {
|
2026-04-06 18:40:34 +00:00
|
|
|
text: "Save Macro";
|
|
|
|
|
enabled: AppState.connection == ConnectionState.connected;
|
|
|
|
|
clicked => { MacroBridge.save-macro(); }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if MacroBridge.new-steps-text != "" : Text {
|
|
|
|
|
text: MacroBridge.new-steps-text;
|
|
|
|
|
color: Theme.fg-secondary;
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
vertical-alignment: center;
|
|
|
|
|
overflow: elide;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Macro list
|
|
|
|
|
ScrollView {
|
|
|
|
|
vertical-stretch: 1;
|
|
|
|
|
|
|
|
|
|
VerticalLayout {
|
|
|
|
|
spacing: 4px;
|
|
|
|
|
|
|
|
|
|
if MacroBridge.macros.length == 0 : Text {
|
|
|
|
|
text: "No macros configured";
|
|
|
|
|
color: Theme.fg-secondary;
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for macro in MacroBridge.macros : Rectangle {
|
|
|
|
|
background: Theme.bg-secondary;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
|
|
|
|
|
HorizontalLayout {
|
|
|
|
|
padding-left: 8px;
|
|
|
|
|
padding-right: 8px;
|
|
|
|
|
spacing: 8px;
|
|
|
|
|
|
|
|
|
|
Text { text: "#" + macro.slot; color: Theme.accent-purple; font-size: 12px; vertical-alignment: center; width: 30px; }
|
|
|
|
|
Text { text: macro.name; color: Theme.fg-primary; font-size: 12px; font-weight: 600; vertical-alignment: center; width: 100px; }
|
|
|
|
|
Text { text: macro.steps; color: Theme.fg-secondary; font-size: 10px; vertical-alignment: center; horizontal-stretch: 1; overflow: elide; }
|
2026-04-06 18:52:44 +00:00
|
|
|
DarkButton { text: "Del"; clicked => { MacroBridge.delete-macro(macro.slot); } }
|
2026-04-06 18:40:34 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|