feat: Editable Tap Dance + combo creation validation
Tap Dance: - Each TD action is now a clickable button that opens the key selector - Clicking an action -> pick key from popup -> sends TD_SET binary to firmware - Labels show 1-tap / 2-tap / 3-tap / hold columns Combo creation: - Added validation: "Pick both keys first" message if keys not selected - Debug log for COMBOSET command Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d69d931ea9
commit
4cfe2fbb7b
3 changed files with 82 additions and 4 deletions
48
src/main.rs
48
src/main.rs
|
|
@ -672,6 +672,7 @@ fn main() {
|
|||
let dispatch_keycode = {
|
||||
let apply_keycode = apply_keycode.clone();
|
||||
let keys_arc = keys_arc.clone();
|
||||
let serial = serial.clone();
|
||||
let macro_steps = macro_steps.clone();
|
||||
let refresh_macro_display = refresh_macro_display.clone();
|
||||
let window_weak = window.as_weak();
|
||||
|
|
@ -738,6 +739,41 @@ fn main() {
|
|||
}
|
||||
if count < 4 { adv.set_new_leader_seq_count(count + 1); }
|
||||
}
|
||||
"td-action" => {
|
||||
let adv = w.global::<AdvancedBridge>();
|
||||
let td_idx = adv.get_editing_td_index();
|
||||
let slot = adv.get_editing_td_slot() as usize;
|
||||
if td_idx >= 0 && slot < 4 {
|
||||
// Update model in place
|
||||
let tds = adv.get_tap_dances();
|
||||
for i in 0..tds.row_count() {
|
||||
let td = tds.row_data(i).unwrap();
|
||||
if td.index == td_idx {
|
||||
let actions = td.actions;
|
||||
let mut a = actions.row_data(slot).unwrap();
|
||||
a.name = name.clone();
|
||||
a.code = code as i32;
|
||||
actions.set_row_data(slot, a);
|
||||
|
||||
// Collect all 4 action codes and send to firmware
|
||||
let mut codes = [0u16; 4];
|
||||
for j in 0..4.min(actions.row_count()) {
|
||||
codes[j] = actions.row_data(j).unwrap().code as u16;
|
||||
}
|
||||
let payload = logic::binary_protocol::td_set_payload(td_idx as u8, &codes);
|
||||
let serial = serial.clone();
|
||||
std::thread::spawn(move || {
|
||||
let mut ser = serial.lock().unwrap_or_else(|e| e.into_inner());
|
||||
let _ = ser.send_binary(logic::binary_protocol::cmd::TD_SET, &payload);
|
||||
});
|
||||
w.global::<AppState>().set_status_text(
|
||||
SharedString::from(format!("TD{} slot {} = {}", td_idx, slot, name))
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"macro-step" => {
|
||||
// Add key press (Down + Up) to macro steps
|
||||
let mut steps = macro_steps.borrow_mut();
|
||||
|
|
@ -1018,7 +1054,14 @@ fn main() {
|
|||
let r2 = adv.get_new_combo_r2() as u8;
|
||||
let c2 = adv.get_new_combo_c2() as u8;
|
||||
let result = adv.get_new_combo_result_code() as u8;
|
||||
let key1_name = adv.get_new_combo_key1_name();
|
||||
let key2_name = adv.get_new_combo_key2_name();
|
||||
if key1_name == "Pick..." || key2_name == "Pick..." {
|
||||
w.global::<AppState>().set_status_text("Pick both keys first".into());
|
||||
return;
|
||||
}
|
||||
let cmd = logic::protocol::cmd_comboset(255, r1, c1, r2, c2, result);
|
||||
eprintln!("COMBOSET: {}", cmd);
|
||||
let serial = serial.clone();
|
||||
let tx = tx.clone();
|
||||
std::thread::spawn(move || {
|
||||
|
|
@ -1481,7 +1524,10 @@ fn main() {
|
|||
.map(|(i, actions)| TapDanceData {
|
||||
index: i as i32,
|
||||
actions: ModelRc::from(Rc::new(VecModel::from(
|
||||
actions.iter().map(|&a| SharedString::from(keycode::decode_keycode(a))).collect::<Vec<_>>()
|
||||
actions.iter().map(|&a| TapDanceAction {
|
||||
name: SharedString::from(keycode::decode_keycode(a)),
|
||||
code: a as i32,
|
||||
}).collect::<Vec<_>>()
|
||||
))),
|
||||
})
|
||||
.collect();
|
||||
|
|
|
|||
|
|
@ -121,9 +121,14 @@ export global StatsBridge {
|
|||
|
||||
// ---- Advanced ----
|
||||
|
||||
export struct TapDanceAction {
|
||||
name: string,
|
||||
code: int,
|
||||
}
|
||||
|
||||
export struct TapDanceData {
|
||||
index: int,
|
||||
actions: [string],
|
||||
actions: [TapDanceAction],
|
||||
}
|
||||
|
||||
export struct ComboData {
|
||||
|
|
@ -183,6 +188,11 @@ export global AdvancedBridge {
|
|||
in-out property <int> new-leader-result-code: 0;
|
||||
in-out property <string> new-leader-result-name: "Pick...";
|
||||
in-out property <int> new-leader-mod-idx: 0;
|
||||
// TD editing: stores which TD slot+action is being edited
|
||||
in-out property <int> editing-td-index: -1;
|
||||
in-out property <int> editing-td-slot: -1; // 0-3 = which action
|
||||
callback save-td(int, int, int, int, int); // index, a1, a2, a3, a4 (keycodes)
|
||||
callback edit-td-action(int, int); // td_index, action_slot -> opens popup
|
||||
callback refresh-advanced();
|
||||
callback delete-combo(int);
|
||||
callback delete-leader(int);
|
||||
|
|
|
|||
|
|
@ -88,14 +88,36 @@ export component TabAdvanced inherits Rectangle {
|
|||
spacing: 4px;
|
||||
SectionHeader { text: "Tap Dance"; }
|
||||
if AdvancedBridge.tap-dances.length == 0 : Text { text: "No tap dances"; color: Theme.fg-secondary; font-size: 11px; }
|
||||
|
||||
Text { text: "1-tap 2-tap 3-tap hold"; color: Theme.fg-secondary; font-size: 10px; }
|
||||
|
||||
for td in AdvancedBridge.tap-dances : Rectangle {
|
||||
background: Theme.bg-primary;
|
||||
border-radius: 4px;
|
||||
height: 30px;
|
||||
HorizontalLayout {
|
||||
padding-left: 8px; padding-right: 8px; spacing: 8px;
|
||||
padding-left: 8px; padding-right: 8px; spacing: 4px;
|
||||
Text { text: "TD" + td.index; color: Theme.accent-purple; font-size: 11px; vertical-alignment: center; width: 35px; }
|
||||
for action in td.actions : Text { text: action; color: Theme.fg-primary; font-size: 11px; vertical-alignment: center; }
|
||||
for action[a-idx] in td.actions : Rectangle {
|
||||
width: 60px;
|
||||
height: 24px;
|
||||
border-radius: 3px;
|
||||
background: td-action-ta.has-hover ? Theme.button-hover : Theme.button-bg;
|
||||
border-width: 1px;
|
||||
border-color: Theme.button-border;
|
||||
|
||||
Text { text: action.name; color: Theme.fg-primary; font-size: 10px; horizontal-alignment: center; vertical-alignment: center; }
|
||||
|
||||
td-action-ta := TouchArea {
|
||||
clicked => {
|
||||
AdvancedBridge.editing-td-index = td.index;
|
||||
AdvancedBridge.editing-td-slot = a-idx;
|
||||
KeymapBridge.selector-target = "td-action";
|
||||
KeymapBridge.key-selector-open = true;
|
||||
}
|
||||
mouse-cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue