fix(ui): auto-size modal dialogs

Measure dialog content before fill layout so fields stay visible.\nKeep the MText preview width tied to its toolbar width.\n\nRefs #608
This commit is contained in:
Hakan Seven 2026-08-01 18:24:05 +03:00
commit 796d7d91d0
11 changed files with 352 additions and 220 deletions

View file

@ -580,6 +580,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
// Cancel layout rename first, then fall through.
let i_e = self.active_tab;
if self.qselect.take().is_some() {
self.reset_modal_geometry();
return Task::none();
}
{
@ -779,8 +780,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
pub(super) fn on_layer_delete_confirm(&mut self) -> Task<Message> {
let i = self.active_tab;
self.active_modal = None;
self.modal_offset = iced::Vector::ZERO;
self.modal_resize = iced::Vector::ZERO;
self.reset_modal_geometry();
let Some((names, _)) = self.layer_delete_pending.take() else {
return Task::none();
};
@ -1224,6 +1224,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
value: String::new(),
append: false,
});
self.reset_modal_geometry();
Task::none()
}
@ -2041,8 +2042,7 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
pub(super) fn cancel_attr_editor(&mut self) {
if self.active_modal == Some(crate::app::ModalKind::AttributeEditor) {
self.active_modal = None;
self.modal_offset = iced::Vector::ZERO;
self.modal_resize = iced::Vector::ZERO;
self.reset_modal_geometry();
}
self.attr_editor_handle = None;
self.attr_editor_block.clear();

View file

@ -65,6 +65,7 @@ impl OpenCADStudio {
self.aec_drop_acknowledged = false;
if self.active_modal == Some(crate::app::ModalKind::SaveDialog) {
self.active_modal = None;
self.reset_modal_geometry();
}
Task::none()
}
@ -94,6 +95,7 @@ impl OpenCADStudio {
pub(in crate::app) fn close_unsaved_dialog_window(&mut self) -> Task<Message> {
if self.active_modal == Some(crate::app::ModalKind::Unsaved) {
self.active_modal = None;
self.reset_modal_geometry();
}
Task::none()
}

View file

@ -399,6 +399,7 @@ impl OpenCADStudio {
/// Check if a suspended command exists on the active tab and resume it
/// with the outcome of the text editor.
pub(in crate::app) fn post_editor_closed(&mut self, committed: bool) -> Task<Message> {
self.reset_modal_geometry();
let i = self.active_tab;
if let Some(mut cmd) = self.tabs[i].suspended_cmd.take() {
let res = cmd.on_editor_closed(committed);
@ -3328,6 +3329,7 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
// the runtime paper/scale choices still drive this one plot operation.
self.sync_dialog_plot_runtime();
self.active_modal = None;
self.reset_modal_geometry();
let plot_style = self.dialog_plot_style(&d);
// Extents, Window and Display use one plot path in both spaces. Only

View file

@ -93,6 +93,15 @@ mod util;
mod viewport;
impl OpenCADStudio {
pub(in crate::app) fn reset_modal_geometry(&mut self) {
self.modal_offset = iced::Vector::ZERO;
self.modal_resize = iced::Vector::ZERO;
self.modal_content_size = None;
self.modal_drag_last = None;
self.modal_dragging = false;
self.modal_resizing = false;
}
fn sync_open_command_history(&mut self) {
if !self.command_line.history_open {
return;
@ -167,10 +176,7 @@ impl OpenCADStudio {
}
self.active_modal = None;
// Recentre / reset the size of the next dialog and drop any drag.
self.modal_offset = iced::Vector::ZERO;
self.modal_resize = iced::Vector::ZERO;
self.modal_drag_last = None;
self.modal_dragging = false;
self.reset_modal_geometry();
}
pub fn update(&mut self, msg: Message) -> Task<Message> {
@ -1423,6 +1429,7 @@ impl OpenCADStudio {
if self.active_modal == Some(super::ModalKind::Layers) {
self.ribbon.deactivate_tool_if("LAYERS");
self.active_modal = None;
self.reset_modal_geometry();
} else {
self.sync_ribbon_layers();
self.active_modal = Some(super::ModalKind::Layers);
@ -3335,6 +3342,7 @@ impl OpenCADStudio {
Message::QSelectClose => {
self.qselect = None;
self.reset_modal_geometry();
Task::none()
}
@ -3390,6 +3398,7 @@ impl OpenCADStudio {
let Some(state) = self.qselect.take() else {
return Task::none();
};
self.reset_modal_geometry();
let i = self.active_tab;
let matched = self.tabs[i].scene.qselect(
state.type_filter.as_deref(),
@ -4402,6 +4411,32 @@ impl OpenCADStudio {
self.modal_drag_last = None;
Task::none()
}
Message::ModalContentResized(size) => {
if !size.width.is_finite()
|| !size.height.is_finite()
|| size.width <= 0.0
|| size.height <= 0.0
{
return Task::none();
}
let first_measurement = self.modal_content_size.replace(size).is_none();
if first_measurement {
let initial_width = self.mtext_editor.as_ref().and_then(|editor| {
editor.editing.is_none().then(|| {
(size.width - 2.0 * super::view::overlay::MTEXT_PREVIEW_PAD)
.max(80.0)
/ editor.preview_scale()
})
});
if let (Some(editor), Some(width)) =
(self.mtext_editor.as_mut(), initial_width)
{
editor.rect_width = f64::from(width.max(1e-6));
self.rebuild_mtext_preview();
}
}
Task::none()
}
Message::RibbonLayerFilterChanged(f) => {
self.ribbon.layer_filter = f;
Task::none()
@ -4444,8 +4479,11 @@ impl OpenCADStudio {
// of being squeezed (the off-centre padding shrinks the
// dialog once it overlaps a border).
if let Some((cw, ch)) = self.modal_outer_size() {
let ww = self.vp_size.0 + 440.0;
let wh = self.vp_size.1;
let (ww, wh) = if self.mtext_editor.is_some() {
self.vp_size
} else {
self.win_size
};
let max_x = ((ww - cw) * 0.5).max(0.0);
let max_y = ((wh - ch) * 0.5).max(0.0);
self.modal_offset.x = self.modal_offset.x.clamp(-max_x, max_x);
@ -5056,6 +5094,7 @@ impl OpenCADStudio {
self.file_assoc_enabled = true;
self.mark_assoc_prompted();
self.active_modal = None;
self.reset_modal_geometry();
// set_default_app registers the handler first, then makes us the
// default — boot no longer does this automatically.
Task::perform(
@ -5067,6 +5106,7 @@ impl OpenCADStudio {
self.file_assoc_enabled = false;
self.mark_assoc_prompted();
self.active_modal = None;
self.reset_modal_geometry();
Task::none()
}
Message::AssocResult(result) => {