From c9d9f9c2bb9c8f84af0201ee01556d7b028c0c33 Mon Sep 17 00:00:00 2001 From: yztink Date: Fri, 2 Oct 2026 21:01:02 -0700 Subject: [PATCH] feat(sysvar): add DYNMODE system variable and command for Dynamic Input --- src/app/commands/styleprops.rs | 67 +++++++++++++++++++++++++++++++++- src/app/drafting_settings.rs | 5 +++ src/app/mod.rs | 3 ++ src/app/settings.rs | 2 + src/app/update/file.rs | 2 + src/app/update/mod.rs | 5 +++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/src/app/commands/styleprops.rs b/src/app/commands/styleprops.rs index 5f9fee957..8051eed84 100644 --- a/src/app/commands/styleprops.rs +++ b/src/app/commands/styleprops.rs @@ -1086,6 +1086,7 @@ impl OpenCADStudio { | "CONSTRAINTNAMEFORMAT" | "DYNCONSTRAINTDISPLAY" | "CCONSTRAINTFORM" + | "DYNMODE" ) => { return self.dispatch_styleprops(&format!("SETVAR {cmd}"), i); @@ -1110,7 +1111,7 @@ impl OpenCADStudio { self.command_line.push_info(&super::plotvars::setvar_listing()); self.command_line.push_info(&super::blockvars::setvar_listing()); self.command_line.push_info( - crate::t!("SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR SHORTCUTMENU SHORTCUTMENUDURATION CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY CONSTRAINTBARMODE CONSTRAINTNAMEFORMAT DYNCONSTRAINTDISPLAY ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(), + crate::t!("SETVAR: CETRANSPARENCY LTSCALE CELTSCALE PDMODE PDSIZE TEXTSIZE ORTHOMODE FILLMODE MIRRTEXT FRAME IMAGEFRAME PDFFRAME WIPEOUTFRAME XCLIPFRAME POINTCLOUDCLIPFRAME ZOOMWHEEL ZOOMFACTOR SHORTCUTMENU SHORTCUTMENUDURATION CURSORSIZE PICKBOX CURSORTYPE SNAPANG TEXTFILL CLIPROMPTLINES COMMANDLINEFADETIME ATTREQ ATTDIA DIMASSOC DIMCONTINUEMODE CONSTRAINTSOLVEMODE CONSTRAINTINFER CONSTRAINTBARDISPLAY CONSTRAINTBARMODE CONSTRAINTNAMEFORMAT DYNCONSTRAINTDISPLAY DYNMODE ANGBASE ANGDIR SKETCHINC SKPOLY SKTOLERANCE DONUTID DONUTOD CENTEREXE CENTERLAYER CENTERLTYPE CENTERLTSCALE CENTERLTYPEFILE CENTERCROSSSIZE CENTERCROSSGAP CENTERMARKEXE COLORTHEME SELECTIONAREA SELECTIONAREAOPACITY SELECTIONEFFECT SELECTIONEFFECTCOLOR WINDOWSAREACOLOR CROSSINGAREACOLOR SELECTIONPREVIEW GRIPSIZE GRIPCOLOR GRIPHOT GRIPHOVER GRIPOBJLIMIT | CLAYER CELTYPE TEXTSTYLE (read-only)").as_ref(), ); } else { // Point cloud settings, kept with the drawing. @@ -1703,6 +1704,24 @@ impl OpenCADStudio { .ok_or_else(|| "SETVAR: 0 or 1 required.".into()), None => Ok((format!("MIRRTEXT = {}", h.mirror_text as i32), false)), }, + "DYNMODE" => match &value { + Some(v) => match v.parse::() { + Ok(mode) if (-3..=3).contains(&mode) => { + self.dyn_mode = mode; + self.dyn_input = mode > 0; + Ok((format!("DYNMODE = {mode}"), true)) + } + _ => Err("SETVAR: integer from -3 to 3 required.".into()), + }, + None => { + let mode = if self.dyn_input { + if self.dyn_mode > 0 { self.dyn_mode } else { 3 } + } else { + if self.dyn_mode <= 0 { self.dyn_mode } else { 0 } + }; + Ok((format!("DYNMODE = {mode}"), false)) + } + }, "ZOOMWHEEL" => match &value { Some(v) => match parse_bool(v) { Some(reversed) => { @@ -2685,6 +2704,7 @@ impl OpenCADStudio { // Without this it marked the drawing // modified instead of persisting. | "TEXTFILL" + | "DYNMODE" ) { self.persist_settings_if_changed(); } else { @@ -3782,6 +3802,51 @@ mod tests { let _ = app.run_command_line("SETVAR DELOBJ 4"); assert_eq!(app.delete_objects, 3); } + + #[test] + fn dynmode_direct_and_setvar_toggles_dynamic_input() { + let mut app = fresh_app(); + assert!(app.dyn_input); + assert_eq!(app.dyn_mode, 3); + + // Turn off via direct command: DYNMODE 0 + let _ = app.run_command_line("DYNMODE 0"); + assert!(!app.dyn_input); + assert_eq!(app.dyn_mode, 0); + + // Turn on via direct command: DYNMODE 3 + let _ = app.run_command_line("DYNMODE 3"); + assert!(app.dyn_input); + assert_eq!(app.dyn_mode, 3); + + // Turn off via SETVAR: SETVAR DYNMODE 0 + let _ = app.run_command_line("SETVAR DYNMODE 0"); + assert!(!app.dyn_input); + assert_eq!(app.dyn_mode, 0); + + // Turn on via SETVAR: SETVAR DYNMODE 1 (pointer input only) + let _ = app.run_command_line("SETVAR DYNMODE 1"); + assert!(app.dyn_input); + assert_eq!(app.dyn_mode, 1); + + // Out of range rejected + let _ = app.run_command_line("DYNMODE 5"); + assert_eq!(app.dyn_mode, 1); + assert!(app.dyn_input); + + let _ = app.run_command_line("DYNMODE -5"); + assert_eq!(app.dyn_mode, 1); + assert!(app.dyn_input); + + // F12 / Message::ToggleDynInput synchronization + let _ = app.update(crate::app::Message::ToggleDynInput); + assert!(!app.dyn_input); + assert_eq!(app.dyn_mode, 0); + + let _ = app.update(crate::app::Message::ToggleDynInput); + assert!(app.dyn_input); + assert_eq!(app.dyn_mode, 3); + } } #[cfg(test)] diff --git a/src/app/drafting_settings.rs b/src/app/drafting_settings.rs index 04f5753ed..2b8b7369f 100644 --- a/src/app/drafting_settings.rs +++ b/src/app/drafting_settings.rs @@ -101,6 +101,11 @@ impl OpenCADStudio { self.snapper.snap3d_enabled = state.osnap3d_on; self.snapper.enabled3d = state.snap3d_modes.clone(); self.dyn_input = state.dyn_input_on; + if self.dyn_input && self.dyn_mode <= 0 { + self.dyn_mode = 3; + } else if !self.dyn_input && self.dyn_mode > 0 { + self.dyn_mode = 0; + } self.quick_properties = state.quick_props_on; self.selection_cycling = state.selection_cycling_on; self.sync_vport_display(self.active_tab); diff --git a/src/app/mod.rs b/src/app/mod.rs index 392e1bed1..755735f17 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -615,6 +615,8 @@ pub(super) struct OpenCADStudio { pub grid_beyond_limits: bool, /// Dynamic input overlay (F12): show coordinate tooltip near cursor. dyn_input: bool, + /// Dynamic input mode (DYNMODE): 0=off, 1=pointer, 2=dimensional, 3=both. + dyn_mode: i16, /// Currently visible page in the application Options dialog. options_tab: crate::ui::window::options::OptionsTab, /// The Options window's commit point (see `options_session`). @@ -4136,6 +4138,7 @@ impl OpenCADStudio { grid_adaptive: true, grid_beyond_limits: true, dyn_input: true, + dyn_mode: 3, options_tab: crate::ui::window::options::OptionsTab::General, options_saved: None, options_close_confirm: false, diff --git a/src/app/settings.rs b/src/app/settings.rs index 690114e91..52907b2bf 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -383,6 +383,7 @@ pub const MAX_SELECTED_GRIPS: usize = 4096; pub struct UserSettings { pub spacemouse: crate::input::spacemouse::Preferences, pub dyn_input: bool, + pub dyn_mode: i16, pub polar: bool, pub polar_increment_deg: f32, pub zoom_wheel_reversed: bool, @@ -771,6 +772,7 @@ impl Default for UserSettings { Self { spacemouse: crate::input::spacemouse::Preferences::default(), dyn_input: true, + dyn_mode: 3, polar: false, polar_increment_deg: 45.0, zoom_wheel_reversed: false, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index ca2945cc5..1aaa47170 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -659,6 +659,7 @@ impl OpenCADStudio { crate::app::settings::UserSettings { spacemouse: self.spacemouse_preferences, dyn_input: self.dyn_input, + dyn_mode: self.dyn_mode, polar: self.polar_mode, polar_increment_deg: self.polar_increment_deg, zoom_wheel_reversed: self.zoom_wheel_reversed, @@ -750,6 +751,7 @@ impl OpenCADStudio { /// Apply restored preferences to live state. pub(in crate::app) fn apply_settings(&mut self, s: &crate::app::settings::UserSettings) { self.dyn_input = s.dyn_input; + self.dyn_mode = if s.dyn_mode != 0 { s.dyn_mode } else if s.dyn_input { 3 } else { 0 }; self.polar_mode = s.polar; self.polar_increment_deg = s.polar_increment_deg; self.zoom_wheel_reversed = s.zoom_wheel_reversed; diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index fdcac3bea..c6680d3ad 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -4197,6 +4197,11 @@ impl OpenCADStudio { } Message::ToggleDynInput => { self.dyn_input ^= true; + if self.dyn_input && self.dyn_mode <= 0 { + self.dyn_mode = 3; + } else if !self.dyn_input && self.dyn_mode > 0 { + self.dyn_mode = 0; + } Task::none() } Message::ToggleViewCube => {