feat(dynamic-input): per-step DynSpec framework + guide overhaul

Introduce a per-command, per-step dynamic-input description so each step
shows the value boxes and guide geometry that make sense for it, instead
of a one-size-fits-all distance/angle pair.

- command: add DynSpec{anchor, fields(role+label), guide}, DynRole
  (X/Y/Z/Distance/Angle/Radius/Diameter/Width/Height/Factor/Count with
  label + value scaling), DynGuide(None/Polar/AxisDelta/Radius/RectSides)
  and DynAnchor. New dyn_spec() trait method defaults to None so commands
  on the legacy dyn_field() path are unchanged.
- overlay: redraw the dynamic-input layer by guide — polar reference line
  + angle arc, single radius line, dotted axis legs, or a cursor row —
  and place each box by its role. Angle reads as the unsigned magnitude
  of the short arc; a typed polar angle takes the sign of the cursor side.
- app: route fields/guide/anchor through the tab; resolve points from the
  step anchor and de-scale typed values by role (typed diameter -> radius).
- circle: CR shows a radius value on one line to the cursor; CD shows the
  diameter (twice the cursor radius). No angle arc on radius steps.
- pline/mline: keep polar dynamic input on vertex steps that also accept
  keyword letters via point_step_accepts_keywords().

Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 4.8 committed 2026-06-15 17:12:08 +03:00
1 parent 18f1688486
commit b0ee1c39bd
8 files changed
+613 -150

No files matched your search

+29
View File
@@ -189,6 +189,21 @@ impl CadCommand for CircleCommand {
StepCR::Radius(_) => DynField::Distance,
}
}
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec};
match self.step {
// Center is a normal first-point pick.
StepCR::Center => None,
// Radius: a single R value with one dotted line from the centre to
// the cursor (no angle, no axis legs).
StepCR::Radius(c) => Some(DynSpec {
anchor: DynAnchor::Point(c),
fields: vec![DynFieldSpec::new(DynRole::Radius)],
guide: DynGuide::Radius,
}),
}
}
}
// ── Command: Center, Diameter ──────────────────────────────────────────────
@@ -262,6 +277,20 @@ impl CadCommand for CircleCDCommand {
}
None
}
fn dyn_spec(&self) -> Option<crate::command::DynSpec> {
use crate::command::{DynAnchor, DynFieldSpec, DynGuide, DynRole, DynSpec};
match self.step {
StepCR::Center => None,
// Diameter: the box shows/accepts twice the cursor radius; resolved
// back to a radius point by the host (role scaling).
StepCR::Radius(c) => Some(DynSpec {
anchor: DynAnchor::Point(c),
fields: vec![DynFieldSpec::new(DynRole::Diameter)],
guide: DynGuide::Radius,
}),
}
}
}
// ── Command: 2-Point ──────────────────────────────────────────────────────
+7
View File
@@ -63,6 +63,13 @@ impl CadCommand for MlineCommand {
self.waiting_scale || !self.points.is_empty()
}
fn point_step_accepts_keywords(&self) -> bool {
// The vertex steps accept J / S keywords but are point picks, so keep
// polar dynamic input. The scale prompt (`waiting_scale`) is genuine
// text entry and is excluded.
!self.waiting_scale && !self.points.is_empty()
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
// Waiting for scale value
if self.waiting_scale {
+6
View File
@@ -253,6 +253,12 @@ impl CadCommand for PlineCommand {
!self.vertices.is_empty()
}
fn point_step_accepts_keywords(&self) -> bool {
// Each segment is a point pick that also accepts A / L / C / U, so the
// polar dynamic-input distance/angle stays visible.
!self.vertices.is_empty()
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
match text.trim().to_uppercase().as_str() {
"A" | "ARC" => {