Commit Graph
157 Commits
Author SHA1 Message Date
Hakan SevenandClaude Opus 4.8 834780ec5d feat(statusbar): quick properties floating panel (QP pill)
When the QP pill is on, a compact floating panel appears at the canvas
top-left on selection, reusing the docked panel's editable section rows.
Toggleable from the status-bar customization menu.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 23:31:36 +03:00
Hakan SevenandClaude Opus 4.8 66f7d24bc9 feat(statusbar): transparency-display toggle (TPY pill)
Add a transparency_enable uniform read by the wire shader: when the new
TPY pill is off, every line is forced opaque; when on, baked entity
transparency shows through. Toggling is a uniform write, no retessellate
(same mechanism as the lineweight-display toggle). Pill is toggleable
from the status-bar customization menu.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 23:25:09 +03:00
Hakan SevenandClaude Opus 4.8 28ec360255 feat(statusbar): isolate action menu on the pill and right-click
Clicking the ISO pill now opens a small menu with Isolate Objects, Hide
Objects and End Isolation instead of ending isolation directly; rows
that don't apply (no selection / nothing hidden) are dimmed. The same
actions are added to the viewport right-click menu.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 23:17:21 +03:00
Hakan SevenandClaude Opus 4.8 e0e7897952 feat(statusbar): isolate / hide objects with status pill
Add a hidden-entity set to the scene that the tessellation visibility
test skips, so hidden objects neither render nor hit-test. New commands
ISOLATEOBJECTS (show only the selection), HIDEOBJECTS (hide the
selection) and UNISOLATEOBJECTS (restore). An ISO status pill lights up
while anything is hidden and restores everything on click.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 22:51:23 +03:00
Hakan SevenandClaude Opus 4.8 243a0bc923 feat(statusbar): drawing-units pill with INSUNITS picker
Add a Units pill that shows the drawing's current INSUNITS value and
opens a picker to change it (Unitless, mm, cm, m, km, in, ft, mi, yd).
Toggleable from the status-bar customization menu like the other pills.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 22:06:42 +03:00
Hakan SevenandClaude Opus 4.8 d829f80a04 feat(statusbar): customizable pills with coordinates and clean screen
Add a customization menu (far-right handle) that lists every status-bar
pill with a check mark; toggling a row shows or hides that pill, and the
choice persists across sessions in the config dir.

New pills:
- Coordinates: live cursor readout (re-adds the model-space world offset
  so it shows true drawing coordinates), updated on every move rather
  than only while a command is active.
- Clean Screen: hide the ribbon and side panels for a full canvas.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 19:14:04 +03:00
Hakan SevenandClaude Opus 4.8 edc8cbc969 feat(scale): drive scales from the drawing's scale list
Read the model-space annotation scale from the standard CANNOSCALE /
CANNOSCALEVALUE header variables instead of a user-variable hack, and
populate both the status-bar scale picker and the viewport Scale
property purely from the drawing's ACAD_SCALELIST — no built-in
fallback set. Xref-derived scales (`_XREF` suffix, `name|` prefix)
are filtered out so only the host drawing's own scales appear.

Bumps the patched acadrust to the commit that adds CANNOSCALE/
CANNOSCALEVALUE read/write.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-04 16:18:01 +03:00
Hakan SevenandClaude Opus 4.8 10d674f606 feat(tablestyle): complete field coverage (flow, description, cell data + borders)
Table Style editor now exposes every TableStyle / RowCellStyle field:
- General: description, flow direction (Down/Up dropdown).
- Per cell: data type, unit type, format string, plus all six borders
  (left/right/top/bottom/horizontal-inside/vertical-inside) with line
  type (Single/Double), line weight, color (ACI), double-line spacing
  and a visibility toggle.
