From 888095cc3ece98dda9c69c8ec9d084e894a256fa Mon Sep 17 00:00:00 2001 From: Hakan Seven Date: Wed, 8 Apr 2026 22:36:28 +0300 Subject: [PATCH] refactor: convert all overlay dialogs to separate OS windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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; 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 --- src/app/mod.rs | 48 +++++++------ src/app/update.rs | 172 ++++++++++++++++++++++++++++++++++++---------- src/app/view.rs | 168 +++++++++++++++++--------------------------- 3 files changed, 227 insertions(+), 161 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 6fe47d862..25c551ea2 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -59,6 +59,15 @@ pub(super) struct H7CAD { layer_window: Option, /// OS window Id of the primary application window. main_window: Option, + // ── Floating panel windows ──────────────────────────────────────────── + page_setup_window: Option, + textstyle_window: Option, + tablestyle_window: Option, + mlstyle_window: Option, + layout_manager_window: Option, + plotstyle_window: Option, + dimstyle_window: Option, + shortcuts_window: Option, /// In-memory clipboard: cloned entities waiting to be pasted. clipboard: Vec, /// Centroid of the clipboard entities (XZ plane, Y-up). @@ -71,8 +80,7 @@ pub(super) struct H7CAD { last_vp_click_time: Option, /// Screen position of the previous viewport left-click release. last_vp_click_pos: Option, - /// Page Setup overlay open/closed. - page_setup_open: bool, + // page_setup_open: moved to page_setup_window: Option /// Editable paper width buffer for the Page Setup panel (string while typing). page_setup_w: String, /// Editable paper height buffer for the Page Setup panel (string while typing). @@ -95,15 +103,12 @@ pub(super) struct H7CAD { active_plot_style: Option, // ── MLineStyle Dialog ───────────────────────────────────────────────── - mlstyle_open: bool, mlstyle_selected: String, // ── TableStyle Dialog ───────────────────────────────────────────────── - tablestyle_open: bool, tablestyle_selected: String, // ── TextStyle Font Browser ──────────────────────────────────────────── - textstyle_open: bool, textstyle_selected: String, /// Edit buffer for font file name. textstyle_font: String, @@ -116,17 +121,14 @@ pub(super) struct H7CAD { active_theme: Theme, // ── Keyboard Shortcut Editor ────────────────────────────────────────── - shortcuts_panel_open: bool, /// User-defined function-key overrides: "F3" → command string. shortcut_overrides: std::collections::HashMap, // ── Layout Manager Panel ────────────────────────────────────────────── - layout_manager_open: bool, layout_manager_selected: String, layout_manager_rename_buf: String, // ── Plot Style Panel ────────────────────────────────────────────────── - plotstyle_panel_open: bool, /// Selected ACI index in the panel (1-255). plotstyle_panel_aci: u8, /// Edit buffers for the selected entry. @@ -135,7 +137,6 @@ pub(super) struct H7CAD { ps_screening_buf: String, // ── DimStyle Dialog ─────────────────────────────────────────────────── - dimstyle_open: bool, /// Name of the style currently shown in the dialog. dimstyle_selected: String, /// Active tab: 0=Lines, 1=Arrows, 2=Text, 3=Scale/Units, 4=Tolerances. @@ -535,13 +536,20 @@ impl H7CAD { last_point: None, layer_window: None, main_window: None, + page_setup_window: None, + textstyle_window: None, + tablestyle_window: None, + mlstyle_window: None, + layout_manager_window: None, + plotstyle_window: None, + dimstyle_window: None, + shortcuts_window: None, clipboard: Vec::new(), clipboard_centroid: glam::Vec3::ZERO, layout_context_menu: None, layout_rename_state: None, last_vp_click_time: None, last_vp_click_pos: None, - page_setup_open: false, page_setup_w: String::new(), page_setup_h: String::new(), page_setup_plot_area: "Layout".to_string(), @@ -555,31 +563,24 @@ impl H7CAD { // Color scheme (default: dark CAD-style) active_theme: Theme::Dark, // Keyboard shortcuts - shortcuts_panel_open: false, shortcut_overrides: std::collections::HashMap::new(), // Layout Manager - layout_manager_open: false, layout_manager_selected: "Model".to_string(), layout_manager_rename_buf: String::new(), - plotstyle_panel_open: false, plotstyle_panel_aci: 1, ps_color_buf: String::new(), ps_lineweight_buf: "255".to_string(), ps_screening_buf: "100".to_string(), // TextStyle font browser - textstyle_open: false, textstyle_selected: "Standard".to_string(), textstyle_font: String::new(), textstyle_width: "1.0".to_string(), textstyle_oblique: "0.0".to_string(), // TableStyle dialog - tablestyle_open: false, tablestyle_selected: "Standard".to_string(), // MLineStyle dialog - mlstyle_open: false, mlstyle_selected: "Standard".to_string(), // DimStyle dialog - dimstyle_open: false, dimstyle_selected: "Standard".to_string(), dimstyle_tab: 0, ds_dimdle: "0".to_string(), ds_dimdli: "3.75".to_string(), @@ -626,9 +627,16 @@ pub fn run() -> iced::Result { iced::daemon(H7CAD::boot, H7CAD::update, H7CAD::view) .subscription(H7CAD::subscription) .title(|state: &H7CAD, window_id: window::Id| { - if Some(window_id) == state.layer_window { - "Layer Properties Manager".to_string() - } else if let Some(tab) = state.tabs.get(state.active_tab) { + if Some(window_id) == state.layer_window { return "Layer Properties Manager".into(); } + if Some(window_id) == state.page_setup_window { return "Page Setup".into(); } + if Some(window_id) == state.textstyle_window { return "Text Style".into(); } + if Some(window_id) == state.tablestyle_window { return "Table Style".into(); } + if Some(window_id) == state.mlstyle_window { return "Multiline Style".into(); } + if Some(window_id) == state.layout_manager_window { return "Layout Manager".into(); } + if Some(window_id) == state.plotstyle_window { return "Plot Style Table Editor".into(); } + if Some(window_id) == state.dimstyle_window { return "Dimension Style Manager".into(); } + if Some(window_id) == state.shortcuts_window { return "Keyboard Shortcuts".into(); } + if let Some(tab) = state.tabs.get(state.active_tab) { let dot = if tab.dirty { "● " } else { "" }; let name = tab.tab_display_name(); format!("{}H7CAD — {}", dot, name) diff --git a/src/app/update.rs b/src/app/update.rs index 7f61b8d64..61afb5290 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -695,9 +695,15 @@ impl H7CAD { if self.main_window == Some(id) { return iced::exit(); } - if self.layer_window == Some(id) { - self.layer_window = None; - } + if self.layer_window == Some(id) { self.layer_window = None; } + if self.page_setup_window == Some(id) { self.page_setup_window = None; } + if self.textstyle_window == Some(id) { self.textstyle_window = None; } + if self.tablestyle_window == Some(id) { self.tablestyle_window = None; } + if self.mlstyle_window == Some(id) { self.mlstyle_window = None; } + if self.layout_manager_window == Some(id) { self.layout_manager_window = None; } + if self.plotstyle_window == Some(id) { self.plotstyle_window = None; } + if self.dimstyle_window == Some(id) { self.dimstyle_window = None; } + if self.shortcuts_window == Some(id) { self.shortcuts_window = None; } Task::none() } @@ -2290,12 +2296,23 @@ impl H7CAD { } else { current }; - self.layout_manager_open = true; - Task::none() + 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, + ..Default::default() + }); + self.layout_manager_window = Some(id); + task.map(|_| Message::Noop) } Message::LayoutManagerClose => { - self.layout_manager_open = false; - Task::none() + if let Some(id) = self.layout_manager_window.take() { + window::close(id) + } else { + Task::none() + } } Message::LayoutManagerSelect(name) => { self.layout_manager_rename_buf = if name == "Model" { String::new() } else { name.clone() }; @@ -2414,12 +2431,23 @@ impl H7CAD { // ── Keyboard Shortcuts Panel ────────────────────────────────────── Message::ShortcutsPanelOpen => { - self.shortcuts_panel_open = true; - Task::none() + if let Some(id) = self.shortcuts_window { + return window::gain_focus(id); + } + let (id, task) = window::open(window::Settings { + size: iced::Size::new(720.0, 520.0), + resizable: true, + ..Default::default() + }); + self.shortcuts_window = Some(id); + task.map(|_| Message::Noop) } Message::ShortcutsPanelClose => { - self.shortcuts_panel_open = false; - Task::none() + if let Some(id) = self.shortcuts_window.take() { + window::close(id) + } else { + Task::none() + } } Message::ViewportContextMenuClose => { @@ -2499,12 +2527,23 @@ impl H7CAD { }; self.page_setup_w = format!("{w:.1}"); self.page_setup_h = format!("{h:.1}"); - self.page_setup_open = true; - Task::none() + 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, + ..Default::default() + }); + self.page_setup_window = Some(id); + task.map(|_| Message::Noop) } Message::PageSetupClose => { - self.page_setup_open = false; - Task::none() + if let Some(id) = self.page_setup_window.take() { + window::close(id) + } else { + Task::none() + } } Message::PageSetupWidthEdit(s) => { self.page_setup_w = s; @@ -2642,7 +2681,9 @@ impl H7CAD { center={center} rot={rotation}°" )); } - self.page_setup_open = false; + if let Some(id) = self.page_setup_window.take() { + return window::close(id); + } Task::none() } @@ -2853,7 +2894,6 @@ impl H7CAD { // ── Plot Style Panel ────────────────────────────────────────────── Message::PlotStylePanelOpen => { - self.plotstyle_panel_open = true; // Initialise edit buffers for ACI 1. self.plotstyle_panel_aci = 1; let entry = self.active_plot_style.as_ref() @@ -2861,11 +2901,23 @@ impl H7CAD { self.ps_color_buf = entry.and_then(|e| e.color.map(|[r,g,b]| format!("#{:02X}{:02X}{:02X}", r, g, b))).unwrap_or_default(); self.ps_lineweight_buf = entry.map(|e| e.lineweight.to_string()).unwrap_or("255".into()); self.ps_screening_buf = entry.map(|e| e.screening.to_string()).unwrap_or("100".into()); - Task::none() + 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, + ..Default::default() + }); + self.plotstyle_window = Some(id); + task.map(|_| Message::Noop) } Message::PlotStylePanelClose => { - self.plotstyle_panel_open = false; - Task::none() + if let Some(id) = self.plotstyle_window.take() { + window::close(id) + } else { + Task::none() + } } Message::PlotStylePanelSelectAci(aci) => { self.plotstyle_panel_aci = aci; @@ -2970,12 +3022,23 @@ impl H7CAD { .unwrap_or_else(|| "Standard".to_string()) }; self.load_textstyle_bufs(i); - self.textstyle_open = true; - Task::none() + if let Some(id) = self.textstyle_window { + return window::gain_focus(id); + } + let (id, task) = window::open(window::Settings { + size: iced::Size::new(620.0, 460.0), + resizable: true, + ..Default::default() + }); + self.textstyle_window = Some(id); + task.map(|_| Message::Noop) } Message::TextStyleDialogClose => { - self.textstyle_open = false; - Task::none() + if let Some(id) = self.textstyle_window.take() { + window::close(id) + } else { + Task::none() + } } Message::TextStyleDialogSelect(name) => { let i = self.active_tab; @@ -3080,12 +3143,23 @@ impl H7CAD { self.tablestyle_selected = self.tabs[i].scene.document.objects.values() .find_map(|o| if let ObjectType::TableStyle(s) = o { Some(s.name.clone()) } else { None }) .unwrap_or_else(|| "Standard".to_string()); - self.tablestyle_open = true; - Task::none() + if let Some(id) = self.tablestyle_window { + return window::gain_focus(id); + } + let (id, task) = window::open(window::Settings { + size: iced::Size::new(620.0, 420.0), + resizable: true, + ..Default::default() + }); + self.tablestyle_window = Some(id); + task.map(|_| Message::Noop) } Message::TableStyleDialogClose => { - self.tablestyle_open = false; - Task::none() + if let Some(id) = self.tablestyle_window.take() { + window::close(id) + } else { + Task::none() + } } Message::TableStyleDialogSelect(name) => { self.tablestyle_selected = name; @@ -3155,12 +3229,23 @@ impl H7CAD { .find_map(|o| if let ObjectType::MLineStyle(s) = o { Some(s.name.clone()) } else { None }) .unwrap_or_else(|| "Standard".to_string()) }; - self.mlstyle_open = true; - Task::none() + if let Some(id) = self.mlstyle_window { + return window::gain_focus(id); + } + let (id, task) = window::open(window::Settings { + size: iced::Size::new(620.0, 420.0), + resizable: true, + ..Default::default() + }); + self.mlstyle_window = Some(id); + task.map(|_| Message::Noop) } Message::MlStyleDialogClose => { - self.mlstyle_open = false; - Task::none() + if let Some(id) = self.mlstyle_window.take() { + window::close(id) + } else { + Task::none() + } } Message::MlStyleDialogSelect(name) => { self.mlstyle_selected = name; @@ -3245,13 +3330,24 @@ impl H7CAD { .unwrap_or_else(|| "Standard".to_string()) }; self.dimstyle_selected = selected.clone(); - self.dimstyle_open = true; self.load_dimstyle_bufs(i); - Task::none() + if let Some(id) = self.dimstyle_window { + return window::gain_focus(id); + } + let (id, task) = window::open(window::Settings { + size: iced::Size::new(720.0, 560.0), + resizable: true, + ..Default::default() + }); + self.dimstyle_window = Some(id); + task.map(|_| Message::Noop) } Message::DimStyleDialogClose => { - self.dimstyle_open = false; - Task::none() + if let Some(id) = self.dimstyle_window.take() { + window::close(id) + } else { + Task::none() + } } Message::DimStyleDialogApply => { let i = self.active_tab; @@ -3271,7 +3367,9 @@ impl H7CAD { Message::DimStyleDialogNew => { // Delegate to the DIMSTYLE NEW command via command line prompt. self.command_line.push_info("Enter new DimStyle name:"); - self.dimstyle_open = false; + if let Some(id) = self.dimstyle_window.take() { + return window::close(id); + } Task::none() } Message::DimStyleDialogSetCurrent => { diff --git a/src/app/view.rs b/src/app/view.rs index 23b31ba09..da9aa8e68 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -13,11 +13,73 @@ const VIEWCUBE_HIT_SIZE: f32 = VIEWCUBE_DRAW_PX; impl H7CAD { pub fn view(&self, window_id: window::Id) -> Element<'_, Message> { - // ── Layer Properties Manager window ─────────────────────────────── + // ── 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 page_setup_overlay( + &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 = tab.scene.document.text_styles + .iter().map(|s| s.name.clone()).collect(); + return textstyle_overlay(styles, &self.textstyle_selected, + &self.textstyle_font, &self.textstyle_width, &self.textstyle_oblique); + } + if Some(window_id) == self.tablestyle_window { + use acadrust::objects::ObjectType; + let tab = &self.tabs[self.active_tab]; + let styles: Vec = tab.scene.document.objects.values() + .filter_map(|o| if let ObjectType::TableStyle(s) = o { Some(s.name.clone()) } else { None }) + .collect(); + let selected_style = tab.scene.document.objects.values() + .find_map(|o| if let ObjectType::TableStyle(s) = o { + if s.name == self.tablestyle_selected { Some(s) } else { None } + } else { None }); + return tablestyle_overlay(styles, &self.tablestyle_selected, selected_style); + } + if Some(window_id) == self.mlstyle_window { + use acadrust::objects::ObjectType; + let tab = &self.tabs[self.active_tab]; + let styles: Vec = tab.scene.document.objects.values() + .filter_map(|o| if let ObjectType::MLineStyle(s) = o { Some(s.name.clone()) } else { None }) + .collect(); + let selected_style = tab.scene.document.objects.values() + .find_map(|o| if let ObjectType::MLineStyle(s) = o { + if s.name == self.mlstyle_selected { Some(s) } else { None } + } else { None }); + return mlstyle_overlay(styles, &self.mlstyle_selected, selected_style, + tab.scene.document.header.multiline_style.clone()); + } + 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 layout_manager_overlay(layouts, &self.layout_manager_selected, + &self.layout_manager_rename_buf, current); + } + if Some(window_id) == self.plotstyle_window { + return plotstyle_panel_overlay( + 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 = tab.scene.document.dim_styles + .iter().map(|s| s.name.clone()).collect(); + return dimstyle_overlay(styles, &self.dimstyle_selected, self.dimstyle_tab, self); + } + if Some(window_id) == self.shortcuts_window { + return shortcuts_panel_overlay(&self.shortcut_overrides); + } let i = self.active_tab; let tab = &self.tabs[i]; @@ -257,102 +319,6 @@ impl H7CAD { iced::widget::Space::new().width(0).height(0).into() }; - let page_setup_layer: Element<'_, Message> = if self.page_setup_open { - page_setup_overlay( - &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, - ) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - let textstyle_layer: Element<'_, Message> = if self.textstyle_open { - let tab = &self.tabs[self.active_tab]; - let styles: Vec = tab.scene.document.text_styles - .iter().map(|s| s.name.clone()).collect(); - textstyle_overlay(styles, &self.textstyle_selected, &self.textstyle_font, &self.textstyle_width, &self.textstyle_oblique) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - let tablestyle_layer: Element<'_, Message> = if self.tablestyle_open { - use acadrust::objects::ObjectType; - let tab = &self.tabs[self.active_tab]; - let styles: Vec = tab.scene.document.objects.values() - .filter_map(|o| if let ObjectType::TableStyle(s) = o { Some(s.name.clone()) } else { None }) - .collect(); - let selected_style = tab.scene.document.objects.values() - .find_map(|o| if let ObjectType::TableStyle(s) = o { - if s.name == self.tablestyle_selected { Some(s) } else { None } - } else { None }); - tablestyle_overlay(styles, &self.tablestyle_selected, selected_style) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - let mlstyle_layer: Element<'_, Message> = if self.mlstyle_open { - use acadrust::objects::ObjectType; - let tab = &self.tabs[self.active_tab]; - let styles: Vec = tab.scene.document.objects.values() - .filter_map(|o| if let ObjectType::MLineStyle(s) = o { Some(s.name.clone()) } else { None }) - .collect(); - let selected_style = tab.scene.document.objects.values() - .find_map(|o| if let ObjectType::MLineStyle(s) = o { - if s.name == self.mlstyle_selected { Some(s) } else { None } - } else { None }); - mlstyle_overlay(styles, &self.mlstyle_selected, selected_style, tab.scene.document.header.multiline_style.clone()) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - // ── Layout Manager Panel ───────────────────────────────────────── - let layout_manager_layer: Element<'_, Message> = if self.layout_manager_open { - let i = self.active_tab; - let layouts = self.tabs[i].scene.layout_names(); - let current = self.tabs[i].scene.current_layout.clone(); - layout_manager_overlay( - layouts, - &self.layout_manager_selected, - &self.layout_manager_rename_buf, - current, - ) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - // ── Plot Style Panel ────────────────────────────────────────────── - let plotstyle_layer: Element<'_, Message> = if self.plotstyle_panel_open { - plotstyle_panel_overlay( - self.active_plot_style.as_ref(), - self.plotstyle_panel_aci, - &self.ps_color_buf, - &self.ps_lineweight_buf, - &self.ps_screening_buf, - ) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - let dimstyle_layer: Element<'_, Message> = if self.dimstyle_open { - let tab = &self.tabs[self.active_tab]; - let styles: Vec = tab.scene.document.dim_styles - .iter().map(|s| s.name.clone()).collect(); - dimstyle_overlay( - styles, - &self.dimstyle_selected, - self.dimstyle_tab, - self, - ) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - // ── Viewport right-click context menu ───────────────────────────── let viewport_ctx_layer: Element<'_, Message> = { let ctx_pos = tab.scene.selection.borrow().context_menu; @@ -367,13 +333,7 @@ impl H7CAD { } }; - let shortcuts_layer: Element<'_, Message> = if self.shortcuts_panel_open { - shortcuts_panel_overlay(&self.shortcut_overrides) - } else { - iced::widget::Space::new().width(0).height(0).into() - }; - - stack![main_ui, self.app_menu.view(), snap_layer, dropdown_layer, layout_ctx_layer, page_setup_layer, textstyle_layer, tablestyle_layer, mlstyle_layer, plotstyle_layer, layout_manager_layer, shortcuts_layer, dimstyle_layer, viewport_ctx_layer].into() + stack![main_ui, self.app_menu.view(), snap_layer, dropdown_layer, layout_ctx_layer, viewport_ctx_layer].into() } pub fn subscription(&self) -> Subscription {