Every dynamic dimension gets a DIMSCALE override for 12 px text, refreshed
per zoom band, at creation and after load. A SCALE/ROTATE of an entity whose
own end points a driving dimension measures now pins its second constraint
point so the dimension moves the first end, as the reference does.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
An OTRACK crossing is the meeting of two tracking vectors, but
`OtrackHit` carried only the one a typed distance is measured along, so
the overlay had a single guide to draw. Snapping to the crossing of two
vectors worked and looked like an unexplained point on one line.
Carry the crossed ray out of `otrack_snap` as `OtrackHit::cross`, keep it
on the app beside `otrack_active`, and let the overlay take a list of
guides rather than one. `otrack_active` still drives typed-distance
entry on its own, so distance entry is unchanged.
FixesHakanSeven12/OpenCADStudio#1313
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01TQzYZsXBwPZ5HwBaGitEUV
(cherry picked from commit 2f6caacece)
- tests/fixtures/plot/.page-setups-metric.dxf.ocs.lock was the app's own
single-instance lock file, committed by accident in #1343; remove it and
ignore *.ocs.lock.
- load_plotsettings_into_dialog: #1343's refresh_custom_scale_fields borrowed
self while #1360 still wrote through the plot_dialog borrow; write the
style error first.
- The wasm list_printers stub returns the Result #1358 gave the native one.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Top Front Right/Left kept Y (north) as up when starting from Top,
tilting the base because orbit is turntable (never banks). Make
snap_to_direction deterministic: world +Z horizon except near +-Z
where north +Y is used, sign-preserved only for intentionally
inverted views. Expand opposite() from faces-only to all 26 regions
so already-there flip works for edges/corners.
Tables copied from other applications into the plot styles folder were
listed but refused on selection, showing as "missing" with the reason
lost on a console: the loader failed hard on a checksum or a text length
that did not add up and on any non-UTF-8 byte — and those files carry
their descriptions in Windows-1252.
- decompress_ctb reads the file as it is: a checksum or length mismatch is
reported and read past (a genuinely cut-off file is still an error), a
zlib stream with a bad trailer is retried as raw deflate, and text that
is not UTF-8 is read as Windows-1252 (pens and colours are numbers and
unaffected; other code pages only show wrong characters in descriptions).
- PlotStyleTable carries load_warnings; every load site reports them on the
command line, so a table that needed tolerance says so.
- load_named explains itself: a .stb name says named tables are not
supported yet, a .ctb missing from the folder says which folder to copy
it into (instead of "Unknown built-in plot style").
- The Plot dialog shows the reason under the Table row (style_error) —
from a selection, from a page setup that names a table, and from the
Load… picker, whose error used to be swallowed.
- A table picked with Load… from outside the plot styles folder is copied
into it (unless a file of that name is already there), so it is listed
from then on and page setups naming it resolve after a restart.
Tests: flipped checksum, text length off by one, a short payload field,
Windows-1252 descriptions, a truncated stream, .stb and missing names.
Co-Authored-By: Claude Opus 5 <[email protected]>
On Windows a plot with no printer chosen went to the PDF application's
bare "print" verb — whatever that application considered its default —
and a viewer without a "printto" verb (Edge, store readers) silently
dropped the printer the dialog had chosen. The dialog also never named the
system default, and a failed printer enumeration left only "Default" and
"PDF" with the reason written to a console nobody sees.
- The default printer is looked up by name (GetDefaultPrinterW) both for
the dialog's "System default printer (…)" entry and before every job.
- A pure route decision (plan_print_route) resolves "Default" to that
name and uses the PDF application only when it registered a "printto"
verb for .pdf (AssocQueryStringW); otherwise the job goes straight to
the spooler through the existing GDI path. The report says which:
"Sent to printer: NAME (direct print | via the PDF application)".
- list_printers reports the system's own error; the plot dialog shows it
under the printer row and the command line warns once. A desktop without
CUPS is an empty list, not an error.
- PRINTERS command: default printer, listed printers (or the error) and,
on Windows, the print route — a report to paste into an issue.
- Properties… on the Default entry opens that printer's preferences sheet.
- GDI path: the driver's dmSize is kept (its private data lives behind
it), the print raster is no longer memoised for the session, and a
spooled temporary PDF is removed.
Tests: route planning (named / blank / no default / printto or not), the
PRINTERS command; the two Windows-only GDI tests pass the resolved name.
Co-Authored-By: Claude Opus 5 <[email protected]>
The Options window used to commit every control the moment it was
touched, with no way back but re-editing. It now works like the Drafting
Settings window: changes still show at once (a theme, a cursor size or a
language is seen immediately), but nothing is committed until Apply or OK.
Close — and the window's × or Esc — puts everything back the way it was
when the window opened or Apply was last pressed, asking first when there
is something to lose.
The commit point is a snapshot of the persisted configuration plus the
drawing variables the 3D Modeling page edits (with the drawing's modified
flag), so every control the window has or gains is covered without being
registered; reverting re-applies the snapshot the way start-up does and
re-bakes text or regenerates solids when those settings moved.
The "unsaved changes" guard is now a shared modal helper used by both
windows.
Co-Authored-By: Claude Opus 5 <[email protected]>
- PSETUPIN / -PSETUPIN: with no arguments a file picker offers the chosen
drawing's named page setups in the Plot dialog; `PSETUPIN <file>` opens
the same chooser on that file; `PSETUPIN <file> *` or
`PSETUPIN <file> name,name` import straight away. Same-named setups are
replaced, the import is one undo step, and the report counts both.
- The Plot dialog's page-setup list gains "Import…": the chooser shows the
source drawing's setups with a tick each, All, Import and Cancel, and
selects the first imported setup afterwards. The source is read off the
UI thread with the same reader the editor uses for its own setups
(`scene::document_page_setups`), and handles that only meant something
in the source drawing (plot view, visual style) are dropped on import.
- Options → General → Plotting: "Show the page setup for new layouts"
opens the Plot / Page Setup dialog whenever a layout is created
(persisted with the plot preferences).
Tests: the document reader, the command (all / named / unknown / missing
file / undo) against a drawing saved by the app, the chooser flow, and the
new-layout option. Nine new strings in all 21 locales.
Co-Authored-By: Claude Opus 5 <[email protected]>
Plot dialog:
- "Units" (millimetres / inches) is the page setup's own unit: the offsets
and the custom scale are shown in it, the stored ratio converts with it
so the plotted result and the paper space's unit stay the same
- "Custom: [paper] mm = [drawing] units" spells the picked scale in that
unit and accepts a typed ratio, which selects the list's name for it or
becomes a ratio entry of its own
- "Hide paperspace objects" is the page setup's plot-hidden flag; it is no
longer derived from the "Hidden Line" shade mode
- "Save changes to layout" (paper layouts, on by default) writes the dialog
into the layout when a plot is sent; previews never touch it
- offsets are labelled in the page-setup unit and kept in millimetres in
the file
System variables (typeable, through SETVAR, listed by SETVAR ?):
- PLOTOFFSET, PAPERUPDATE, PLOTROTMODE, PLOTTRANSPARENCYOVERRIDE and
BACKGROUNDPLOT live with the persisted plot preferences: the plot origin
counts from the printable corner unless PLOTOFFSET says the paper edge;
a printer that does not list the sheet either warns or switches to its
default sheet; transparency can be forced on or off; BACKGROUNDPLOT bit 1
is the dialog's background switch and bit 2 drives batch plots
- CTAB and TILEMODE read and switch the current layout
Tests cover every new control and variable; six new strings in all 21
locales; the Plot dialog grows to 940×690 for the two extra rows.
Co-Authored-By: Claude Opus 5 <[email protected]>
On the web build, {"op":"open","path":…} resolves the name against the
browser's recent-files cache, so a file that is not already there cannot be
opened by the control channel. Accept {"op":"open","name":…,
"data_base64":…} instead: the bytes go through the same parse, caching
and recovery path as a file chosen in the browser picker
(io::open_bytes_web, now shared with pick_and_load_web).
Native builds keep path-based open and reject data_base64 with
invalid_request.
Refs #1349
crate::network and the open crate are desktop-only, but the community-font
download (font_repo.rs) and the XREF row Open action call them
unconditionally, so cargo check --target wasm32-unknown-unknown fails on
main. Gate both; on the web download_fonts returns an error, since the web
build has no network client or fonts folder.
- app_with_fixture embedded CARGO_MANIFEST_DIR straight into an
automation JSON string; on Windows the backslashes become invalid
JSON escapes and the open failed. Build the payload with json!.
- driver_options_are_edited_remembered_and_sent_with_the_job exercises
the CUPS options editor, which Windows never opens (PrinterProperties
routes to the system dialog there); gate it to non-Windows.
A drawing saved by a commercial CAD application (tests/fixtures/plot,
provided by Panos: one 10 m line, three layouts) corrects several
assumptions of the previous commit:
- rotation is relative to the medium's own orientation: an unchanged sheet
keeps the driver's exact medium (its PDF driver lists landscape media of
its own, written with rotation 0; a system printer's portrait A3 plotted
landscape carries 90); only a newly picked sheet is written as the
catalogue's portrait medium
- the stored scale factor (DXF 147) is the ratio × 25.4 on millimetre page
setups and the plain ratio on inch ones; the "use standard scale" bit
stays clear for standard and custom scales and is set for fit-to-paper;
a standard code is kept across a paper-unit conversion ("1:1" on an
inch page setup over a millimetre paper space is 1 in = 25.4 units)
- layout plots take the same scale path as every other area, so a foreign
layout's paper-space unit survives
- "plot with plot styles" / "display plot styles" round-trip as the
dialog's checkboxes, independent of a style table being named
- layout limits place the sheet at minus its displayed margins, the way the
source application does (the printable corner is the paper-space origin);
the margin ↔ rotation mapping shared by the printable-area guide now
turns the medium's top margin into the layout's left one at 90°
The fixture round-trips through the dialog with every stored field
unchanged; picking a new sheet on it writes the canonical form.
Co-Authored-By: Claude Opus 5 <[email protected]>
One writer (plot_settings_from_dialog) now serves both the layout's embedded
settings and named page setups, so the two can no longer drift apart:
- media are stored as the portrait sheet with the orientation in the
rotation code (0/90/180/270; upside-down adds a half turn) instead of
swapped width/height with rotation limited to 0/180
- standard scales are written by their code (1:1 … 100:1) with matching
numerator/denominator; other scales as a readable custom ratio in the
layout's own paper unit, converting from the drawing's scale-family unit
(a metre drawing plotting "1:100" stores 10 mm = 1 unit, an inch layout
stores 1 in = 2540 units); reading converts back and keeps a ratio the
scale list has no name for instead of snapping to 1:1
- the layout's paper unit and every flag the dialog does not edit survive a
round trip untouched; layout limits follow the paper-space unit
- new drawings, new layouts, CLEAR and the automation "new" op share one
default page setup (canonical A4 name, rotation 90, none_device, 1:1
layout plot) through Scene::reset_to_new_drawing
Scene gains scale_family_unit_mm, scale_ratio and a scale-name ratio parser
(ratios and architectural names). Round-trip tests cover orientation,
standard/custom/fit scales, unit conversion, named-setup parity, preserved
flags and the default layout.
Co-Authored-By: Claude Opus 5 <[email protected]>
The Display page could only take a typed #RRGGBB, which is precise but
no way to choose a colour by eye. The swatches are now buttons that
open a wheel.
The wheel does not replace the hex field, it sits beside it: typing an
exact value stays the quickest way to reproduce a known colour. On
submit it hands a hex string to the existing ModelSpaceBgChanged and
friends rather than writing the config itself, so the two routes
cannot drift apart on validation, persistence, or the MatchTheme
fallback an empty field triggers.
Also stops the Browser shipping in the default dock layout. It was
added to the right edge, which shifted the block palette to index 1
and broke four dock tests that encode the existing arrangement. Those
tests were right and the change was wrong: the earlier commit claimed
to leave the layout alone. BROWSER now docks the panel on first use,
so nobody's edges rearrange on upgrade for a panel they never asked
for.
A docked outline of the drawing: the three origin planes, the open
sketch, and every solid labelled with the operation that made it,
recovered through primitive_property_operation. Rows run ordinary
commands, so clicking a plane starts a sketch on it and clicking a
body selects it.
It is a table of contents, not a feature tree. The DWG history records
how each solid was built but nothing re-runs it, so an earlier feature
cannot be edited to rebuild the ones after it. Presenting the list as
editable history would promise exactly that, so the panel states what
it is instead.
Off by default and opened with BROWSER, leaving the existing dock
layout alone for anyone who does not want it.
The toolbar Help button was a disabled tip that could never be clicked. It now opens a modal window explaining the manager on the web build: read-only reference list, disabled Attach/Refresh/Change Path (no filesystem access or native file pickers, so no add/reload/repath and no previews), nested read-only rows, and saved-drawing requirement for relative paths.
Includes a Report-an-issue button opening the GitHub issues page and a Close button; wired as ModalKind::XrefHelp through the shared modal frame so dismissal works on native and web.
The Snap and Grid tab showed static grid values with dead checkboxes, and every grid line stroked identically.
Grid X/Y spacing and Major-line-every are now editable inputs (validated 2-100 for majors, dirty-tracked, applied with an error message on bad input); Adaptive-grid and Display-beyond-Limits checkboxes are wired to live state. Every Nth line partitions into major_segments and strokes subtly brighter (1.5x alpha, 0.75px) for the reference hierarchy. GRIDMAJOR state persists to UserSettings. Covers partition, invalidation, and parse tests.
The viewport grid ignored the configured spacing and forced square grids: compute_grid_step hardcoded a 1.0 base, a single shared step drove both line families (including the tangent derivative used for gap measurement), and the display always clipped to LIMITS despite the beyond-Limits option.
Grid spacing now scales per-axis from GRIDUNIT via compute_grid_steps (adaptive 5x growth, fixed passthrough), GridParams carries step_x/step_y, snap tolerances follow the visible steps, and the LIMITS clip is skipped when beyond-Limits is set. Also fixes a per-axis overflow fallback that returned the X base for Y. Spacing state (X/Y, adaptive, beyond-Limits) is stored on the app and persisted to UserSettings at defaults; dialog wiring follows separately.
Comments and the Options help text describe the behaviour of commercial
solutions without naming any product. The command menu puts the command's
keywords directly under Enter / Cancel, with Recent Input and Snap
Overrides below them, so the choices that change from step to step are the
closest to the pointer.
Co-Authored-By: Claude Opus 5 <[email protected]>
The command menu now follows the commercial solutions' layout row for row: Enter, Cancel,
Recent Input ▸, the command's keywords, Snap Overrides ▸ (every object snap
with its glyph, Mid Between 2 Points, None, Osnap Settings…), then Pan and
Zoom. Pan / Zoom run transparently while a command is active ('PAN,
'ZOOM …): navigation modes leave the command untouched and an interactive
ZOOM Window parks it and resumes it afterwards. Rows carry an icon gutter
so labels align; the Enter row shows a default value ("<0.5000>") as its
hint instead of a second finish row.
ARC gets the commercial solutions' prompt flow with all keywords: start [Center], second
point [Center/End], end; after Center: end [Angle/chord Length]; after
End: center [Angle/Direction/Radius]; Enter continues tangent. The
previous default (Center/Start/End with method codes) stays as ARC_CSE.
Object snaps gain a one-shot "None" override. New strings in all locales.
Co-Authored-By: Claude Opus 5 <[email protected]>
Every new UI string (menu rows, Options → Right-click Customization, the
PLINE sub-prompts and keyword labels, MOVE/COPY/OFFSET prompts) gets its
Fluent attribute in all 21 locales plus the source → attribute rows in
locale_catalog.rs; the SETVAR help line lists SHORTCUTMENU and
SHORTCUTMENUDURATION. Tests that compared prompt text now check command
state so they pass under any UI language.
Co-Authored-By: Claude Opus 5 <[email protected]>
A right-click while a grip is hot opens the grip menu: Enter places the
grip, Stretch / Move / Rotate / Scale / Mirror switch how the gripped
objects are edited (the latter four hand the selection to the matching
command with the grip as its base point), Base Point re-bases the gesture
at the next click, Copy leaves the originals in place and adds the moved
shapes as new entities while keeping the grip hot, Undo snaps the shapes
back to where the gesture started, Exit drops the edit.
The grip commit path moves out of the left-release handler into
`commit_active_grip_edit` so the menu, Enter and the placement click share
it; Enter with a hot grip now places it instead of repeating the last
command.
Co-Authored-By: Claude Opus 5 <[email protected]>
The viewport context menu is now built from a pure model
(ui::popup::context_menu) rendered by the overlay: while a command runs it
offers Enter / Cancel / every keyword of the command's options() with the
keyword shown as a hint, a Recent Input submenu and M2P; when idle it offers
Repeat, Recent Input, Clipboard, Undo/Redo with history labels, the edit
block (Erase/Move/Copy/Scale/Rotate/Mirror/Draw Order), Isolate, Pan/Zoom,
selection tools, Properties and Options. The default row is anchored under
the pointer and the menu is keyboard-navigable (arrows, Enter, mnemonic
letters; any other key closes it and types into the command line).
Right-click behaviour is a user preference (Options → User Preferences,
SHORTCUTMENU / SHORTCUTMENUDURATION): shortcut menu (commercial solutions default),
time-sensitive, or the previous Enter-first cycle.
LINE gains options() (Close/Undo/eXit).
Co-Authored-By: Claude Opus 5 <[email protected]>