fix(editor): wire preset save-as-name typing on the web host
Follow-up to the tool-switch gating: web's input_active() now treats the variables preset save-as-name input as keyboard owner, but apply_text/backspace/ send/escape had no branch for it, so typed keys went nowhere (codex stop-review). Wire the keyboard path on the shared property_input_draft buffer the ThemePresetMenu widget paints — type/backspace (end-caret), Enter saves via save_theme_preset, Escape cancels — mirroring native. 3 tests. Note: the preset menu's inner press dispatch (click to focus the name input / Save Current / Load / Import) is still unwired on web — a separate pre-existing feature gap; this commit makes the name input functional once focused.
This commit is contained in:
parent
4a01f0950b
commit
fb8ccd33f1
|
|
@ -72,6 +72,22 @@ impl WidgetHost {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
// #20: the variables preset dropdown's save-as-name input. Types into
|
||||
// the shared `property_input_draft` the ThemePresetMenu widget paints
|
||||
// (native parity, `keyboard.rs::apply_text`). Web keeps the caret at the
|
||||
// end of the draft (no mid-string editing wired for this field).
|
||||
if self.editor_state.editor_ui.preset_name_input_active() && !c.is_control() {
|
||||
let ui = &mut self.editor_state.ui;
|
||||
if ui.property_draft_select_all {
|
||||
ui.property_input_draft.clear();
|
||||
ui.property_draft_select_all = false;
|
||||
}
|
||||
ui.property_input_draft.push(c);
|
||||
ui.property_caret_pos = ui.property_input_draft.len();
|
||||
ui.property_caret_anchor_ms = self.now_ms;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
// Variables-panel row/cell drafts — per-kind char gates
|
||||
// mirror the native host (numeric / free text / hex).
|
||||
if let Some(focus) = self.editor_state.editor_ui.variable_row_focus {
|
||||
|
|
@ -241,6 +257,26 @@ impl WidgetHost {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
// #20: preset save-as-name input — pop the last char (or clear a
|
||||
// select-all draft), native parity.
|
||||
if self.editor_state.editor_ui.preset_name_input_active() {
|
||||
let ui = &mut self.editor_state.ui;
|
||||
if ui.property_draft_select_all {
|
||||
ui.property_input_draft.clear();
|
||||
ui.property_caret_pos = 0;
|
||||
ui.property_draft_select_all = false;
|
||||
ui.property_caret_anchor_ms = self.now_ms;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
if ui.property_input_draft.pop().is_some() {
|
||||
ui.property_caret_pos = ui.property_input_draft.len();
|
||||
ui.property_caret_anchor_ms = self.now_ms;
|
||||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if self
|
||||
.editor_state
|
||||
.editor_ui
|
||||
|
|
@ -389,6 +425,11 @@ impl WidgetHost {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
// #20: Enter in the preset save-as-name input saves the preset
|
||||
// (native parity, `variable-theme-manager.tsx:298`).
|
||||
if self.commit_variables_preset_name_if_any() {
|
||||
return true;
|
||||
}
|
||||
// Enter in the variables search box just blurs it (the
|
||||
// filter is already live).
|
||||
if self.variables_search_active() {
|
||||
|
|
@ -604,6 +645,11 @@ impl WidgetHost {
|
|||
self.mark_dirty();
|
||||
return true;
|
||||
}
|
||||
// #20: Escape closes just the preset save-as-name input; the preset
|
||||
// dropdown stays open (native parity, `variable-theme-manager.tsx:299`).
|
||||
if self.escape_variables_preset_name() {
|
||||
return true;
|
||||
}
|
||||
// Modal overlays close one per press (mirrors native order:
|
||||
// export dialog → figma import → file menu).
|
||||
if self.editor_state.editor_ui.export_dialog_open {
|
||||
|
|
|
|||
|
|
@ -55,6 +55,40 @@ impl WidgetHost {
|
|||
true
|
||||
}
|
||||
|
||||
/// Enter in the preset save-as-name input — saves the current theme as a
|
||||
/// named preset, clears the draft, and defocuses (the dropdown stays open).
|
||||
/// Native parity (`variables_preset_press.rs::commit_variables_preset_name_if_any`).
|
||||
/// Blank names keep the input open. Returns whether the input was active.
|
||||
pub(in crate::widget_host) fn commit_variables_preset_name_if_any(&mut self) -> bool {
|
||||
if !self.editor_state.editor_ui.preset_name_input_active() {
|
||||
return false;
|
||||
}
|
||||
let name = self.editor_state.ui.property_input_draft.clone();
|
||||
if name.trim().is_empty() {
|
||||
return true;
|
||||
}
|
||||
let now_ms = self.now_ms;
|
||||
let _ = self.editor_state.save_theme_preset(&name, now_ms);
|
||||
self.editor_state.editor_ui.variables_preset_name_focus = false;
|
||||
self.editor_state.ui.property_input_draft.clear();
|
||||
self.editor_state.ui.property_caret_pos = 0;
|
||||
self.mark_dirty();
|
||||
true
|
||||
}
|
||||
|
||||
/// Escape in the preset save-as-name input — closes just the input, leaving
|
||||
/// the preset dropdown open. Native parity.
|
||||
pub(in crate::widget_host) fn escape_variables_preset_name(&mut self) -> bool {
|
||||
if !self.editor_state.editor_ui.preset_name_input_active() {
|
||||
return false;
|
||||
}
|
||||
self.editor_state.editor_ui.variables_preset_name_focus = false;
|
||||
self.editor_state.ui.property_input_draft.clear();
|
||||
self.editor_state.ui.property_caret_pos = 0;
|
||||
self.mark_dirty();
|
||||
true
|
||||
}
|
||||
|
||||
/// Cmd/Ctrl+D — duplicate the selected node as a sibling
|
||||
/// offset by ~10 doc px. Selection follows the clone.
|
||||
pub fn apply_duplicate(&mut self) -> bool {
|
||||
|
|
|
|||
|
|
@ -501,3 +501,41 @@ fn tool_shortcut_does_not_steal_keys_from_preset_name_input() {
|
|||
assert!(!host.apply_tool_shortcut("r"));
|
||||
assert_eq!(host.editor_state.tool, Tool::Select);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preset_name_input_receives_typed_keys_backspace_and_commit() {
|
||||
let mut host = WidgetHost::new();
|
||||
host.editor_state.editor_ui.variables_preset_menu_open = true;
|
||||
host.editor_state.editor_ui.variables_preset_name_focus = true;
|
||||
host.editor_state.ui.property_input_draft.clear();
|
||||
assert!(host.editor_state.editor_ui.preset_name_input_active());
|
||||
|
||||
// Typed keys land in the shared draft the preset menu widget paints.
|
||||
assert!(host.apply_text('M'));
|
||||
assert!(host.apply_text('i'));
|
||||
assert!(host.apply_text('d'));
|
||||
assert_eq!(host.editor_state.ui.property_input_draft, "Mid");
|
||||
|
||||
// Backspace pops the last char.
|
||||
assert!(host.apply_backspace());
|
||||
assert_eq!(host.editor_state.ui.property_input_draft, "Mi");
|
||||
|
||||
// Enter commits the preset and defocuses the name input.
|
||||
assert!(host.apply_send());
|
||||
assert!(!host.editor_state.editor_ui.variables_preset_name_focus);
|
||||
assert!(host.editor_state.ui.property_input_draft.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preset_name_input_escape_cancels_without_saving() {
|
||||
let mut host = WidgetHost::new();
|
||||
host.editor_state.editor_ui.variables_preset_menu_open = true;
|
||||
host.editor_state.editor_ui.variables_preset_name_focus = true;
|
||||
host.editor_state.ui.property_input_draft = "Draft".to_string();
|
||||
|
||||
// Escape closes just the name input; the dropdown stays open.
|
||||
assert!(host.apply_escape());
|
||||
assert!(!host.editor_state.editor_ui.variables_preset_name_focus);
|
||||
assert!(host.editor_state.editor_ui.variables_preset_menu_open);
|
||||
assert!(host.editor_state.ui.property_input_draft.is_empty());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue