Add named parameters, UI scale setting, OPTIONS/OP dispatch, and help manual

Named parameters (docs/named_parameters_design.md, stages 3-4):
- ParameterTable (src/scene/named_parameters.rs): a document-wide table of
  named formulas, each resolved through a shared expression evaluator with
  cross-parameter references, cycle detection, and division-by-zero/parse
  error reporting. SketchConstraint::driving_param becomes DrivingValue
  (Literal | Named) instead of a bare f64, resolved at solve time by
  sketch_solve::build_constraint via Scene::named_parameters.
- Persistence (src/scene/named_parameters_persist.rs): same lazy XRecord
  model sketch_persist.rs already established, synced around open/save.
  Bumps the sketch-constraint XRecord's own format version (1 -> 2) since
  DrivingValue's shape doesn't line up with a version-1 blob's bytes.
- PARAMETERS command and its editor (src/ui/window/named_parameters.rs):
  add/remove/edit rows, live validation, duplicate-name detection: Apply
  defines the table and re-solves every constraint scope with a
  named-parameter-driven constraint (command_driver.rs). Distance/Angle
  constraint prompts (modules/draw/constrain/value.rs) now accept either a
  literal or an existing parameter's name, mirroring how the constraint's
  own value prompt already worked.
- Undo scope-down carried over from this project's own established pattern
  (constraint-system stage 9): the geometry an Apply moves is one undo
  step, but the parameter table edit itself has no undo entry yet — a
  documented follow-up, not a silent gap.

UI scale setting (src/app/settings.rs, app/mod.rs, ui/window/options.rs):
  a whole-interface scale percentage (50-200%), wired directly into iced's
  own per-window scale_factor hook rather than per-panel font-size
  plumbing, so it applies immediately to every panel, dialog, and the
  ribbon at once. Persisted alongside the existing lineweight-display-scale
  setting.

OPTIONS/OP command dispatch (app/commands/view.rs, app/commands/mod.rs):
  OPTIONS was registered for autocomplete but had no dispatch arm — the
  Start-tab welcome screen's own button was the only way to reach it.
  Found live-testing the UI-scale setting, unrelated to that feature
  itself. "OP" is AutoCAD's own short alias.

help/: a new self-contained static user-help manual (manual.md as the
authored source, tools/build_help.py splits it into the committed static
HTML pages plus search index) — not yet wired into the application.

64 new/updated tests across the named-parameters and constraint-prompt
changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Felix RiestraandClaude Sonnet 5 committed 2026-09-09 13:50:08 +02:00
1 parent e2d39b049d
commit 7cddf31bcb
47 files changed
+4136 -52

No files matched your search

+40
View File
@@ -504,6 +504,12 @@ pub(super) struct OpenCADStudio {
crosshair_color_input: String,
/// Model-space lineweight preview scale, in percent (25..=200).
lineweight_display_scale: i32,
/// Whole-interface scale, in percent (50..=200) — drives iced's own
/// per-window `scale_factor` (`run`'s `.scale_factor(...)` hook), so it
/// resizes every panel, dialog, the ribbon, and their text uniformly
/// rather than needing a separate font-size knob threaded through each
/// panel's own view code.
ui_scale: i32,
/// Isometric drafting state and active axis pair.
isometric_drafting: bool,
iso_plane: settings::IsoPlane,
@@ -1018,6 +1024,15 @@ pub(super) struct OpenCADStudio {
/// edited. Seeded from `command_aliases` on open, committed back on close.
alias_editor_rows: Vec<(String, String)>,
// ── Named Parameters (docs/named_parameters_design.md, stage 4) ────────
/// Working buffer for the PARAMETERS modal. Unlike `alias_editor_rows`,
/// this isn't a copy of a separate app-level store — the real state
/// lives per-document at `Scene::named_parameters`; this buffer is
/// seeded from the active tab's table on open and only written back to
/// it on Apply (`apply_named_parameter_editor_rows`,
/// `src/app/named_parameters.rs`).
named_parameter_editor_rows: Vec<crate::ui::window::named_parameters::ParamEditorRow>,
// ── Layout Manager Panel ──────────────────────────────────────────────
layout_manager_selected: String,
layout_manager_rename_buf: String,
@@ -1695,6 +1710,7 @@ pub enum ModalKind {
AttributeEditor,
LayerDeleteWarning,
Aliases,
NamedParameters,
ScaleManager,
/// Add / remove the annotation scales a single selected object has a
/// per-object representation for.
@@ -1937,6 +1953,8 @@ pub enum Message {
CursorTypeChanged(settings::CursorType),
/// Set the model-space lineweight preview scale from Options.
LineweightDisplayScaleChanged(i32),
/// Set the whole-interface scale (`ui_scale`) from Options.
UiScaleChanged(i32),
/// Set the ribbon's large-button icon-label font size from Options.
RibbonLabelFontSizeChanged(i32),
/// Set the ribbon's panel-title ("Draw", "Modify", …) font size from Options.
@@ -2706,6 +2724,24 @@ pub enum Message {
AliasEditorRemove(usize),
/// Commit the edited rows to the alias table (Apply button); stays open.
AliasEditorApply,
// ── Named Parameters (PARAMETERS) ───────────────────────────────────
/// Open the named-parameter editor, seeding rows from the active tab's
/// `Scene::named_parameters`.
NamedParametersOpen,
/// Live edit of the name or formula in row `idx`.
NamedParametersInput {
idx: usize,
field: crate::ui::window::named_parameters::ParamField,
value: String,
},
/// Append a blank parameter row.
NamedParametersAdd,
/// Remove parameter row `idx`.
NamedParametersRemove(usize),
/// Commit the edited rows to `Scene::named_parameters` (Apply button)
/// and re-solve every constraint that reads a named parameter; stays
/// open.
NamedParametersApply,
// ── About window ────────────────────────────────────────────────────
AboutOpen,
/// Close whatever in-canvas modal dialog is open (Plan B).
@@ -3378,6 +3414,7 @@ impl OpenCADStudio {
crosshair_color: None,
crosshair_color_input: String::new(),
lineweight_display_scale: 100,
ui_scale: 100,
isometric_drafting: false,
iso_plane: settings::IsoPlane::Left,
snap_angle_deg: 0.0,
@@ -3579,6 +3616,7 @@ impl OpenCADStudio {
// Command aliases (populated from ocad.pgp just after construction)
command_aliases: rustc_hash::FxHashMap::default(),
alias_editor_rows: Vec::new(),
named_parameter_editor_rows: Vec::new(),
// Layout Manager
layout_manager_selected: "Model".to_string(),
layer_state_selected: None,
@@ -3978,6 +4016,7 @@ pub fn run() -> iced::Result {
..iced::Settings::default()
})
.subscription(OpenCADStudio::subscription)
.scale_factor(|state: &OpenCADStudio, _window: window::Id| state.ui_scale as f32 / 100.0)
.title(|state: &OpenCADStudio, window_id: window::Id| {
let _ = window_id; // all dialogs are in-canvas modals now
if let Some(tab) = state.tabs.get(state.active_tab) {
@@ -4022,6 +4061,7 @@ pub fn run_web() -> iced::Result {
OpenCADStudio::view_main,
)
.subscription(OpenCADStudio::subscription)
.scale_factor(|state: &OpenCADStudio| state.ui_scale as f32 / 100.0)
.title(|_state: &OpenCADStudio| {
concat!("Open CAD Studio ", env!("OCS_APP_VERSION")).to_string()
})