Replace Wrapping::WordOrGlyph with Wrapping::Word to prevent labels
like 'Dimensions' from breaking as 'Dimensio' + 'ns'. Word-only wrapping
ensures labels only wrap at word boundaries.
Fixes#1046
Co-authored-by: Copilot <[email protected]>
Replace the 8 owned clones in StyleContext construction (4 Vec<String>
+ 4 String) at ribbon/mod.rs:568 and :1059 with borrowed refs.
StyleContext is now StyleContext<'a> with &'a [String]/&'a str
(widgets.rs:313), RenderCtx holds &'a StyleContext<'a> (:633),
and call sites pass &self.x instead of .clone(). The view-local
borrow is valid for the duration of Ribbon::view() and the style
dropdown overlay.
TDD: RED style_context_tests failed E0308 (owned vs borrowed),
GREEN added lifetime and updated 4 type sites + 2 construction sites.
Style selectors (Standard, layer, Color/Linetype/Lineweight) sat at
different heights across ribbon panels because each combo panel
vertically centered its content: panels with fewer icon rows
(Multiline Text, Table) centered their dropdown differently than
panels with two rows (Dimensions, Multileader).
- Pin all combo panels (Layers, Properties, style combos) to the top
of the fixed 3-row panel with a shared COMBO_PANEL_PAD, so every
primary dropdown shares one baseline regardless of icon-row count
- Extract the duplicated tool-row and panel-container code into
tool_row() / combo_panel_col() helpers + named spacing constants
- Bundle the per-view render state into RenderCtx, shrinking
render_large/render_large_dropdown from 12-14 positional args to
(item, &ctx)
- print_to_printer: silence the Windows-only dead-code warning on
PrintOptions::quality with the platform split documented (the field
is read by the CUPS `lp -o print-quality` path)
The Insert tab's Block Palette button now behaves like the View tab toggles
(UCS Icon, View Cube, Ortho, Properties, File Tabs, Layout Tabs): it stays
highlighted while the block palette panel is open and clears when closed.
The Ribbon gains a show_block_palette flag with a set_block_palette setter,
fed into ToggleState and mapped in is_active_tool for the BLOCKPALETTE tool
id. show_block_palette on the app remains the single source of truth; both
mutation paths now mirror it onto the ribbon:
- BLOCKPALETTE / BLOCKSPALETTE command toggles ribbon state alongside the
panel (src/app/commands/blocks.rs).
- Closing the panel via the dock rail close button clears the ribbon state,
matching how Close(Properties) already syncs (src/app/update/dialog.rs).
Removing the binary wireframe event left add-ons with no way to set a
visual style except by spelling out a command string. Give them the
intent back, in the vocabulary everything else now speaks.
The name is carried as text rather than the render-mode type: this crate
is dependency-free by default and only pulls acadrust in behind the host
feature, which is not a weight to add to the manifest contract for one
enum. The host resolves it against the same list its ribbon, its picker
and its commands read, so an add-on can offer exactly the styles the
application does and no others. An unknown name says so and lists the
ones that exist, from that same list.
Co-Authored-By: Claude Opus 5 <[email protected]>
The ribbon offered four styles and dispatched each one's id as a command.
Two of those ids were not visual-style commands: "Shaded" carried SOLID,
which draws a 2D filled polygon, so choosing it started a draw command;
"Hidden" carried HIDDEN, which matched nothing at all, so it did nothing.
The intent had been for the button to fire its tool's event, but the
dropdown never reads one -- it dispatches the id.
Behind that sat two generations of the same feature. The render-mode
picker offered the seven styles a viewport can actually be drawn in;
everything else -- the ribbon, VSCURRENT, SHADEMODE, VISUALSTYLES and a
handful of bare verbs -- went through a binary wireframe-or-shaded flag
that could only approximate them, reporting "Hidden (shown shaded)" and
"X-Ray (shown as wireframe)" when asked for something it had no way to
draw. Four descriptions of one choice, each drifting on its own.
There is one list now. Each style names itself once -- mode, label, icon,
and the command that applies it -- and the ribbon, the picker, the
VISUALSTYLES verb and the interactive prompt all read from it, down to
the line that lists the choices, so what is offered cannot disagree with
what works. Keywords are the render modes' own names, since there is one
set of styles left to name.
The binary path is gone rather than kept alongside: its message, its
module event, the tool definitions that produced it, the ribbon's
special-cased highlight arms, the bare style verbs and the older keyword
spellings. X-Ray goes with it -- no render mode draws one, and it was
already coming out as a plain wireframe. Three files that had been left
holding a single icon constant each fold into the list.
Note: ModuleEvent::SetWireframe leaves the plugin API with it.
Closes#621
Co-Authored-By: Claude Opus 5 <[email protected]>
Pin Iced 0.15 so dialogs can measure content before distributing the resolved frame across child panels. Keep the web plugin notice compact and non-resizable.\n\nCloses #582
Tool highlights used to stick: every click set active_tool, but only
interactive commands and a hand-maintained per-modal list ever cleared
it, so one-shots (Cleanup/AUDIT, view actions) and unlisted dialogs
(Customization/ALIASEDIT, CUI, Plugin Manager…) stayed blue forever.
Now the rule is mechanical: state toggles light from state only (the
click highlight drops immediately), interactive commands stay lit
while running, dialog openers stay lit until the modal closes (generic
clear replaces the per-modal list), and one-shots never keep it — the
dispatch site turns the highlight off when nothing stayed running.
On the Start tab, tools whose command the start gate refuses now render
dimmed (grey icon + label) across all button styles, dropdowns and the
quick-access strip; start_allowed() is the single shared authority.
Co-Authored-By: Claude Fable 5 <[email protected]>
The UCS Icon, Properties, File Tabs, and Layout Tabs buttons were highlighted unconditionally because their ids fell through to an active_tool equality check in is_active_tool, which activate_tool sets on every click and never clears. Each id is now mapped to its corresponding visibility flag (show_ucs_icon, show_properties, show_file_tabs, show_layout_tabs), and those flags are kept in sync from their toggle handlers so the highlight reflects live state. The per-toggle booleans previously threaded through the render path are consolidated into a single ToggleState struct read from one place, so introducing a further toggle no longer requires changing every render-function signature and call site.
NAVVCUBE is a command toggle, so it never set active_tool and is_active_tool never matched it. Add a show_viewcube flag to the ribbon (mirroring wireframe/ortho_mode) and special-case "NAVVCUBE" in is_active_tool to return the live show_viewcube state; ToggleViewCube now calls ribbon.set_viewcube.
Ribbon:
- A ▾ selector at the right of the tab row picks the tool-panel density —
Auto (size to window), Full, Compact, or Collapsed. The choice is persisted.
It hides itself when the tool row is already tight, giving the cramped tab
row its space back.
Start page:
- The Recent Documents card is restyled to match the Supporters card
(rounded corners, 20px padding, matching title + colours).
- Its scrollbar no longer sits on top of the row ✕ remove buttons (a gutter
is reserved).
- A footer control sets how many recent files to keep: [-] an editable count
[+] with the max shown. Typing applies on Enter; an over-max entry snaps to
the max. The limit is persisted and enforced on load and on every open.
- The "Support on Patreon" heart is now the SVG icon instead of a font glyph.
Config:
- Add a shared `config::config_dir()` helper; the recent-files and status-bar
stores now go through it instead of each re-deriving the path. New minus.svg
icon for the stepper.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Adaptive tool-panel row now keeps degrading past the collapsed state instead
of overflowing:
- Panels still shrink from the right one at a time (full -> compact ->
collapsed). Once every panel is a collapsed button and the row still
overflows, each button drops its large icon to a small one (tight) together,
then the buttons are pulled together up to 8px per gap (reclaiming edge
padding) before anything is clipped.
- Tool-area height now tracks the tallest shown panel, so the ribbon shrinks
as its panels collapse; the measured height is reported for dropdown
anchoring.
- Tabs squeeze their inter-tab gaps shut before wrapping to a new row
(WrapFlow min_spacing_x).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
- The Properties panel's Match button now shrinks to a small icon when the
panel is compacted (render_large gained a `compact` flag), so the panel
actually gets narrower and joins the from-the-right degradation instead
of jumping straight to collapsed.
- A collapsed Properties panel now shows its Match button as the
representative face: item_id recognises PropertiesGroup and
collapse_button renders its match_prop as a large tool.
- The undo/redo history controls are wrapped in PosReport, so their
dropdowns anchor directly below the button (and flip at the edge) like
every other dropdown, instead of falling back to the corner.
- Panel dividers are back: CollapsePanels draws a 1px line between adjacent
panels, skipped only between two collapsed panels.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
All ribbon dropdowns (tool dropdowns, undo/redo history, and the layer /
colour / linetype / lineweight / style combos) and all status-bar popups
(scale, units, OSNAP, isolate, filter, customization menu, layout list)
now open directly at their widget instead of at a layout-estimated
position, and flip to the other side when the panel would cross a window
edge.
Each dropdown/pill widget is wrapped in a transparent PosReport that
records its screen bounds into a thread-local map on draw. The overlays
read those bounds:
- Ribbon: Ribbon::dd_anchor(id, panel_w, win_w) returns
(align_right, h_pad, top); position_ribbon_dropdown places the panel
below the widget, flipping right-aligned near the right edge.
- Status bar: popup builders take (pill_bounds, win_size) and call
popup::position_statusbar_popup / popup_anchor, opening just above the
pill and flipping left/right at the window edge.
The window size is now tracked (win_size, set on WindowResized). The old
layout estimators compute_dropdown_left / compute_layer_combo_left /
compute_prop_combo_left / compute_history_dropdown_left and
osnap_popup_right_offset are deleted.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Third stage of the adaptive tool area (shrink -> compact -> collapse).
When even the compact panels don't fit, DensitySwap picks a new
CollapsePanels widget that keeps a left prefix of panels inline and
collapses the rest to buttons; clicking a button opens the full panel as
an overlay flyout anchored below it (closes on outside press, mirroring
color_select's Floating).
A collapsed panel's button shows a live representative tool — the panel's
last-used tool (tracked per panel in Ribbon::note_panel_tool on every
tool click), defaulting to its first — rendered as a large icon. To
support this, render_small now also renders Large* items small and
render_large now renders plain Tool/Dropdown items large. Large dropdowns
render as icon + label + a bottom arrow strip via a shared
render_large_dropdown helper, used in both the full ribbon and collapsed
panels.
Ribbon dropdowns (tool dropdowns + undo/redo history) now anchor their
overlay directly below their widget at any size: a transparent PosReport
wrapper records each dropdown button's screen bounds into a thread-local
map on draw, and the overlays read it via Ribbon::dd_anchor instead of the
layout-estimating compute_*_left helpers.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Add a DensitySwap widget that renders the widest variant of the tool
area that fits the current width. The ribbon builds two variants via the
new build_tool_row(compact) helper: full-size panels, and a compact one
where LargeTool/LargeDropdown collapse to small icon columns (the
layer/properties/style combos always stay full). render_small now also
matches the Large* variants so they can be drawn small.
First stage of the adaptive tool area (shrink -> wrap -> collapse).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Remove the OCS-logo button and the dropdown menu it opened; the top strip
now shows direct New / Open / Save / Save As / Print buttons ahead of the
undo/redo controls. Drop the HELP and OPTIONS commands (HELP dumped text to
the command line, OPTIONS aliased the drafting-settings popup).
The app-menu overlay is gone, so its rendering code, MENU_ITEMS, four dead
messages, and the LOGO_RED / GEAR / HELP assets are removed. The recents
list it held is the only survivor — moved into OpenCADStudio.recent_files
via a new app/recent.rs, backing the Start page's Recent Documents panel.
Quick-access icons are redrawn in a cleaner style (file-plus, folder-open,
device-floppy, file-export, printer) and tinted light so they read on the
dark strip; FOLDER / DOC keep their old glyphs for the file browser.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Change CadModule::ribbon_groups() from -> Vec<RibbonGroup> to -> &[RibbonGroup],
backed by a per-module OnceLock cache. The ribbon group tree is static data
(&'static str, Copy enums) that was being fully reconstructed — Vecs, String
clones, enum discriminants — on every call, including all three per-frame
call sites (view(), dropdown_overlay(), style_combo_overlay()).
- 7 built-in modules + 2 plugin templates cache via function-local OnceLock.
Safe because each is a unit struct with exactly one instance per process
(plugins run in isolated child processes; see PluginProcess::spawn()).
- SharedCadModule avoids the static pattern entirely, storing groups in an
instance field (owned.rs).
- IPC runner converts &RibbonGroup -> OwnedRibbonGroup directly via new
From<&T> impls, avoiding an intermediate clone.
- render_small/render_large and the two make_tool_row closures now borrow
(&RibbonItem, &[ToolDef]) instead of taking ownership, so the view loop
doesn't need to clone items to satisfy the old by-value signatures.
- Various match-ergonomics deref fixes (*id, *default, *icon, etc.) from
the &RibbonItem pattern change.
PR #213 introduced rustfmt drift in tip_style / swatch indentation and an
inline if-expression in two previously fmt-clean files. Format pass only;
no behaviour change.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Consolidates 21 occurrences of identical Color literals scattered across widgets.rs and mod.rs into 9 named constants (COMBO_BG, COMBO_HOVER_BG, COMBO_OPEN_BG, COMBO_BORDER, COMBO_ACTIVE_BORDER, COMBO_ARROW, SWATCH_BORDER, TIP_BG, HIST_INACTIVE_BG). -85 net lines, zero behaviour change.
The text / dimension / multileader / table style combos rendered their
open list inline inside the ribbon row, so the popup was clipped to the
fixed ribbon-row height and got cut off even with only two entries. The
Draw-tab dropdowns and the layer combo avoid this by rendering as
floating overlays.
Render the style list as an overlay too: the combo now draws only its
button inline, and a new `style_combo_overlay` builds the list as a
floating panel that grows to fit its entries, positioned with the
existing `compute_dropdown_left` (which already accounts for
StyleComboGroup widths) and mirroring `layer_combo_overlay`.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Ribbon tooltips were centered below the button, so the cursor resting on
the button covered the tip text. Anchor them to the right instead, clear
of the cursor. (#143)
Co-Authored-By: Claude Opus 4.8 <[email protected]>
UI chrome (dropdown carets, undo/redo, checkmarks, close/menu/osnap,
layer/file-menu icons) and ribbon tool icons were drawn as Unicode
glyphs. Desktop fills missing glyphs via system-font fallback, but the
web build bundles only Fira Sans with no fallback, so they rendered as
empty tofu boxes (issue #116).
Add a shared src/ui/icons.rs (SVG-backed, tintable) plus bundled assets,
and switch every glyph site and IconKind::Glyph tool def to SVG.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Add-ons had no way to request a file picker — only the host's own Open
dialog. PluginFileDialog lets a plugin tool ask the host to open a native
picker; on selection the host dispatches "<command> <path>" back to the
plugin with original case preserved (bypassing the command-line
upper-casing that mangles case-sensitive paths on Linux/macOS). The demo
plugin gains an Import tool exercising it.
Part of the #100 extensibility epic (surfaced in #106).
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Picking a colour from the full palette showed a generic "Index" label.
Add color_display_name: standard names for ACI 1-9 / ByLayer / ByBlock,
"R,G,B" (0-255) for every other indexed or true colour. Used by the shared
colour selector button and the ribbon colour combo.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Phase 4.1. Handle / u64 keys dominate the hot maps (block_defn,
hatch / image / mesh caches, draw-depth map, viewport wire caches);
the default SipHash is wasted work on integer keys. Alias the std
types to rustc_hash::FxHashMap / FxHashSet crate-wide so every map and
set — including the cross-module draw-depth map shared between scene
and the GPU pipelines — uses the faster hasher consistently.
Mechanical: no behavioural change. Constructors moved from ::new() to
::default() (Fx variants carry no RandomState). Build + 13 tests green.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Two behaviour changes that bring the command line in line with
established CAD workflow:
1. **Always-on keyboard capture.** The input was blurred whenever no
command was running, so typed letters went nowhere until the user
clicked into the field. Replace the blur with a re-focus on every
command lifecycle transition (cmd_result.rs) and on viewport / tab
/ ribbon clicks (update.rs), focus the input at startup, and add
a global key subscription that catches any unhandled character key
or backspace and routes it into the buffer via two new messages
(CommandAppendChar / CommandBackspace) — so the command line keeps
accumulating keystrokes even if focus parks somewhere not
focusable. Removes the unused blur_cmd_input helper.
2. **Space submits.** Typing a space at the end of the input now
submits the buffer the same way Enter does. Only exception: an
active command that's currently collecting free-form prose
(TEXT / MTEXT / DDEDIT body, ATTDEF prompt / default value,
INSERT attribute value, ATTEDIT cell). Those advertise the new
`CadCommand::wants_text_with_spaces()` flag so the Space character
reaches the buffer as a literal.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
New StyleComboGroup RibbonItem renders a dropdown combobox for selecting
text style, dimension style, multileader style, or table style. Ribbon
groups in the Annotate tab now match the reference layout: each group
has a large tool on the left and a style combobox with tool rows on the
right.
Each combobox shows the active style name, opens a scrollable item list
on click, and has an optional "Manage…" row that fires the style manager
command (STYLE / DIMSTYLE / MLEADERSTYLE / TABLESTYLE).
Style state is synced from the document on every layer refresh via
sync_ribbon_styles(). RibbonStyleChanged message propagates selections
back to the acadrust document header and the active_mleader_style field.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- Deactivate ribbon tool highlight when command ends (issue #1)
- Reset last_point on new command dispatch to fix polar snap offset (issue #2)
- FILLET/CHAMFER: disable text input during entity pick so clicks reach viewport (issue #3)
- Dropdown icon buttons now use RibbonToolClick so they highlight blue when active
- FILLET/CHAMFER: AutoCAD-style sub-step UX — typing R/D + Enter prompts for value
- Fix FILLET arc preview plane: arc was rendering in XZ instead of XY
- Bump version to 0.1.1
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
- ribbon.rs → ribbon/mod.rs + ribbon/widgets.rs
Extracted all rendering helpers, button styles, colour constants,
layout constants and dropdown position calculators into widgets.rs.
- scene/mod.rs → scene/mod.rs + scene/selection.rs + scene/render.rs
Moved SelectionState into selection.rs; moved CameraState, Primitive,
shader::Program/Primitive impls, render_style and resolve_pattern
into render.rs, reducing mod.rs from 1323 to ~1030 lines.
Co-Authored-By: Claude Sonnet 4.6 <[email protected]>