feat(ui): migrate 4 more dialogs to in-canvas modals (Plan B)
Consolidate all migrated dialogs into a single `modal_content` builder matched on `active_modal`, rendered by view_main. Move Layers, Page Setup, Layout Manager and Plot Style off OS windows onto it (their construction relocated from the daemon `view` dispatch, which now only handles the not-yet-migrated dialogs). Each keeps a bounded size. Open/close handlers set `active_modal` instead of opening/closing windows; ToggleLayers toggles it. Native + web build; 75 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
6a3663b3f1
commit
b21d10f6e8
3 changed files with 90 additions and 144 deletions
|
|
@ -597,6 +597,10 @@ pub enum ModalKind {
|
|||
Shortcuts,
|
||||
PluginManager,
|
||||
UpdateNotice,
|
||||
Layers,
|
||||
PageSetup,
|
||||
LayoutManager,
|
||||
Plotstyle,
|
||||
}
|
||||
|
||||
/// Identifies a DimStyle field that can be edited in the dialog.
|
||||
|
|
|
|||
|
|
@ -1600,24 +1600,14 @@ impl OpenCADStudio {
|
|||
}
|
||||
|
||||
Message::ToggleLayers => {
|
||||
if let Some(id) = self.layer_window.take() {
|
||||
// Close path: `OsWindowClosed`'s deactivate guard
|
||||
// sees `layer_window == None` after the take(), so do
|
||||
// it here so the button flips off in the same frame
|
||||
// (#40).
|
||||
if self.active_modal == Some(super::ModalKind::Layers) {
|
||||
self.ribbon.deactivate_tool_if("LAYERS");
|
||||
window::close(id)
|
||||
self.active_modal = None;
|
||||
} else {
|
||||
self.sync_ribbon_layers();
|
||||
let (id, task) = window::open(window::Settings {
|
||||
size: iced::Size::new(900.0, 360.0),
|
||||
resizable: true,
|
||||
level: window::Level::AlwaysOnTop,
|
||||
..Default::default()
|
||||
});
|
||||
self.layer_window = Some(id);
|
||||
task.map(|_| Message::Noop)
|
||||
self.active_modal = Some(super::ModalKind::Layers);
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::WindowCloseRequested(id) => {
|
||||
|
|
@ -5299,24 +5289,12 @@ impl OpenCADStudio {
|
|||
} else {
|
||||
current
|
||||
};
|
||||
if let Some(id) = self.layout_manager_window {
|
||||
return window::gain_focus(id);
|
||||
}
|
||||
let (id, task) = window::open(window::Settings {
|
||||
size: iced::Size::new(640.0, 320.0),
|
||||
resizable: true,
|
||||
level: window::Level::AlwaysOnTop,
|
||||
..Default::default()
|
||||
});
|
||||
self.layout_manager_window = Some(id);
|
||||
task.map(|_| Message::Noop)
|
||||
self.active_modal = Some(super::ModalKind::LayoutManager);
|
||||
Task::none()
|
||||
}
|
||||
Message::LayoutManagerClose => {
|
||||
if let Some(id) = self.layout_manager_window.take() {
|
||||
window::close(id)
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
self.active_modal = None;
|
||||
Task::none()
|
||||
}
|
||||
Message::LayoutManagerSelect(name) => {
|
||||
self.layout_manager_rename_buf = if name == "Model" {
|
||||
|
|
@ -5739,24 +5717,12 @@ impl OpenCADStudio {
|
|||
};
|
||||
self.page_setup_w = format!("{w:.1}");
|
||||
self.page_setup_h = format!("{h:.1}");
|
||||
if let Some(id) = self.page_setup_window {
|
||||
return window::gain_focus(id);
|
||||
}
|
||||
let (id, task) = window::open(window::Settings {
|
||||
size: iced::Size::new(520.0, 460.0),
|
||||
resizable: false,
|
||||
level: window::Level::AlwaysOnTop,
|
||||
..Default::default()
|
||||
});
|
||||
self.page_setup_window = Some(id);
|
||||
task.map(|_| Message::Noop)
|
||||
self.active_modal = Some(super::ModalKind::PageSetup);
|
||||
Task::none()
|
||||
}
|
||||
Message::PageSetupClose => {
|
||||
if let Some(id) = self.page_setup_window.take() {
|
||||
window::close(id)
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
self.active_modal = None;
|
||||
Task::none()
|
||||
}
|
||||
Message::UpdateCheckResult(latest) => {
|
||||
let Some(info) = latest else {
|
||||
|
|
@ -6232,24 +6198,12 @@ impl OpenCADStudio {
|
|||
self.ps_screening_buf = entry
|
||||
.map(|e| e.screening.to_string())
|
||||
.unwrap_or("100".into());
|
||||
if let Some(id) = self.plotstyle_window {
|
||||
return window::gain_focus(id);
|
||||
}
|
||||
let (id, task) = window::open(window::Settings {
|
||||
size: iced::Size::new(780.0, 540.0),
|
||||
resizable: true,
|
||||
level: window::Level::AlwaysOnTop,
|
||||
..Default::default()
|
||||
});
|
||||
self.plotstyle_window = Some(id);
|
||||
task.map(|_| Message::Noop)
|
||||
self.active_modal = Some(super::ModalKind::Plotstyle);
|
||||
Task::none()
|
||||
}
|
||||
Message::PlotStylePanelClose => {
|
||||
if let Some(id) = self.plotstyle_window.take() {
|
||||
window::close(id)
|
||||
} else {
|
||||
Task::none()
|
||||
}
|
||||
self.active_modal = None;
|
||||
Task::none()
|
||||
}
|
||||
Message::PlotStylePanelSelectAci(aci) => {
|
||||
self.plotstyle_panel_aci = aci;
|
||||
|
|
|
|||
160
src/app/view.rs
160
src/app/view.rs
|
|
@ -40,22 +40,6 @@ impl std::fmt::Display for RenderModeChoice {
|
|||
impl OpenCADStudio {
|
||||
pub fn view(&self, window_id: window::Id) -> Element<'_, Message> {
|
||||
// ── Floating panel windows ─────────────────────────────────────────
|
||||
if Some(window_id) == self.layer_window {
|
||||
let tab = &self.tabs[self.active_tab];
|
||||
return tab.layers.view_window();
|
||||
}
|
||||
if Some(window_id) == self.page_setup_window {
|
||||
return crate::ui::page_setup::view_window(
|
||||
&self.page_setup_w,
|
||||
&self.page_setup_h,
|
||||
&self.page_setup_plot_area,
|
||||
self.page_setup_center,
|
||||
&self.page_setup_offset_x,
|
||||
&self.page_setup_offset_y,
|
||||
&self.page_setup_rotation,
|
||||
&self.page_setup_scale,
|
||||
);
|
||||
}
|
||||
if Some(window_id) == self.textstyle_window {
|
||||
let tab = &self.tabs[self.active_tab];
|
||||
let styles: Vec<String> = tab
|
||||
|
|
@ -291,26 +275,6 @@ impl OpenCADStudio {
|
|||
},
|
||||
);
|
||||
}
|
||||
if Some(window_id) == self.layout_manager_window {
|
||||
let i = self.active_tab;
|
||||
let layouts = self.tabs[i].scene.layout_names();
|
||||
let current = self.tabs[i].scene.current_layout.clone();
|
||||
return crate::ui::layout_manager::view_window(
|
||||
layouts,
|
||||
&self.layout_manager_selected,
|
||||
&self.layout_manager_rename_buf,
|
||||
current,
|
||||
);
|
||||
}
|
||||
if Some(window_id) == self.plotstyle_window {
|
||||
return crate::ui::plotstyle::view_window(
|
||||
self.active_plot_style.as_ref(),
|
||||
self.plotstyle_panel_aci,
|
||||
&self.ps_color_buf,
|
||||
&self.ps_lineweight_buf,
|
||||
&self.ps_screening_buf,
|
||||
);
|
||||
}
|
||||
if Some(window_id) == self.dimstyle_window {
|
||||
let tab = &self.tabs[self.active_tab];
|
||||
let styles: Vec<String> = tab
|
||||
|
|
@ -468,20 +432,6 @@ impl OpenCADStudio {
|
|||
if Some(window_id) == self.color_pick_window {
|
||||
return crate::ui::color_select::color_grid_window(Message::ColorWindowPick);
|
||||
}
|
||||
if Some(window_id) == self.shortcuts_window {
|
||||
return crate::ui::shortcuts::view_window(&self.shortcut_overrides);
|
||||
}
|
||||
if Some(window_id) == self.about_window {
|
||||
return crate::ui::about::view_window();
|
||||
}
|
||||
if Some(window_id) == self.plugin_manager_window {
|
||||
return crate::ui::plugin_manager::view_window(&crate::plugin::installed_manifests());
|
||||
}
|
||||
if Some(window_id) == self.update_notice_window {
|
||||
let latest = self.update_notice_version.as_deref().unwrap_or("?");
|
||||
let body = self.update_notice_body.as_deref().unwrap_or("");
|
||||
return crate::ui::update_notice::view_window(latest, body);
|
||||
}
|
||||
if Some(window_id) == self.assoc_prompt_window {
|
||||
return default_assoc_dialog_window();
|
||||
}
|
||||
|
|
@ -1517,46 +1467,84 @@ impl OpenCADStudio {
|
|||
// ── In-canvas modal dialogs (Plan B) ───────────────────────────────
|
||||
// Former pop-up windows render as overlays here, so they work on both
|
||||
// the native (single main window) and web builds.
|
||||
use crate::ui::modal::modal;
|
||||
match self.active_modal {
|
||||
Some(super::ModalKind::About) => {
|
||||
modal(composed, crate::ui::about::view_window(), Message::CloseModal)
|
||||
}
|
||||
Some(super::ModalKind::Shortcuts) => modal(
|
||||
composed,
|
||||
// Content-heavy dialogs (lists / scroll) keep a bounded size —
|
||||
// their inner `Fill` widgets fill it — instead of shrinking.
|
||||
iced::widget::container(crate::ui::shortcuts::view_window(
|
||||
&self.shortcut_overrides,
|
||||
))
|
||||
.width(720)
|
||||
.height(520),
|
||||
Message::CloseModal,
|
||||
),
|
||||
Some(super::ModalKind::PluginManager) => modal(
|
||||
composed,
|
||||
iced::widget::container(crate::ui::plugin_manager::view_window(
|
||||
&crate::plugin::installed_manifests(),
|
||||
))
|
||||
.width(520)
|
||||
.height(460),
|
||||
Message::CloseModal,
|
||||
),
|
||||
Some(super::ModalKind::UpdateNotice) => {
|
||||
let latest = self.update_notice_version.as_deref().unwrap_or("?");
|
||||
let body = self.update_notice_body.as_deref().unwrap_or("");
|
||||
modal(
|
||||
composed,
|
||||
iced::widget::container(crate::ui::update_notice::view_window(latest, body))
|
||||
.width(560)
|
||||
.height(460),
|
||||
Message::CloseModal,
|
||||
)
|
||||
}
|
||||
match self.modal_content() {
|
||||
Some(content) => crate::ui::modal::modal(composed, content, Message::CloseModal),
|
||||
None => composed.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Build the currently-open modal dialog's content (Plan B), or `None`.
|
||||
/// Each former pop-up window is constructed here and given a bounded size
|
||||
/// (About shrinks to its content). Rendered as an overlay by `view_main`.
|
||||
fn modal_content(&self) -> Option<Element<'_, Message>> {
|
||||
fn sized<'a>(e: Element<'a, Message>, w: u16, h: u16) -> Element<'a, Message> {
|
||||
iced::widget::container(e)
|
||||
.width(iced::Length::Fixed(w as f32))
|
||||
.height(iced::Length::Fixed(h as f32))
|
||||
.into()
|
||||
}
|
||||
Some(match self.active_modal? {
|
||||
super::ModalKind::About => crate::ui::about::view_window(),
|
||||
super::ModalKind::Shortcuts => {
|
||||
sized(crate::ui::shortcuts::view_window(&self.shortcut_overrides), 720, 520)
|
||||
}
|
||||
super::ModalKind::PluginManager => sized(
|
||||
crate::ui::plugin_manager::view_window(&crate::plugin::installed_manifests()),
|
||||
520,
|
||||
460,
|
||||
),
|
||||
super::ModalKind::UpdateNotice => {
|
||||
let latest = self.update_notice_version.as_deref().unwrap_or("?");
|
||||
let body = self.update_notice_body.as_deref().unwrap_or("");
|
||||
sized(crate::ui::update_notice::view_window(latest, body), 560, 460)
|
||||
}
|
||||
super::ModalKind::Layers => {
|
||||
let tab = &self.tabs[self.active_tab];
|
||||
sized(tab.layers.view_window(), 900, 360)
|
||||
}
|
||||
super::ModalKind::PageSetup => sized(
|
||||
crate::ui::page_setup::view_window(
|
||||
&self.page_setup_w,
|
||||
&self.page_setup_h,
|
||||
&self.page_setup_plot_area,
|
||||
self.page_setup_center,
|
||||
&self.page_setup_offset_x,
|
||||
&self.page_setup_offset_y,
|
||||
&self.page_setup_rotation,
|
||||
&self.page_setup_scale,
|
||||
),
|
||||
520,
|
||||
460,
|
||||
),
|
||||
super::ModalKind::LayoutManager => {
|
||||
let i = self.active_tab;
|
||||
let layouts = self.tabs[i].scene.layout_names();
|
||||
let current = self.tabs[i].scene.current_layout.clone();
|
||||
sized(
|
||||
crate::ui::layout_manager::view_window(
|
||||
layouts,
|
||||
&self.layout_manager_selected,
|
||||
&self.layout_manager_rename_buf,
|
||||
current,
|
||||
),
|
||||
640,
|
||||
320,
|
||||
)
|
||||
}
|
||||
super::ModalKind::Plotstyle => sized(
|
||||
crate::ui::plotstyle::view_window(
|
||||
self.active_plot_style.as_ref(),
|
||||
self.plotstyle_panel_aci,
|
||||
&self.ps_color_buf,
|
||||
&self.ps_lineweight_buf,
|
||||
&self.ps_screening_buf,
|
||||
),
|
||||
780,
|
||||
540,
|
||||
),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
use iced::event;
|
||||
// Only request per-frame ticks while something on screen is animating
|
||||
|
|
|
|||
Loading…
Reference in a new issue