Border type/visibility apply immediately; the numeric border fields and
data/unit/format are written by the existing "Apply cell" button.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 21:09:37 +03:00
Hakan SevenandClaude Opus 4.8 6ae24cceee feat(styles): complete MLeader field coverage + Dimension DIMUNIT
MLeader Style editor now exposes every MultiLeaderStyle field: line
weight, line type / arrowhead block / text style / block-content blocks
(handle dropdowns from the document's tables), the four text attachments
+ attachment direction, the full block-content group (color, connection,
rotation, X/Y/Z scale, enable scale/rotation), leader & multileader draw
order, align space, and description.

Dimension Style editor adds the last uncovered DIMUNIT field.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 21:04:22 +03:00
Hakan SevenandClaude Opus 4.8 bd120ca307 refactor(styles): replace cycle buttons with dropdowns
The enum/handle pickers in the MLeader, Table and Dimension style
editors used "Cycle" buttons, which were awkward. Replace them with
pick_list dropdowns:
- MLeader: path type, content type, text angle, text alignment.
- Table per-cell: alignment (9 anchors).
- Dimension: arrowhead blocks (DIMBLK/1/2/LDRBLK) and ext-line linetypes
  (DIMLTYPE/LTEX1/LTEX2), options resolved from block_records/line_types.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 20:44:37 +03:00
Hakan SevenandClaude Opus 4.8 1b7edf6cae feat(dimstyle): block/linetype Handle cyclers for arrowheads & ext lines
The arrowhead-block (DIMBLK/DIMBLK1/DIMBLK2/DIMLDRBLK) and ext-line
linetype (DIMLTYPE/DIMLTEX1/DIMLTEX2) Handle fields are now editable in
the Dimension Style manager. Each shows the resolved block-record /
linetype name and a Cycle button that advances through the available
records (NULL = Default arrowhead / ByBlock linetype). Edits bump
geometry so dimensions re-render with the new arrowheads/linetypes.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 20:34:09 +03:00
Hakan SevenandClaude Opus 4.8 df44031ae8 feat(tablestyle): per-cell editing for Data/Header/Title rows
Each row's RowCellStyle is now editable in the Table Style manager:
text style name, text height, text & fill color (ACI), background-fill
toggle, and alignment (cycle through the 9 anchor positions). Margin and
per-cell buffers load on open/select via a shared load_tablestyle_bufs;
edits write through objects.values_mut() and bump geometry so tables
using the style re-render.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 20:28:58 +03:00
Hakan SevenandClaude Opus 4.8 e0c5db9e0c feat(mleaderstyle): add Multileader Style editor window
New dedicated style-manager window for MultiLeaderStyle objects (the
existing mlstyle window edits MLineStyle, not multileader). Opened via
the MLEADERSTYLE command / ribbon manager entry.

Editable across Leader Format / Structure / Content sections:
- path/content/text-angle/text-alignment enums (cycle buttons)
- line & text color (ACI), arrowhead & break-gap sizes
- landing distance/gap, max points, segment angles, scale factor
- default text, text height
- enable landing/dogleg, text frame, text-always-left, annotative
New/Delete/Set-Current style management.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 20:25:14 +03:00
Hakan SevenandClaude Opus 4.8 0dd86eb658 feat(tablestyle): editable General section (margins, flow-suppress toggles)
The Table Style editor's General fields are now editable: H/V cell margins
(buffers + Apply) and title/header row-suppressed toggles, loaded from the
selected style on select. Per-cell (Data/Header/Title) text/fill/border
editing remains a follow-up.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 20:13:30 +03:00
Hakan SevenandClaude Opus 4.8 a21caf821b feat(dimstyle): expand Dimension Style editor to the full field set
Adds the remaining ~40 DIMSTYLE variables across the tabs (and a new
Alternate Units tab): dim/ext line colors+lineweights, fixed-length ext
lines, separate arrow blocks, arc symbol, jog angle, text color/justify/
vertical pos/fill/direction, fit options (DIMATFIT/DIMTIX/DIMSOXD/DIMTMOVE/
DIMUPT/DIMTOFL/DIMFIT), decimal separator, rounding, zero suppression,
fraction + angular units, alternate units, and tolerance vert-just/zero
suppression. Only the arrowhead-block / extension-line-linetype Handle
references are left (they need a block/linetype picker).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 20:09:08 +03:00
Hakan SevenandClaude Opus 4.8 07a2a05975 feat(dimstyle): add Annotative toggle to the Dimension Style editor
Adds the annotative property (DsField::Annotative) to the dim-style edit
buffers, load/apply, and a checkbox on the Fit/Scale tab — consistent with
the Text Style editor's annotative toggle.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 17:49:38 +03:00
Hakan SevenandClaude Opus 4.8 52850947d2 feat(textstyle): full Text Style editor — big/TT font, fixed height, flags, annotative
Expands the Text Style manager beyond font/width/oblique: big-font and
TrueType font fields, fixed text height, Backward / Upside-down flags, and
an Annotative toggle (applied live). Bumps acadrust for the annotative
field.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 17:43:12 +03:00
Hakan SevenandClaude Opus 4.8 6f64d8d0d9 fix(text): keep in-place MText/Text editor on-screen at window edges
The editor overlay's anchor was only clamped to the top/left edge, so an
MTEXT placed near the right/bottom of the viewport pushed the 640px panel
(and its toolbar buttons) off-screen where they were unclickable.

Both the MText editor and the single-line TEXT editor now receive the
viewport canvas size and clamp the anchor against the right/bottom edges
too, so the whole panel stays visible and usable wherever it is opened.

Fixes #52

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-03 10:59:59 +03:00
Hakan SevenandClaude Opus 4.8 ac4126072c refactor(paper): drop sheet viewport border + clean stale PaperCanvas docs
The overall "sheet" viewport now IS the paper view, so its own border
rectangle must not be drawn as an entity — filter it out of
paper_sheet_wires_arc (content viewport borders are kept).

Also update the doc comments that still referenced the removed
PaperCanvas widget / paper_canvas_wires to describe the GPU sheet path.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-02 20:33:51 +03:00
Hakan SevenandClaude Opus 4.8 206ad1a4aa feat(paper): render the whole sheet via the shader, drop PaperCanvas
Paper space now renders entirely through the unified GPU shader, exactly
like model space — there is no separate 2-D canvas. The full-canvas
top-locked "sheet" viewport draws the layout's entities, hatches,
wipeouts and images; the desk is the container background and the white
printable area is a synthetic solid hatch.

- The white sheet fill uses draw_depth -2.0 (entity fills/wires are in
  the signed (-1, 1) range) so it is always strictly behind every object,
  with a negligible z offset (BIAS 0.001) — no far-plane clipping.
- Delete src/scene/paper_canvas.rs (the PaperCanvas widget) and its dead
  helpers (paper_canvas_wires / paper_sheet_wires / paper_canvas_cache).
  paper_canvas_hatches / _wipeouts remain — still used by paper hit-test.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-02 20:25:50 +03:00
Hakan SevenandClaude Opus 4.8 2627d06224 feat(text): in-place editors for all text entities
Route every text-bearing entity to an in-place editor instead of the
command line: a plain text box for single-line text (Text, attributes,
dimension override, tolerance) and the rich MText editor for MText /
MultiLeader. A Leader resolves to the entity it annotates.

- TEXT places a plain text box at the click; double-click edits in place.
- DDEDIT and double-click on any text entity open the matching editor.
- MLEADER places the leader then opens the MText editor on its content.
- LEADER follows the classic flow: points, Enter, then a linked empty
  MText annotation opens in the editor; the leader's annotation_handle
  links the pair so they edit/erase as a unit.

Adds shared field read/write helpers and a begin_text_edit router, plus
CommitAndEditText / CommitManyAndEditText results that commit an entity
and open its editor (commit_entity now returns the new handle).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 14:44:18 +03:00
Hakan SevenandClaude Opus 4.8 ac9022676f feat(mtext): editable preview with caret, blink, and new-line support
Type directly into the rendered MText preview: click to position the
caret, insert/delete, Enter for a new line, Space for a literal space,
and arrow keys to move. The caret blinks when idle.

Keep the trailing empty paragraph in the editor so the caret appears on a
fresh line immediately after Enter, before any typing. parse_mtext_paragraphs
split into a _ex variant; layout_mtext skips the blank-edge trim when
emitting glyph boxes.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 13:14:35 +03:00
Hakan SevenandClaude Opus 4.8 7190df8274 feat(mtext): edit text directly in the preview with a caret
The rendered MText preview is now an editable surface: it shows a text caret,
click to position it between glyphs, and type/Backspace/Delete/Enter to edit.
Left/Right arrows move the caret; drag-selection + toolbar formatting still
work and typing replaces a selection.

Keystrokes are routed by mapping visible-character offsets to byte spans in
the value (raw newlines count as paragraph breaks, matching the layout). While
the preview is open the command line releases its on_input so typed glyphs
reach the preview instead, and the caret starts at the end of the text so
typing works without a click first.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 10:16:30 +03:00
Hakan SevenandClaude Opus 4.8 c1b33677e2 feat(fonts): switch the text engine from CXF to LibreCAD LFF fonts
Replace the small QCAD CXF stroke-font set with the full LibreCAD LFF font
library (46 fonts) and a new LFF parser/engine in src/scene/lff.rs. The
parser reads the `[hex] char` glyph blocks, tessellates `,A<bulge>` arcs, and
resolves `C<hex>` glyph references. Per LibreCAD's RS_Polyline convention a
vertex's bulge curves the segment ending at that vertex — getting this wrong
turned round letters like O into sideways capsules.

Font name resolution mirrors LibreCAD: match by file stem, map common
AutoCAD/DXF SHX names (txt, isocp, complex, italic, gothic*, gost, …) to the
nearest LFF family, and fall back to `standard`. A missing glyph in the chosen
family falls back to the broad `unicode` font so accented/Cyrillic/Greek text
still renders. Cap height stays 9 units, so the text scale is unchanged.

All text consumers move from cxf:: to lff::; complex linetypes still render
embedded text but no longer draw embedded shapes (LibreCAD ships no shape
file). The CXF module and every *.cxf asset are removed, and the MText / text
style font pickers list the LFF families.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 03:31:20 +03:00
Hakan SevenandClaude Opus 4.8 32136dd9dd feat(mtext): character-level text selection on the preview
The rendered MText preview is now interactive: drag across glyphs to select
a character range and apply toolbar formatting (bold, italic, underline,
overline, strike, case, alignment) to exactly those characters.

The text layout gains an opt-in pass that emits one world-space box per
visible character carrying its reading-order index; the editor maps those
boxes into the preview frame, hit-tests pointer drags against them, and
highlights the selection. Formatting splices the matching inline codes into
the raw value at the selected byte range (mapped via a code-aware visible-
character scanner). With no preview selection the Edit-box path is unchanged.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 02:33:47 +03:00
Hakan SevenandClaude Opus 4.8 a07af32714 feat(mtext): segmented Edit/Preview toggle, default to preview
The editor opens in preview by default and switches body views with a
segmented Edit | Preview control placed between the toolbar and the body
(active tab highlighted). All toolbar properties feed the rendered preview.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 01:59:39 +03:00
Hakan SevenandClaude Opus 4.8 a5ad0666ce feat: Implement in-place MText editor with formatting toolbar and live preview
MTEXT (and double-clicking an MText) now opens an in-place editor anchored
at the insertion point instead of typing a single line on the command line.

The editor has a two-row formatting toolbar: text style / font / height,
bold, italic, underline, overline, strike, upper/lowercase and a colour
dropdown on top; oblique angle, width factor, character spacing, paragraph
alignment, justification, line spacing and OK/Cancel below. Character
toggles wrap the selection in DXF inline codes; global fields apply as
leading runs or entity properties.

Editing is shown in the editor's own area, not on the drawing: a Code/Preview
toggle switches between the raw text input and a rendered preview canvas that
draws the tessellated glyph strokes at real size, scrolling when the text
outgrows the view. The MText box keeps a fixed width so text wraps to the
next line instead of stretching into one line.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-31 01:51:20 +03:00
Hakan Seven de08bd598c feat: Implement in-place MText editor with formatting toolbar and live preview
- Added SVG icons for text formatting options (bold, italic, underline, overline, strike, uppercase, lowercase).
- Introduced a new `MTextEditorState` struct to manage the state of the MText editor.
- Implemented functionality to open the MText editor from the command line and edit existing MText entities.
- Added message handling for various MText editor actions (formatting, height, style, font, justification, alignment, line spacing).
- Created a dedicated preview area for MText rendering within the editor.
- Updated the command structure to support opening the MText editor and handling text input.
- Refactored existing MText command logic to integrate with the new editor.
2026-05-30 23:24:06 +03:00
Hakan SevenandClaude Opus 4.8 1b3760913e fix(selection): individual picks accumulate; Shift+click removes
Single-click picking cleared the selection first (exclusive), so clicking a
second object dropped the first (issue #47). Plain clicks now add to the
selection and Shift+click removes the picked entity; empty-space click and
Esc still clear. Shift state is tracked from keyboard ModifiersChanged
events since mouse click messages carry no modifiers.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-30 21:54:35 +03:00
Hakan SevenandClaude Opus 4.8 e4668c66e7 fix(zoom): Zoom Extents fits full drawing on first use
The viewport context-menu item dispatched a bare "ZOOM" string, which no
command branch handled, so it did nothing. Send "ZOOM EXTENTS" instead.

fit_all() also fed on the frustum-culled wire set, so it only framed the
entities already on screen — each invocation revealed a little more and the
view converged on the true extent only after several uses (issue #51). It
now builds an un-culled wire set at a fixed tessellation tolerance, so the
bounds are zoom-independent and the first call fits the whole drawing.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-30 21:40:48 +03:00
Hakan SevenandClaude Opus 4.8 09e7e90aae feat(draworder): cross-type draw order + context menu
DRAWORDER now affects rendering immediately (bump geometry epoch so the
sort cache rebuilds) and front/back anchor to the block's full effective
sort range instead of only existing table entries, so bring-to-front
actually lands on top. The render fallback sorts table-absent entities by
their own handle.

Draw order now applies across entity types, not just within a type. A
per-entity draw-order rank (SortEntitiesTable override or own handle,
ranked per block) is encoded as a small signed clip-z bias in the wire,
hatch, image, and 3DFACE pipelines. The shared LessEqual depth buffer then
orders fills, lines, rasters and 2D solids against each other regardless of
pass order — without touching model coordinates. 3D meshes keep real
geometric depth; wipeouts stay always-on-top.

Adds a Draw Order submenu to the viewport context menu (Bring to Front,
Send to Back, Bring Above/Send Under Object). The above/under items run an
interactive reference-object pick, then reorder the captured selection.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-05-30 21:29:01 +03:00
Hakan SevenandClaude Opus 4.7 7d9ac435cb feat(qselect): per-property filter with operator + value (#28)
Quick Select used to filter only on Object Type and Layer. The classic
QSELECT model — pick a type, pick one of its properties, compare with a
typed value via a logical operator — is much more general: a Line entity
can now be filtered on Start X, Length, Angle, Delta Y, …, and any
entity on the common Layer / Color / Linetype / Lineweight.

Panel layout: Object type → Property → Operator → Value → Append. The
Property dropdown is type-aware: it always lists the four common
properties; picking a specific Object type appends that type's
`geometry_properties` rows, sampled from the first entity of that type
in the active layout (so `Start X`, `Radius`, `Pattern Angle`, … only
appear when their entity type is selected). Changing the Object type
drops the Property when the new type no longer offers it.

Operators: `= Equals`, `!= Not equal`, `> Greater than`, `< Less than`,
`* Any value`. Eq/Neq do case-insensitive string compare against the
canonical property string (so "ByLayer" matches "bylayer"); Gt/Lt parse
both sides as `f64` and reject anything non-numeric; Any skips the
value test entirely. The Value text field is disabled when no Property
is picked or the operator is Any. Append behaves the same as before.

The new value reader is hand-rolled for the four common properties
(layer name, color formatted as `ByLayer`/`ByBlock`/`<aci>`/`r,g,b`,
linetype name, lineweight formatted as `0.30mm`) and falls back through
`geometry_properties()` for type-specific fields — extracting the
property's value out of the `PropValue` variant so EditText / Choice /
ColorChoice all canonicalise the same way the panel will display them.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-29 21:09:56 +03:00
Hakan SevenandClaude Opus 4.7 9db206f310 feat(select): Select Similar + Quick Select panel (#28)
Two ways to extend the current selection by entity type + layer:

* `Select Similar` (right-click → Select Similar, or `SELECTSIMILAR` /
  `SELSIM`): for every (type, layer) pair in the current selection, add
  every entity in the active layout that matches the pair. The seed
  selection stays selected; matches already selected are not
  double-counted.
* `Quick Select` (right-click → Quick Select…, or `QSELECT` / `QS`):
  opens a centred floating panel with Object-type and Layer pick_lists
  (populated only with values that actually exist in the active layout)
  and an "Append to current selection" checkbox. `(Any)` in either slot
  skips that filter. Apply replaces the selection (or extends it with
  Append on) and reports the match count on the command line. Cancel,
  Esc, and outside-click all dismiss without applying. When opened with
  a selection present, the panel pre-fills both filters from the first
  selected entity so QSelect doubles as an editable Select Similar.

Scoped to entities owned by the active layout's block-record so a paper
layout's QSELECT never reaches into model-space entities (and vice
versa). Entity-type names live in a single `entity_type_name` helper in
`entities::traits` to keep the UI strings and the filter keys aligned.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-29 20:30:56 +03:00
Hakan SevenandClaude Opus 4.7 cf925be861 fix(ctx-menu): anchor right-click menu under cursor, share overlay infra
The right-click context menu was being placed via window-relative
coordinates while the cursor position it anchored to was canvas-relative,
so the menu drifted away from the cursor by the size of the surrounding
ribbon / sidebar / status bar.

Move the menu inside `viewport_stack` so its anchor resolves in the same
coordinate space the cursor was captured in. Introduce
`position_canvas_overlay(anchor, panel)` — wraps `panel` in a column+row
of fixed-width spacers and `iced::widget::opaque(panel)` so events on the
menu itself don't fall through to the viewport mouse area underneath.
The multi-functional grip popup is rewritten to use the same helper.

Outside-click dismiss now mirrors the grip popup: `ViewportLeftPress`
checks `selection.context_menu.take()` at the top and returns early
when it was set, so a press that reaches the viewport mouse area
(i.e. outside the opaque panel) closes the menu without running the
rest of the left-click handler. The redundant `context_menu = None`
later in the handler and the now-unused `Message::ViewportContextMenuClose`
are removed.

Two related issues fixed in the same pass:

* The right-drag threshold for orbit was 3 px squared, small enough that
  normal hand jitter between a right-button press and release was
  promoting a click to a drag and suppressing the menu on release.
  Bumped to 8 px squared.
* `Message::ViewportExit` was clearing `context_menu`. With the menu now
  living above the viewport mouse area, the opaque panel capturing the
  cursor fires the underlying area's exit handler — which closed the
  menu the instant it opened. Stop clearing the menu there; outside-click
  dismiss is enough.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-29 15:58:23 +03:00
Hakan Seven 86ca1b7033 fix(grip): popup sizes to widest label + full-row highlight
The previous single-widget refactor left the column at
`Length::Shrink` and the buttons at `Length::Fill`, which iced
resolves to a zero-width column — the popup degenerated into a
1-pixel vertical line. Set the column to a fixed pixel width
derived from the longest item's label (and restore `width(Fill)`
on the buttons) so the selection highlight covers the full row
instead of just the text glyphs.

Also silences a `dead_code` warning on `GripMenuAction` variants
that the Phase 2 / 3 wiring will consume next.
2026-05-29 10:00:30 +03:00
Hakan Seven 391aa3a76f feat(grip): keyboard nav + click-outside dismiss + single-widget popup
- Popup items now render inside one bordered container with
  borderless buttons so the menu reads as a single widget instead
  of stacked tiles.
- Arrow Up / Down (and Tab) walk `grip_popup.selected` while the
  popup is open; Enter commits the highlighted item by re-emitting
  `GripMenuPick`; Escape dismisses without acting; the underlying
  handlers (`CommandHistoryPrev/Next`, `CommandFinalize`,
  `CommandEscape`, `DynTabNext`) keep their normal behaviour when
  no popup is open.
- A press inside the viewport that reaches the mouse_area (i.e. not
  on a popup item) closes the popup and consumes the click — the
  user's next press then starts a normal selection / drag.
2026-05-29 09:51:12 +03:00
Hakan Seven 595ee3e245 fix(grip): open hover popup without requiring mouse motion
Dwell timing only advanced when `ViewportMove` fired; a perfectly
still cursor never triggered the popup. Subscribe to animation
frames while `grip_hover` is set and dispatch `GripDwellTick` from
them so `update_grip_hover` re-checks the elapsed dwell each frame.
Subscription auto-stops as soon as the hover clears or the popup
opens.
2026-05-29 09:38:37 +03:00
Hakan Seven 6c1462932d feat(grip): multi-functional hover popup menu
Phase 2 of the grip overhaul: when the cursor dwells on a selected
entity's grip the overlay opens a popup menu with entity-specific
options (Stretch / Add Vertex / Remove Vertex / Convert to
Arc-Line / …). Picking an item dispatches `apply_grip_menu`; the
default `Stretch` entry is a no-op that lets the user click the
grip to start the normal drag.

- `GripMenuItem` + `GripMenuAction` (full vocabulary covering
  stretch, add/remove vertex, convert to arc/line, reverse arrows,
  text-position variants, hatch parameters, tangent direction).
- `Grippable` trait gains `grip_menu` + `apply_grip_menu` with
  trait-default `Stretch`-only impls so every existing entity gets
  the popup for free.
- `EntityTypeOps` dispatches both methods across the same entity
  list as the existing grip / property machinery.
- `OpenCADStudio` carries `grip_hover` (dwell tracker) +
  `grip_popup` (open menu). `update_grip_hover` runs from
  `ViewportMove`: hit-tests grips, refreshes the dwell timer, and
  opens the popup after 600 ms. Cursor drift past 80 px or a grip
  click dismisses it.
- `view.rs` paints the popup as an absolute-positioned column of
  buttons at the grip's screen anchor; each button publishes
  `Message::GripMenuPick(idx)`.
- `LwPolyline` overrides `Grippable` manually (skips the standard
  macro) so its vertex grip exposes Add/Remove Vertex and its
  segment-midpoint grip exposes Add Vertex + Convert to Arc/Line.
  `apply_grip_menu` performs the edits: vertex insertion at the
  chord midpoint inheriting the previous bulge, vertex removal,
  and bulge toggle for the arc/line conversion.
2026-05-29 09:30:54 +03:00
Hakan Seven 5755c2e130 refactor(grip): square-only marker set + oriented stretch handles
Phase 1 of bringing the grip vocabulary in line with the standard
CAD convention: drop the per-position-type shape encoding (Diamond
for translate, Circle for parameter) and use a plain square for
every endpoint / vertex / centre grip. The behavioural midpoint vs
vertex distinction stays — it lives on `GripDef::is_midpoint` and
drives `Translate` vs `Absolute` in the grip-edit dispatcher — only
the marker shape collapses.

- `GripShape` keeps `Square`, `Rectangle`, `Triangle`. Diamond /
  Circle are gone.
- `GripDef` gains `dir: Option<[f32; 2]>` — a world-XY direction
  vector consumed by `Rectangle` to orient the box along its
  segment.
- `entities::common` exposes `square_grip`, `center_grip` (same
  square marker, flagged as translate), `rectangle_grip(id, world,
  dir)` for oriented mid-segment handles, and a kept-for-Phase-2
  `triangle_grip`.
- `overlay::GripMarker` / `grips_to_screen[_paper]` thread `dir`
  through to the canvas painter; the Rectangle path rotates the
  box around the grip centre using the world-XY direction (flipping
  the sin for screen-Y).

Every existing `diamond_grip(id, w)` call becomes `center_grip(id,
w)` — the visual change is square-replacing-diamond at line
midpoints / arc midpoints / circle centres / ellipse centres /
viewport / hatch loop centroids / dimension grips / image corners /
multileader / ray / underlay / ole2frame / solid3d.

`LwPolyline` grows a stretch handle on every segment (straight or
arc), drawn as the new oriented rectangle. The grip-edit path
translates both segment endpoints for straight segments and
adjusts the arc bulge from the new midpoint for arc segments.
2026-05-29 02:07:34 +03:00
Hakan SevenandClaude Opus 4.7 f952fc9bb7 fix(dyn-input): support cartesian coords via "," (#35)
Typing \`,\` while DYN was on appended the character to the focused
field's buffer (treated as a European decimal point), so users
couldn't switch to cartesian coordinates mid-command. The dynamic
input was stuck in polar \`[Distance, Angle]\` whenever a base point
existed.

\`,\` now acts as the coordinate separator (matching the AutoCAD
convention): pressing it locks the current field's buffer, then
either advances within the existing cartesian set or reshapes a
polar configuration into cartesian:

- \`[Distance, Angle]\` + \`,\` on the first field → \`[X(buf), Y]\`
- \`[X, Y]\` + \`,\` on Y → \`[X, Y, Z]\`
- otherwise advances to the next field (Tab-equivalent)

\`sync_dyn_fields\` accepts both polar and cartesian shapes as valid
\`Point\` configurations so a mouse-move after the reshape doesn't
revert to polar. \`dyn_resolve_point\` and the overlay's live-value
formatter treat cartesian inputs as RELATIVE to the base point when
one exists — matching the DYN-on relative-coords convention from
#26. \`DynComponent::Z\` plus its label / value paths fill out the
3-D variant.

Closes #35

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-29 00:46:18 +03:00
Hakan Seven 39eab9a4c4 feat(viewport): draggable Model-tile dividers
Tiled Model layouts now show the inner edges between panes as 2-px
dividers that act as resize grips: cursor on a divider → resize cue,
press + drag moves the edge (with every tile sharing that edge
updating together), release runs a collapse pass that absorbs any
tile that fell below the viewcube-comfort minimum into the neighbour
with the longest shared contact edge — drag a divider all the way
across to remove a pane.

- Scene grows `model_tile_edges`, `hit_model_tile_edge`,
  `move_model_tile_edge`, `collapse_small_model_tiles`. Edges are
  derived from the existing `model_tiles` rects (no extra storage).
- App carries a `tile_drag` Option; `ViewportLeftPress` starts it
  ahead of tile activation / picking, `ViewportMove` drives the
  edge, `ViewportLeftRelease` collapses + clears.
- `SelectionCanvas` draws the dividers and switches the cursor to
  `Resizing{Horizontally, Vertically}` over them.
2026-05-28 18:42:39 +03:00
Hakan SevenandClaude Opus 4.7 8d64426087 refactor(viewport): unify paper into one shader, transparent clear
Collapse the paper-space rendering path into the same shader widget the
model layout uses. The PaperCanvas widget draws the paper sheet, paper
entities and viewport borders underneath; the unified shader paints all
content viewports on top, scissored to each viewport's rect.

- Drop PaperViewportPane / PaperViewportPipeline newtype /
  PaperViewportPrimitive, ViewportPaneMode::Paper, ViewportPane::paper,
  build_primitive and build_viewport_primitive. Scene exposes only
  build_viewports.
- view.rs: the paper branch stacks PaperCanvas + the unified shader
  instead of the old paper_canvas_view function. The active paper
  viewport's blue border, render-mode picker and ViewCube hit-area are
  layered in the same viewport_stack as the model layout.
- Transparent shader clear (alpha=0) + alpha-blended blit pipeline so
  the underlying widget (container bg in model, PaperCanvas sheet in
  paper) shows through everywhere the shader has not painted geometry —
  including inside content viewport rectangles.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-28 10:03:01 +03:00
Hakan SevenandClaude Opus 4.7 417a708275 feat(viewport): tile-relative input + overlays in unified model
Route every per-frame interaction through the active model tile's screen
rectangle so a tiled VPORTS layout behaves like independent panes while
still sharing one shader widget:

- pick / draw / snap / box+lasso: cursor mapped to tile-local space; the
  hover-activates-tile rule on ViewportMove keeps the active tile under
  the cursor, so the picked camera matches what the user sees.
- pan: switched to Camera::pan_screen(dx, dy, viewport_height) so the
  world-per-pixel rate matches the active tile's height instead of the
  full canvas.
- grid: clipped via Frame::with_clip to the active tile rect; world→screen
  adds the tile origin so the lines align with the panel.
- UCS icon: origin offset by the tile rect so the gizmo sits in the
  active tile's bottom-left corner.
- render-mode picker (+ split buttons) and ViewCube hit-area: positioned
  via active_model_tile_bounds with leading Spaces, mirroring the
  floating-viewport pattern.
- ViewCube hover: driven by the CursorMoved/ViewportMove handlers into a
  new Scene::viewcube_hover cell, so the highlight survives the cube
  hit-area overlay masking events from shader::Program::update.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-28 09:45:19 +03:00
Hakan SevenandClaude Opus 4.7 c45146bbda feat(viewport): model-space tiled split (▤ / ▥ buttons)
Model layout artık tiled viewport destekliyor. Scene.model_tiles
normalized ekran rect'i + kamera tutan tile listesi; active_viewports
model layout'ta her tile için bir ViewportInstance üretiyor (aktif tile
canlı scene.camera'yı, inaktif tile'lar saklı snapshot'ı kullanıyor).

Render-mode picker'ın yanına iki buton eklendi: ▤ aktif tile'ı yatay
(üst/alt), ▥ dikey (sol/sağ) ikiye böler. split_active_model_tile
bölmeyi yapıyor; her iki yarı aktif kameranın kopyasını alıyor.

set_active_model_tile_at (cursor → aktif tile, kamera swap) ve
reset_model_tiles eklendi ama henüz bağlanmadı — aktif tile cursor
seçimi + pick yönlendirme sonraki adımda.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 22:33:36 +03:00
Hakan SevenandClaude Opus 4.7 eeb7df5fec fix(paper): make active-viewport picker & ViewCube clickable
Picker ve ViewCube paper_canvas içinde gömülü olduğundan tümüyle
viewport mouse_area'nın altında kalıyor ve tıklamaları o yakalıyordu.
İkisi de ana view stack'ine, mouse_area'nın üstüne taşındı; aktif
viewport'un ekran dikdörtgeninin köşelerine konumlandırılıyor.

- model picker artık yalnızca model layout'ta gösteriliyor (paper'da
  viewport kendi picker'ını alıyor)
- ViewCube hit alanı aktif viewport rect'inin sağ üst köşesinde
- CursorMoved ve ViewportClick, ViewCube hit-test'ini full canvas yerine
  aktif viewport rect'ine göre eşliyor; böylece paper'da gizmo ile
  tıklama hizalı
- picker/viewcube dışı tıklamalar alttaki mouse_area'ya geçtiği için
  viewport içi seçim/çizim etkilenmiyor

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 18:17:10 +03:00
Hakan SevenandClaude Opus 4.7 a41a05baa1 feat(paper): render-mode picker + ViewCube on active viewport
Paper-space'de bir viewport'a çift tıklanıp aktif hale gelince, model
space'deki gibi sol üst köşede render-mode dropdown'ı ve sağ üst köşede
ViewCube gösteriliyor.

- render_mode_picker helper'a çıkarıldı; model space ve aktif paper
  viewport ortak kullanıyor (kod tekrarı yok)
- SetRenderMode artık viewport-aware: aktif viewport varsa o viewport
  entity'sinin render_mode'unu, yoksa model-layout tab stilini günceller
  (Scene::active_viewport_render_mode getter/setter)
- build_viewport_primitive'e show_viewcube parametresi; aktif viewport
  primitive'i ViewCube gizmo'sunu çiziyor
- PaperViewportPane shader::Program artık Message'a özelleşti ve sol
  tık ViewCube hit-test'ini bounds-relative yapıp ViewCubeSnap üretiyor

Bilinen sorun: picker/viewcube doğru konumda görünüyor ancak tıklamalar
üstteki viewport_mouse mouse_area katmanına gidiyor; katman sırası bir
sonraki adımda düzeltilecek.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 18:05:09 +03:00
Hakan SevenandClaude Opus 4.7 fda0f2dbb9 refactor(ui): remove the viewport navigation bar
Sağ kenardaki dikey orbit/pan/zoom/zoom-extents araç çubuğu tümüyle
kaldırıldı: viewport render'ı, overlay::nav_toolbar fonksiyonu,
show_navbar state + ToggleNavbar mesajı/handler'ı, NAVBAR komutu, View
şeridindeki "Navigation Bar" aracı ve navbar modülü/ikonu. Aynı
gezinme işlemleri komutlar (3DORBIT, PAN, ZOOM*) ve ViewCube üzerinden
erişilebilir durumda.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 17:49:23 +03:00
Hakan SevenandClaude Opus 4.7 6483566c5d fix(dyn-input): capture numpad 8/2 as digits, not history nav
NumLock açıkken Numpad8 / Numpad2 logical key olarak ArrowUp / ArrowDown
geliyor ama text "8" / "2" taşıyor. Klavye aboneliğinde ok/geçmiş
arm'ları bu tuşları yakaladığı için dinamik girişte 8 ve 2 yazılamıyordu.
Printable glyph üreten her tuş artık navigation arm'larından önce, text
alanı üzerinden yakalanıyor; whitespace/control üreten tuşlar (Space,
Enter, Tab) named handler'lara düşmeye devam ediyor. Gerçek ok tuşları
(text üretmez) geçmiş gezinmesini korur.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 17:36:06 +03:00
Hakan SevenandClaude Opus 4.7 4abde6001a feat(dyn-input): TAB-editable distance/angle/coordinate fields
Dinamik giriş artık cursor yanında düzenlenebilir kutular gösteriyor.
TAB ile kutular arası geçilir, bir kutuya yazınca o kutu kilitlenir
(değer sabitlenir), diğerleri cursor'ı takip etmeye devam eder, Enter
ile çözülen nokta komuta point-pick olarak iletilir.

- DocumentTab.dyn_fields / dyn_active state; DynComponent + DynFieldEntry
- sync_dyn_fields komutun dyn_field() + base noktasına göre kutu kümesini
  türetir: nokta+base → [d, açı], nokta+base yok → [X, Y], radius → [d],
  açı → [açı]
- klavye yönlendirme: rakam/./-/+ aktif kutuya, TAB sonraki kutuya,
  Enter try_dyn_commit, Backspace kilidi açar
- dyn_capturing iken command-line text alanı on_input'suz bırakılıyor
  ki focus'u ve tuşları çalmasın; girişler global key aboneliğinden
  geliyor (numpad dahil text alanı üzerinden yakalanıyor)
- çoklu-kutu overlay render: aktif kutu vurgulu, kilitli kutu farklı
  kenar rengi

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 14:08:57 +03:00
Hakan SevenandClaude Opus 4.7 1fc73ba8b0 fix(dyn-input): correct Y coordinate, label by prompt type — #27
DYN overlay artık ekran-yukarı için world Y'yi (w.y) gösteriyor; eskiden
w.z kullanıldığı için Z-up sahnede top/3D görünümde Y hep 0 görünüyordu
(#27).

Ayrıca overlay etiketi aktif komutun o anki sorusuna göre biçimleniyor:
yeni DynField (Point / Distance / Angle) trait metodu eklendi. CIRCLE
yarıçap adımında 'd=...', ROTATE açı adımında '<...°', diğer nokta
adımlarında koordinat / polar (d + açı) gösteriliyor.

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-27 13:41:54 +03:00