Commit Graph
21 Commits
Author SHA1 Message Date
Karim Jerbi a7e3cf3a6d fix(ui): enforce WCAG AA contrast thresholds across UI themes
- Raise accessible_accent default threshold from 2.0 to 3.0 (WCAG 1.4.11 UI state indicators)
- Raise EntryKind::Info command line history threshold from 3.0 to 4.5 (WCAG 1.4.3 normal text)
- Update themed_check_cell to verify >= 3.0:1 contrast across both weak popup panel and strong highlighted rows
- Replace fully-qualified accessible_accent_threshold calls inside closures with use imports
- Add comprehensive 9-suite theme accessibility contrast test suite covering all 22 themes across core surfaces
2026-09-04 15:34:24 +01:00
Karim Jerbi afff1a40dd feat(ui): add press-to-capture shortcut editing to Keyboard Shortcuts dialog
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.
2026-09-01 12:17:47 +01:00
Karim Jerbi 166d758820 fix(ui): stabilize themed cache keys with static byte slices and harden tests
The themed handle cache (introduced in cc430cdb) keys entries by (address, length) of &'static [u8] art. However, the art was declared as const; Rust may inline distinct copies of a const at each use site, giving the same logical icon different addresses. This broke cache lookups for wrapper functions (e.g., themed_arrow_down) that refer to the same icon from a different module than the cache test. Convert the ~30 themed-relevant byte slices (TRI_*, HOME, UNDO, REDO, CHECK, etc.) to static so each artwork has one stable address process-wide.

Also refactor themed_handle to drop the immutable borrow before building a miss handle, preventing a reentrant call from panicking on a second borrow_mut.

Adjust themed_cache_tests to use delta assertions and per-test cache removal for isolation, and add coverage for second-call cache hits, wrapper delegation, and the length component of the key.

Test: ui::icons::themed_cache_tests::themed_cache_returns_same_handle_for_same_bytes
Test: ui::icons::themed_cache_tests::themed_cache_caches_distinct_bytes_separately
Test: ui::icons::themed_cache_tests::themed_cache_second_call_is_a_cache_hit
Test: ui::icons::themed_cache_tests::themed_wrapper_populates_cache_for_underlying_bytes
Test: ui::icons::themed_cache_tests::themed_cache_key_includes_length
Test: ui::icons::themed_cache_tests::themed_cache_speedup_over_uncached_handle_creation
2026-08-30 00:36:04 +01:00
Hakan Seven 0237652963 style(icons): unify theme-aware palette 2026-08-29 12:20:24 +03:00
Karim Jerbi cc430cdbe9 perf(ui): cache svg::Handle in themed* icon functions
The 9 themed* functions that take &'static [u8] artwork each built a
fresh svg::Handle::from_memory(bytes) on every call, with 113
icons::themed call sites per frame across ribbon/statusbar/popups.
Add a thread_local THEMED_CACHE keyed by the bytes' (address, length)
and a single themed_handle(bytes) helper; the 9 themed* functions all
go through it. The 13 wrapper functions (themed_arrow_*, themed_home,
themed_undo, themed_redo, themed_check_cell, ...) delegate to those 9
and pick up the cache transparently, so all 22 themed* functions (and
the 113 call sites) are deduped.

The cache key is (address, length) -- not pointer alone -- to match the
existing SEMANTIC_CACHE belt-and-braces pattern and guard against any
future call site that passes a sub-slice of a static (e.g. &BYTES[1..]),
which would otherwise alias the wrong handle. Every include_bytes!
produces a unique static slice today, so the length is defensive.

Construction is amortised to once per artwork per process. No semantic
change; visual output is byte-identical.

Measured performance gains (integrated
themed_cache_speedup_over_uncached_handle_creation bench, 20_000
iterations each, same machine):

  |                | dev profile   | release profile |
  |----------------|---------------|-----------------|
  | Before         | 235 ns/iter   | 26 ns/iter      |
  | After (cached) |  78 ns/iter   |  9 ns/iter      |
  | Cold miss      | 900 ns        | 100 ns          |
  | Speedup        | 3.0x          | 2.9x            |

