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.
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.
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.
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 <noreply@anthropic.com>
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.
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Replaces the binary Wireframe/Solid toggle with a 7-mode dropdown
that maps onto acadrust's `ViewportRenderMode` enum: Wireframe 2D /
Wireframe 3D / Hidden Line / Flat Shaded / Gouraud Shaded / Flat
Shaded + Edges / Gouraud Shaded + Edges. Per-tab state lives on
`DocumentTab::render_mode`; the old `SetWireframe` message forwards
to `SetRenderMode` so the ribbon + WIREFRAME / SOLID command line
keep working.
`scene::render::render_mode_flags()` translates the mode into three
pipeline-facing booleans (`face3d_fill`, `mesh_fill`,
`show_3d_edges`); `mesh_fill` / `show_3d_edges` are stubs that the
upcoming phases will consume. `Primitive` carries the derived
`view_wireframe` plus the two new fields through to `prepare()`.
`Pipeline::upload_face3d` takes a `wireframe_only` flag that drops
3DFACE quad fills (built from `key_vertices`) while keeping the
`all_wires.fill_tris` channel — that channel carries text-LOD greek
rectangles and PolyfaceMesh face triangles which must stay visible
in wireframe mode. 3DFACE edges stay on regardless of mode.
Solid3D mesh wireframe rendering, Hidden Line z-prepass, and Flat /
Gouraud shader uniforms come in phases 1–4.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Type-as-you-go suggestion popup above the input that filters known
command names by substring (so `LEADER` surfaces both LEADER and
MLEADER). Up / Down navigates the list, Enter dispatches the
highlighted entry, clicking a row dispatches directly. Esc on a
non-empty input clears the buffer instead of escalating to
command-cancel.
The name corpus comes from `inventory::submit!` blocks placed next
to each `CadCommand` impl rather than a hand-maintained static list.
Adding a new interactive command means dropping one `inventory::submit!`
line in its own module file — the autocomplete picks it up at the
next compile. 98 submissions seeded across 60 module files;
`command::all_registered_command_names()` flattens them at runtime.
One-shot dispatch arms in `app/commands.rs` (NEW, OPEN, SAVE, UNDO,
…) don't have a `CadCommand` impl and are therefore absent from
autocomplete by design.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The overlay used to keep the last four history lines on screen
indefinitely, leaving stale command output cluttering the viewport.
Stamp each `HistoryEntry` with its `Instant` of insertion and filter
the rows shown on the overlay to entries younger than 3 s.
`CommandLine::has_visible_history()` gates a per-frame tick
subscription so the view re-renders and the entry disappears
on time. The subscription auto-stops once no entry is fresh enough,
keeping the idle CPU draw at zero (#18).
Full history stays in the backing `Vec` — the upcoming dropdown
button will surface it on demand.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The command-line panel used to consume a full-width row at the bottom
of the main column, taking vertical space away from the drawing area
even when no command was active. Move it onto the viewport stack as a
fixed-width 720 px pill aligned to the bottom-centre, with rounded
corners so it reads as a floating element. The status bar still owns
the very bottom of the window.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
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 <noreply@anthropic.com>
Joining the OpenAEC Foundation (open-aec.com). Crate/struct become
OpenCADStudio, user-facing strings use the spaced "Open CAD Studio"
form, repo URLs point to HakanSeven12/open-cad-studio. Logo (window
icon + assets/logo.svg) redrawn with OCS initials. Drop examples/
binaries — unused.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Wide LWPolyline/Polyline2D fill bands now sit on the centerline:
- polyline_segment_fill arc center matched explode.rs / acadrust convention
(signed d_perp = r·(1-b²)/(1+b²), handles major arcs correctly)
- per-vertex hw0/hw1 taper applied along arc; tess density 24/circle
- synced_hatch_models subtracts hatch_offset from band boundary so it lives
in the same offset-rel space as the wire tessellation
LWDISPLAY:
- new command `LWDISPLAY [ON|OFF]` toggles header.lineweight_display
- LWT status-bar pill (between ORTHO and POLAR) drives the same flag
- bump_geometry() on change so the viewport re-tess immediately
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The update notice dialog now mirrors what GitHub publishes: side-by-side
Installed → Latest version cards (the new one accent-tinted to draw the
eye) and a scrollable release-notes panel under them. Light markdown
rendering covers `##` headings, bullet lists, and inline bold/code
markers — enough for the GitHub release body format we ship.
- update_check::check_for_update() returns UpdateInfo { version, body };
body is parsed from the releases API JSON with a tiny custom
string-field extractor (\n / \" / \u handling) so we keep the
serde_json-free dependency footprint.
- Window grows from 480x240 to 560x460 and becomes resizable to make
room for the notes scroller.
- Layout tightens: header bottom padding drops 14 → 6, notes heading
10, and the notes block is wrapped in a Fill-height container so it
claims every leftover pixel — no more dead space between heading and
footer.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
A pinned "Start" tab now lives at index 0. It cannot be closed, opens
on launch, and replaces the model-space viewport with a welcome page —
similar to Thunderbird's first-run screen.
- Boot opens with only the Start tab; Drawing1 is no longer auto-spawned.
- The Properties left panel becomes a Recent Documents rail on the
Start tab. Recents persist to disk
(%APPDATA%/H7CAD/recent.txt on Windows, $XDG_CONFIG_HOME/H7CAD/recent.txt
on Linux, ~/Library/Application Support/H7CAD/recent.txt on macOS), up
to 20 entries, saved on every open and rehydrated at boot.
Each row opens or removes the file in place.
- The Support ribbon group (Donate / Report / About / Changelog) is
retired; the same actions surface on the welcome page instead. The
Donate button is the primary call-to-action — solid #B03020 brand
fill with a soft brand-tinted ambient glow halo behind the action row
(Thunderbird-style coloured glow against the neutral page).
- Drawing-only overlays (mouse-area, viewcube, navbar, dynamic input,
selection markers) are suppressed on the Start tab so its clicks
don't trigger CAD commands. OpenFile's "current_is_empty" check
refuses to replace the Start tab, and TabClose ignores requests on
it.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
H7CAD was subscribing to window::frames() unconditionally, so iced kept
firing Message::Tick at the display refresh rate even when nothing on
screen was changing. That re-ran update() and the view-tree builder ~60
times a second and showed up as 2-3% idle CPU on Windows.
Gate the subscription on `self.opening.is_some()` (the only thing that
actually needs per-frame redraws today — the file-open progress
indicator). Camera-change detection moves with the events that drive it
since user input already wakes iced.
Closes#18
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a one-shot update check that runs on app boot and pops up a small
"Update Available" window when the GitHub releases API reports a newer
tag than this build's `CARGO_PKG_VERSION`.
- `src/update_check.rs` — async wrapper around a blocking `ureq` GET to
`api.github.com/repos/HakanSeven12/H7CAD/releases/latest`, returns
`Some(version)` when a newer release exists, `None` otherwise (silent
on network / parse failure so a missing connection never blocks
launch).
- `H7CAD::boot` chains a `Task::perform` next to the main-window open
so the check runs in parallel with first-paint.
- New window plumbed like the other floating panels (page setup, about,
etc.): `update_notice_window`/`update_notice_version` state, title
entry, `OsWindowClosed` cleanup, and a `ui::update_notice::view_window`
modal that shows installed vs. latest version plus "Later" /
"Open Release Page" buttons. Open routes through `open::that` to the
releases URL and dismisses the modal.
Dependency: adds `ureq = { version = "2", default-features = false,
features = ["tls"] }` — the smallest blocking HTTP client + TLS combo
that doesn't drag in an async runtime.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Big DWG/DXF files block the UI for seconds during parse + cache build,
leaving the user wondering whether the app froze. Split the file-open
flow into pick → load and show a modal overlay during the load: file
name, size, phase label (Reading / Parsing / Building caches /
Finalizing), animated indeterminate bar, and Cancel button.
Phase is tracked via a shared AtomicU8 written by the loader thread
and read by the overlay each frame. Cancel hides the overlay and
discards the result; the parser thread runs to completion in the
background since acadrust has no interrupt hook.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Scale popup dropdown replaces cycle behavior; paper-space viewport scale
editable when a viewport is active/selected
- CANNOSCALE persisted via DXF $USERR1 (read on open, write on save)
- PSLTSCALE: linetype dash lengths scaled by viewport anno_scale so dashes
appear uniform physical size in paper space
- MultiLeader: arrow size, dogleg length, and text height scale with
anno_scale; leader line paths (world coords) stay fixed
- Leader: arrow size and hookline length scale with anno_scale; also
corrects world_offset subtraction that was previously deferred to the
TruckObject::Lines path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements a full in-app file browser window for Save As, replacing the
rfd dialog that could not expose the selected filter index on Linux.
The new chooser includes directory navigation, a scrollable file list,
filename input, and a format drop-down (DWG/DXF × 2018/2013/2010/2007/2004/2000/R14).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rfd does not expose the selected filter index, so the old DWG/DXF version
filters had no effect — every file was always saved as AC1032 (R2018).
Replace with a single custom iced Save As window that shows:
- File name text input
- Save-in folder input + Browse button (rfd folder picker)
- Format dropdown: DWG/DXF × 2018/2013/2010/2007/2004/2000/R14
- Save / Cancel buttons
The selected format is passed to save_as_version() which temporarily sets
doc.version before writing, so DwgWriter/DxfWriter emit the correct file
format. Also works in the unsaved-changes Save flow.
Closes#9
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Grip points in paper space now use the 2-D linear to_px transform
(tx/ty/ortho_size) instead of the 3-D view_proj matrix, matching the
coordinate system used by PaperCanvas
- world_offset is no longer subtracted from paper-space entity grip
positions — paper-space coordinates are already unoffset (same rule
as wire tessellation in wires_for_block)
- MTEXT v_offset corrected for all attachment points: CXF glyphs sit
on the baseline and extend upward, so Top attachment needs v_offset=-h,
Bottom needs (n_lines-1)*line_h, Middle is the midpoint of the two
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Opens a floating OS window when the user tries to close a dirty tab or
quit with unsaved changes. The main window stays alive until the dialog
is resolved — Cancel aborts the close entirely. For quit with multiple
dirty tabs the dialog loops through each one individually.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NAVVCUBE: propagate show_viewcube flag through ViewportPane and the wgpu
Primitive so the ViewCube pipeline skips upload/render when hidden.
UCSICON: bare command (no subcommand) now toggles current state instead
of printing usage info.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Integrate NAVVCUBE, NAVBAR, PROPERTIES, FILETAB, LAYOUTTAB, TOOLPALETTES
and SHEETSET commands to toggle UI widgets at runtime. Add 15 new SVG icons
for view module tools previously using glyph fallbacks. Restyle all SVG
icons to use the established two-color scheme: #e0e0e0 for passive geometry,
#4cc9f0 for active/accent elements.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Hide OS crosshair and drawn crosshair when cursor is over ViewCube area
- Enhanced hover glow in WGSL shader (blue tint + brightness boost)
- Fixed region_centroids() Y-axis sign for all FRONT/BACK-related regions
- Swapped corner geometry assignments (TPF↔TBK, BTF↔BBK) to match
intended FRONT/BACK semantics (sy=-1 = FRONT, sy=+1 = BACK)
- Corrected all snap_angles: yaw_pitch_to_quat(0,0) places eye at -Y
(FRONT view), so FRONT=0 and BACK=PI — cascaded through edges/corners
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- paper_limits() falls back to A4 landscape (297×210mm) even when no
Layout object exists in the document
- paper_bg_color default changed to white [1,1,1,1] (paper surface color)
- PaperCanvas::draw() fills canvas with dark desk color first, then draws
the paper area rectangle in paper_bg_color so the sheet is clearly
visible against the desk background
- view.rs container background uses the fixed desk color instead of
paper_bg_color so it no longer conflicts with the paper-surface color
- BACKGROUND RESET for paper space now resets to white instead of dark gray
- Removed unused PaperSheet variant, paper_sheet() constructor,
build_paper_sheet_primitive() and associated match arms (dead code
after 2D canvas replacement)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- viewport_screen_rect now mirrors PaperCanvas::draw()'s camera-based
to_px transform instead of using a fixed paper-limits scale, so the
3D overlay lands exactly over the drawn viewport border at any zoom/pan
- blue border frame (stack layer above the shader) makes the active
viewport boundary always visible even when it fills the canvas
- space-mode pill in MSPACE now dispatches LayoutSwitch("Model") so
clicking it jumps to full Model Space instead of just exiting to PSPACE
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Paper space is now rendered as a 2D vector canvas (iced::widget::canvas)
instead of a 3D shader widget. This allows direct interaction with
paper-space entities (title blocks, viewport borders, annotations)
without needing to enter MSPACE first.
Changes:
- src/scene/paper_canvas.rs: new PaperCanvas<'a> canvas::Program that
renders paper wires (with linetype dashes), solid/gradient hatch fills,
and wipeout backgrounds using the paper camera's orthographic transform.
- Scene: three new public helpers (paper_canvas_wires, paper_canvas_hatches,
paper_canvas_wipeouts) feed the canvas with the same data the old shader
used, including inactive viewport projections and interim/preview wires.
- view.rs: paper_canvas_view() now wraps PaperCanvas instead of
shader(ViewportPane::paper_sheet(…)). The PaperViewportPane 3D shader
overlay for the active MSPACE viewport is unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the static MODEL/LAYOUT pill with an interactive button that
shows and toggles the current editing context:
Model tab → "MODEL" (non-clickable, informational)
Layout PSPACE → "PAPER" (click → MspaceCommand: enter MSPACE)
Layout MSPACE → "MODEL" (click → ExitViewport: return to PSPACE)
Highlights in blue when in MSPACE to signal active model editing.
Mirrors the AutoCAD status-bar convention for MODEL/PAPER switching.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a paper-space viewport is activated (double-click / MSPACE command),
it is now rendered through its own dedicated PaperViewportPipeline rather
than the CPU-projection approach used for inactive viewports.
Architecture:
- PaperSheet widget (full-canvas): renders paper entities + CPU-projected
content of all viewports *except* the active one.
- PaperViewportPane widget (Fixed w×h, positioned at viewport screen rect):
renders the active viewport with a true 3D camera derived from the
viewport's view_direction/view_target/view_height.
PaperViewportPipeline is a newtype of Pipeline. Having a distinct type
gives it its own Iced storage entry (keyed by TypeId), preventing the
shared prepare() overwrite that broke the earlier per-viewport approach.
Changes:
- render.rs: PaperViewportPipeline, PaperViewportPrimitive newtypes;
build_active_viewport_primitive(); build_paper_sheet_primitive() now
excludes active_viewport from CPU projection.
- viewport_pane.rs: PaperViewportPane<'a> with shader::Program impl.
- mod.rs: viewport_content_wires() gains exclude_vp parameter;
viewport_screen_rect() is no longer dead code.
- view.rs: paper_canvas_view() overlays PaperViewportPane when MSPACE
is active.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Iced 0.14 batches all shader prepare() calls before any render() calls.
Multiple ViewportPane widgets sharing the same Pipeline type overwrite
each other's GPU buffers, causing the PaperSheet layer (full-canvas) to
render the last viewport's model content — appearing full-screen.
Fix: paper_canvas_view() now renders a single full-canvas PaperSheet
widget. build_paper_sheet_primitive() is updated to include model-space
content projected through each viewport's view matrix (via
viewport_content_wires()), restoring the original working behaviour.
ViewportPane::Paper mode and viewport_screen_rect() are kept with
#[allow(dead_code)] — a per-viewport wgpu sub-renderer that accumulates
data across frames could revive them in the future.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove Scene: shader::Program<Msg> impl; all GPU rendering now goes
through ViewportPane which supports three modes:
Model — full model-space view (unchanged behaviour)
PaperSheet — paper-space entities only, using the paper camera
Paper — model content through a specific viewport's own camera
- Add Scene helper methods (render.rs):
build_primitive / build_paper_sheet_primitive / build_viewport_primitive
update_viewcube_state / viewcube_mouse_interaction
- Add Scene helpers (mod.rs):
paper_sheet_wires() — paper entities without viewport projection
camera_for_viewport() — derive Camera from Viewport entity data
model_wires_for_viewport() — model wires filtered by per-viewport frozen layers
viewport_screen_rect() — paper-space → pixel rect mapping
- Add paper_canvas_view() in view.rs:
Stack of PaperSheet + one Paper widget per Viewport entity,
positioned with Space offsets from viewport_screen_rect()
- Update VIEWPORT_WIDGET_PLAN.md: mark Steps 1–4 done, document remaining work
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- REPORT opens GitHub new-issue page
- ABOUT opens a dialog window with version, OS and arch info + Copy Info button
- CHANGELOG opens GitHub releases page
- Fix Windows build: add windows-sys as target-specific dependency for PRINT command
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each dialog window (Page Setup, DimStyle, TextStyle, TableStyle,
MLineStyle, PlotStyle, Layout Manager, Shortcuts) is now a proper
window-filling layout in its own src/ui/<dialog>.rs file.
Pattern: toolbar strip at top + vertical separator + scrollable
content filling the rest — no modal card, no backdrop, no stack[].
Matches the existing Layer Manager window pattern.
Removed ~1600 lines of old overlay functions from view.rs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Every dialog panel that previously rendered as an in-viewport overlay is
now opened as its own OS window, matching the behaviour of the Layer
Properties Manager:
• Page Setup (520×460, non-resizable)
• Text Style (620×460)
• Table Style (620×420)
• Multiline Style (620×420)
• Layout Manager (640×320)
• Plot Style Editor (780×540)
• Dimension Style Mgr (720×560)
• Keyboard Shortcuts (720×520)
Changes:
- mod.rs: replace *_open: bool fields with *_window: Option<window::Id>;
add window IDs to OsWindowClosed tracker; add per-window title strings.
- update.rs: Open handlers call window::open(); if already open, call
window::gain_focus() instead of reopening. Close handlers call
window::close(). OsWindowClosed clears all panel window IDs.
- view.rs: each window_id is matched at the top of view() and returns
the panel content directly. All overlay let-bindings and stack![]
references removed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Opens a reference panel listing all built-in keyboard shortcuts
(F3-F12, Ctrl+N/O/S/Z/C/X/V, etc.) and any user-defined overrides.
SHORTCUTS LIST — open the panel
SHORTCUTS SET <k> <c> — add/update a custom shortcut label
SHORTCUTS CLEAR <k> — remove a custom entry
Custom shortcuts are stored in app state (runtime only) and shown
in the panel alongside the hardcoded bindings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Panel overlay showing all layouts with their active status.
Supports: select, rename, new, delete, reorder (◀▶), set current.
Also adds Scene::swap_layout_order() for tab-order resequencing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>