Refresh stale depth bounds after geometry edits, allow orbiting through camera poles, and preserve hidden-line occlusion for selected solids.\n\nFixes #957
Model space appearance now follows the active UI theme instead of a
hardcoded dark background. Canvas mode is user-selectable (Match Theme /
Classic CAD Dark / Custom RGB), with matching controls for the paper
sheet and desk surround backgrounds and grid opacity.
Implements industry-standard CAD system variables, all settable via
command name, SETVAR, and the Options dialog:
- COLORTHEME (0/1 dark/light UI scheme, preserves custom palettes)
- COLORSCHEME (interactive named-theme selection)
- BACKGROUND (THEME/CLASSIC/DEFAULT/DESK presets, RGB triplets)
- SELECTIONAREA, SELECTIONAREAOPACITY (translucent marquee fill)
- WINDOWSAREACOLOR/WINDOWAREACOLOR, CROSSINGAREACOLOR (ACI or theme)
- SELECTIONEFFECT, SELECTIONEFFECTCOLOR (GPU highlight pass, driven by
selection_generation invalidation — no retessellation)
- SELECTIONPREVIEW (bit 0 = idle rollover, bit 1 = in-command hover)
- GRIPSIZE, GRIPCOLOR, GRIPHOT, GRIPHOVER (grip overlay styling)
parse_power accepted a single exponent (`unary ('^' unary)?`), so a
chained power stopped after the first `^`. The remaining `^2` was left
unconsumed, and eval_to_string's embedded-expression scan then handed it
back as literal text: typing `2^3^2` into a coordinate or distance field
produced the string "8^2" rather than 512, and `2^3^2+1" produced "8^3".
Recurse into parse_power for the exponent so `2^3^2` reads as
`2^(3^2)", the standard right-associative meaning, and the whole
expression is consumed.
Scene.selection was Rc<RefCell<SelectionState>> (31 fields, ~1.6KB
with poly_points) cloned deep per view_main per pane via
(*sel_ref).clone() at view/mod.rs:760. Replace with
Arc<RefCell<SelectionState>> (Arc::new(RefCell::new(...)) at
scene/mod.rs:1436,1868), SelectionCanvas holds Arc<RefCell> and
selection_overlay takes Arc<RefCell> (overlay.rs:365,415),
view_main does Arc::clone(&tab.scene.selection) (atomic bump)
instead of deep clone. sel_ref stays borrowed for field reads
(vp_size etc.).
Bench (release, n=5000): SelectionState::clone 39-68ns (64pts) /
11-22ns (0pts) vs Arc::clone 7ns — 5-9x on the hot path.
Tests:
- selection_is_arc_not_refcell: asserts type_name contains Arc
- bench_selection_state_clone / bench_selection_arc_clone / bench_selection_state_clone_empty (ignored, release)
Hold a single Ref<SelectionState> across the block; replace six redundant borrow().vp_size/last_move_pos calls with field reads on the Ref. Move the deep clone to the widget call site ((*sel_ref).clone()), preserving one clone per frame but ensuring the widget receives the same snapshot as the geometry reads.
- Implement `TABLEDIT` command supporting viewport point-picking on cell interiors
- Support overlapping table hit testing with selection cycling and top-table resolution
- Support `/n` and `\n` multiline paragraph breaks (`\P`) in table cell text editing
- Unify text input modes via `InputKind` (`Point`, `SingleToken`, `FreeText`) on `CadCommand`
- Migrate 15+ commands across annotate, draw, insert, layout, and view modules
- Preserve text casing, literal spaces, and bypass arithmetic eval during FreeText input
- Add dedicated `tabledit_tests` and `free_text_entry_tests` test suites
Replace manual key entry with per-row capture cells: clicking + Add
inserts a draft row at the top with its key cell armed, and the next
key combination is recorded and normalized (Ctrl+Shift+S →
CTRL+SHIFT+S). While armed, a dedicated keyboard listener swallows all
input so recorded shortcuts do not execute in the drawing.
Draft rows are transient and confirmed only through an explicit accept
button, so the table never retains empty rows. Duplicate keys and
unrunnable commands are flagged in danger styling with a persistent
warning naming the conflicting command. Added reset-to-defaults with
confirmation, apply-and-exit, a shortcut count, and an unsaved-changes
guard on close.