Release-mode warm path: 9 ns/iter. At 60 fps with up to 113 themed*
calls per frame, that is ~1 us/frame of handle work -- a rounding
error against a 16.6 ms frame budget. The cold-miss cost (one parse
+ one insert, 100 ns release) is the one-time price per new artwork
and is amortised to negligible per-frame overhead (it happens at
most once per artwork per process). The speedup is stable at ~3x
across dev and release because the ratio of work is the same:
hashmap-get + Handle clone vs. a full SVG parse + allocation.

test ui::icons::themed_cache_tests::themed_cache_returns_same_handle_for_same_bytes
test ui::icons::themed_cache_tests::themed_cache_caches_distinct_bytes_separately
test ui::icons::themed_cache_tests::themed_cache_speedup_over_uncached_handle_creation
2026-08-28 06:05:31 +01:00
Hakan Seven deaecdfb1e fix(ribbon): mask tools on start
Refs #494

Refs #507
2026-08-05 00:52:52 +03:00
Hakan Seven 4be8d9735c feat(ui): add dockable properties panel
Preserve panel width across left/right docking and persist resize, position, and auto-collapse preferences.

Refs #348
Refs #650
2026-08-04 13:46:02 +03:00
Hakan Seven e35348525a fix: resolve offset and theme regressions
Keep OFFSET picks current across newly created entities and preserve the command after pick-first offsets.

Use palette-provided contrast colors for active status controls and Alias Editor removal buttons.

Refs #653
Refs #658
Refs #659
2026-08-04 13:24:38 +03:00
Hakan Seven 3ab8dc3ca5 fix(ui): make modal layouts intrinsic
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
2026-07-30 14:47:28 +03:00
Hakan Seven 28d8246c68 feat(ui): add discussions and semantic theming
Generate web discussion snapshots in CI; native builds use the public
feed and an offline cache.
2026-07-28 11:49:45 +03:00
Hakan Seven 4bf14baea4 feat(ui): add configurable Iced themes 2026-07-28 09:33:12 +03:00
Hakan SevenandClaude Opus 4.8 977701b47e feat(ui): MTEXT editor in shared modal + resizable dialogs
Route the MTEXT editor through the shared in-canvas modal frame with an
Apply-at-top toolbar (style-manager style): Apply commits and keeps the
editor open (creates on first apply, updates in place after), the ✕
cancels. Give every modal a bottom-right corner resize grip driven by a
shared grow-only `modal_resize` delta that resets with `modal_offset` on
open/close, so each dialog opens at its natural size and can be dragged
larger — style managers included.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-19 15:47:09 +03:00
Hakan SevenandClaude Opus 4.8 6032a024d2 feat(save): native OS dialog for Save As, keep in-app format picker
Replace the custom in-app file-browser save dialog with the native OS
save dialog for choosing the destination path — folder browsing and
overwrite confirmation now come from the OS. A compact in-app modal
still selects the DWG/DXF format and version (a native dialog cannot
carry the version year) and appears only for Save As. Plain Save on an
unsaved drawing defaults to DWG 2018 with no picker. The file-name field
is shown only on web, where there is no native dialog.

