feat(ui): migrate the last dialogs to modals — fully single-window (Plan B)

Move the unsaved-changes prompt, the Save As browser and the
file-association prompt onto `active_modal` / `modal_content`, reusing
their existing open/close helpers (now just set/clear active_modal). The
daemon `view` no longer dispatches per window — every dialog is an
in-canvas modal, so the app is single-window on native and web alike.

`close_active_modal` handles their ✕: the unsaved prompt cancels the
pending close, the association prompt records that it was answered.

Native + web build; 75 tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Hakan Seven 2026-06-16 23:59:38 +03:00
commit 94b906d95b
3 changed files with 56 additions and 81 deletions

View file

@ -606,6 +606,9 @@ pub enum ModalKind {
MlStyle,
MLeaderStyle,
DimStyle,
Unsaved,
SaveDialog,
AssocPrompt,
}
/// Identifies a DimStyle field that can be edited in the dialog.
@ -1687,18 +1690,10 @@ impl OpenCADStudio {
// One-time prompt offering to make Open CAD Studio the default app for
// .dwg / .dxf. Shown only on the first launch that hasn't answered it
// yet; the flag is persisted so we never ask twice.
let assoc_prompt: Task<Message> = if s.default_assoc_prompted {
Task::none()
} else {
let (id, open) = window::open(window::Settings {
size: iced::Size::new(440.0, 210.0),
resizable: false,
level: window::Level::AlwaysOnTop,
..Default::default()
});
s.assoc_prompt_window = Some(id);
open.map(|_| Message::Noop)
};
let assoc_prompt: Task<Message> = Task::none();
if !s.default_assoc_prompted {
s.active_modal = Some(ModalKind::AssocPrompt);
}
(
s,
Task::batch([open_main, check_update, focus_cmd, cli_open, assoc_prompt]),