Remove the hand-rolled directory listing, folder navigation, and
overwrite modal.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-11 09:12:36 +03:00
Hakan SevenandClaude Opus 4.8 f4ed263425 feat(ui): ribbon collapse-mode selector + start-page recent controls
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 <noreply@anthropic.com>
2026-07-06 01:06:01 +03:00
Hakan SevenandClaude Opus 4.8 7bab7c7b8d feat(ui): replace app-menu logo with a quick-access file toolbar
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 <noreply@anthropic.com>
2026-07-03 09:51:49 +03:00
Hakan SevenandClaude Opus 4.8 1e9de924be feat(command-line): copy/clear history + selectable log (#232)
The command-line history dropdown was display-only, so users had to
screenshot and OCR the log to report output. Make it copyable:

- Render the whole backlog in one read-only text_editor so a single
  mouse drag selects across lines and Ctrl+C copies the span.
- Add Copy (whole log to clipboard) and Clear buttons to the dropdown.
- Gate the Ctrl+C/X/V accelerators on event Status::Ignored so a focused
  text widget's copy/cut/paste wins instead of firing COPYCLIP/CUTCLIP;
  the drawing's clipboard shortcuts still work when it has focus.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 18:17:46 +03:00
Hakan SevenandClaude Opus 4.8 d8862ee9cf feat(statusbar): icon toggles, taller bar, centered pills (#216)
The bottom status bar was hard to read: size-10 text labels in a 26px
strip. Make it legible and consistent with the drawing tab bar:

- Height 26 → 30 to match the document tab bar; the command-line input
  pill matches too, so the three strips line up.
- Replace the drafting-aid toggle labels (ORTHO/POLAR/OSNAP/OTRACK/DYN/
  LWT and the OCS pills ISO/QP/FILTER/SC/CLEAN/TPY) with 17px SVG icons;
  the name stays in each pill's tooltip. Twelve new icons under
  assets/icons/status, drawn in the existing 24×24 stroke style.
- Vertically centre every element via center_y, and bump all pill text to
  size 12 with uniform padding plus larger chrome icons (menu, scroll
  arrows, osnap dropdown) so nothing reads smaller than the toggle icons.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-01 09:33:00 +03:00
Hakan SevenandClaude Opus 4.8 f00c48bebb feat(viewcube): add compass ring, cardinals, and nav controls
Build the ViewCube out into a full navigation widget:

- Compass ring painted in the cube's ground plane (z = base) so it
  reads as a ground compass and foreshortens with the view.
- N/E/S/W cardinals painted flat onto the ring band; clicking one snaps
  to that side elevation. Added S/W glyphs to the bitmap atlas.
- Home button (top-down view), roll arrows (90° roll about the view
  axis), and four nudge triangles (90° tip/spin) as SVG iced controls
  laid out around the cube. New home.svg.
- WCS / named-UCS selector under the cube, switching the active UCS.

The cube hit area now shares one stack with the control buttons, so a
click on a button is caught by the button while a click on the cube
falls through. Face clicks snap to the highlighted region rather than a
cursor position the viewport's move handler can overwrite.

Camera gains home_view / roll_by / nudge_90; floating viewports drive
the same ops through mutate_active_viewport_camera, which re-encodes the
result to the stored view direction + twist.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 23:46:43 +03:00
Hakan SevenandClaude Opus 4.8 d36cc9786f fix(web): render OSNAP menu symbols as SVG instead of font glyphs
The object-snap menu drew each mode's marker as a Unicode geometric glyph
(◻ △ ◯ …). The web build bundles only Fira Sans, which lacks them, so they
rendered as tofu boxes (#138). Add an SVG per snap mode under
assets/icons/osnap, map them via icons::osnap, and draw them with the same
tinted-SVG path the rest of the chrome uses — font-independent on every
platform.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 14:27:59 +03:00
Hakan SevenandClaude Opus 4.8 9eb805df23 feat(ui): unified viewport control chip + per-tile grid rendering
Move grid and grid-snap toggles off the status bar (and its customization
menu) into the top-left viewport chip, now a single container holding the
horizontal/vertical split buttons, render-mode picker, and grid/snap
toggles, sized to the render-mode picker. Add grid + magnet (snap) icons.

Render the grid per model tile from each tile's own grid_on, in its own
bounds and camera, instead of a single grid in the active tile. This stops
the grid flickering on/off as the cursor crosses panes or leaves the
viewport, and lets each pane show its own grid independently.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 23:29:34 +03:00
Hakan SevenandClaude Opus 4.8 864dd6cef7 fix(web): render UI icons as SVG instead of font glyphs
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 <noreply@anthropic.com>
2026-06-18 12:29:32 +03:00