fix(styles): re-seed missing Standard styles on open; titled modals (#366)
Deleting/renaming Standard was already blocked in the five style managers, but a file saved without the built-ins (foreign or damaged) opened with broken style dropdowns and nothing to reference — re-seed missing Standard text/dim/table/mleader/mline styles on every open. Every modal dialog now shows its name in the title bar: centred at 15px across the dialog width, with the move grip and close button overlaid at the right edge. The bar takes an explicit width from the caller — a Fill child inside the Shrink frame would blow the dialog out to the full screen. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
bd27ee31d0
commit
ac14e0041d
7 changed files with 127 additions and 1 deletions
|
|
@ -224,6 +224,9 @@ impl OpenCADStudio {
|
|||
Ok(doc) => {
|
||||
let i = self.active_tab;
|
||||
self.tabs[i].scene.document = doc;
|
||||
crate::app::style_ops::ensure_standard_styles(
|
||||
&mut self.tabs[i].scene.document,
|
||||
);
|
||||
self.tabs[i].adopt_active_ucs_from_header();
|
||||
self.tabs[i].current_path = Some(PathBuf::from(path));
|
||||
self.tabs[i].is_start = false;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,62 @@ use acadrust::objects::{MLineStyle, MultiLeaderStyle, ObjectType, TableStyle};
|
|||
use acadrust::tables::{DimStyle, TextStyle};
|
||||
use acadrust::types::Handle;
|
||||
|
||||
/// Guarantee the built-in "Standard" style of every kind exists in `doc` —
|
||||
/// a foreign or damaged file saved without them leaves the style dropdowns
|
||||
/// broken with no way to recover, and new text/dimensions have nothing to
|
||||
/// reference (#366). Missing entries are re-seeded with the app defaults.
|
||||
/// Called on every file open; a no-op for healthy documents.
|
||||
pub(crate) fn ensure_standard_styles(doc: &mut acadrust::CadDocument) {
|
||||
if !doc
|
||||
.text_styles
|
||||
.iter()
|
||||
.any(|s| s.name.eq_ignore_ascii_case("Standard"))
|
||||
{
|
||||
let mut s = TextStyle::new("Standard");
|
||||
s.handle = doc.allocate_handle();
|
||||
let _ = doc.text_styles.add(s);
|
||||
}
|
||||
if !doc
|
||||
.dim_styles
|
||||
.iter()
|
||||
.any(|s| s.name.eq_ignore_ascii_case("Standard"))
|
||||
{
|
||||
let mut s = DimStyle::new("Standard");
|
||||
s.handle = doc.allocate_handle();
|
||||
let _ = doc.dim_styles.add(s);
|
||||
}
|
||||
let has = |doc: &acadrust::CadDocument, pred: fn(&ObjectType) -> Option<&str>| {
|
||||
doc.objects
|
||||
.values()
|
||||
.filter_map(pred)
|
||||
.any(|n| n.eq_ignore_ascii_case("Standard"))
|
||||
};
|
||||
if !has(doc, |o| match o {
|
||||
ObjectType::TableStyle(s) => Some(&s.name),
|
||||
_ => None,
|
||||
}) {
|
||||
let mut s = TableStyle::standard();
|
||||
s.handle = doc.allocate_handle();
|
||||
doc.objects.insert(s.handle, ObjectType::TableStyle(s));
|
||||
}
|
||||
if !has(doc, |o| match o {
|
||||
ObjectType::MultiLeaderStyle(s) => Some(&s.name),
|
||||
_ => None,
|
||||
}) {
|
||||
let mut s = MultiLeaderStyle::standard();
|
||||
s.handle = doc.allocate_handle();
|
||||
doc.objects.insert(s.handle, ObjectType::MultiLeaderStyle(s));
|
||||
}
|
||||
if !has(doc, |o| match o {
|
||||
ObjectType::MLineStyle(s) => Some(&s.name),
|
||||
_ => None,
|
||||
}) {
|
||||
let mut s = MLineStyle::standard();
|
||||
s.handle = doc.allocate_handle();
|
||||
doc.objects.insert(s.handle, ObjectType::MLineStyle(s));
|
||||
}
|
||||
}
|
||||
|
||||
/// Which style manager an operation targets. Carried by the shared rename
|
||||
/// messages so one handler can dispatch to the right storage.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
|
|||
|
|
@ -435,6 +435,11 @@ pub(super) fn on_open_file(&mut self) -> Task<Message> {
|
|||
|
||||
self.tabs[i].current_path = Some(path.clone());
|
||||
self.tabs[i].scene.document = doc;
|
||||
// A file saved without the built-in Standard styles (foreign
|
||||
// or damaged) gets them re-seeded so nothing dangles (#366).
|
||||
crate::app::style_ops::ensure_standard_styles(
|
||||
&mut self.tabs[i].scene.document,
|
||||
);
|
||||
// Follow the file's saved current UCS from the moment it opens.
|
||||
self.tabs[i].adopt_active_ucs_from_header();
|
||||
// Adopt the drawing's own Ortho ($ORTHOMODE) and running OSNAP
|
||||
|
|
|
|||
|
|
@ -1547,6 +1547,9 @@ impl OpenCADStudio {
|
|||
Some(content) => {
|
||||
crate::ui::modal::modal(
|
||||
composed,
|
||||
self.modal_title(),
|
||||
// Content width — outer size minus the frame padding.
|
||||
self.modal_outer_size().map(|s| s.0 - 20.0).unwrap_or(420.0),
|
||||
content,
|
||||
Message::CloseModal,
|
||||
self.modal_offset,
|
||||
|
|
@ -1560,6 +1563,8 @@ impl OpenCADStudio {
|
|||
if self.color_pick_target.is_some() {
|
||||
crate::ui::modal::modal(
|
||||
base,
|
||||
"Select Color",
|
||||
420.0,
|
||||
iced::widget::container(crate::ui::color_select::color_grid_window(
|
||||
Message::ColorWindowPick,
|
||||
))
|
||||
|
|
|
|||
|
|
@ -3,6 +3,38 @@ use iced::widget::{button, column, container, pick_list, row, text, Space};
|
|||
use iced::{Background, Border, Color, Element, Fill, Theme};
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// Title shown in the active modal's title bar, left of the move/close
|
||||
/// buttons. Keep in sync with the [`Self::modal_content`] dispatch.
|
||||
pub(super) fn modal_title(&self) -> &'static str {
|
||||
use super::super::ModalKind as K;
|
||||
match self.active_modal {
|
||||
Some(K::About) => "About",
|
||||
Some(K::Shortcuts) => "Keyboard Shortcuts",
|
||||
Some(K::Aliases) => "Command Aliases",
|
||||
Some(K::PluginManager) => "Plugin Manager",
|
||||
Some(K::UpdateNotice) => "Update Available",
|
||||
Some(K::Layers) => "Layer Manager",
|
||||
Some(K::Plot) => "Plot",
|
||||
Some(K::LayoutManager) => "Layout Manager",
|
||||
Some(K::ScaleManager) => "Scale Manager",
|
||||
Some(K::AnnoObjectScale) => "Annotation Object Scale",
|
||||
Some(K::Plotstyle) => "Plot Style Editor",
|
||||
Some(K::TextStyle) => "Text Style Manager",
|
||||
Some(K::MlStyle) => "Multiline Style Manager",
|
||||
Some(K::TableStyle) => "Table Style Manager",
|
||||
Some(K::MLeaderStyle) => "Multileader Style Manager",
|
||||
Some(K::DimStyle) => "Dimension Style Manager",
|
||||
Some(K::AssocPrompt) => "Default Application",
|
||||
Some(K::AecDropWarning) => "Save Warning",
|
||||
Some(K::LayerDeleteWarning) => "Delete Layer",
|
||||
Some(K::Unsaved) => "Unsaved Changes",
|
||||
Some(K::PointStyle) => "Point Style",
|
||||
Some(K::AttributeEditor) => "Attribute Editor",
|
||||
Some(K::SaveDialog) => "Save Drawing As",
|
||||
None => "",
|
||||
}
|
||||
}
|
||||
|
||||
/// 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`.
|
||||
|
|
|
|||
|
|
@ -702,6 +702,8 @@ pub(super) fn mtext_editor_overlay<'a>(
|
|||
// transparent fill lets the dimmed viewport show through beneath.
|
||||
crate::ui::modal::modal(
|
||||
iced::widget::Space::new().width(Fill).height(Fill),
|
||||
"Text Editor",
|
||||
660.0 + modal_resize.x,
|
||||
content,
|
||||
Message::MTextCancel,
|
||||
modal_offset,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ const GRIP_C: Color = Color {
|
|||
/// title bar; pass `Vector::ZERO` to keep it centred.
|
||||
pub fn modal<'a>(
|
||||
base: impl Into<Element<'a, Message>>,
|
||||
title: &'a str,
|
||||
title_width: f32,
|
||||
content: impl Into<Element<'a, Message>>,
|
||||
on_close: Message,
|
||||
offset: Vector,
|
||||
|
|
@ -83,7 +85,28 @@ pub fn modal<'a>(
|
|||
.on_press(Message::ModalGrab)
|
||||
.interaction(iced::mouse::Interaction::Grab);
|
||||
|
||||
let title_bar = row![grip, close].spacing(6).align_y(iced::Center);
|
||||
// The dialog name is centred across the dialog width with the grip + ✕
|
||||
// overlaid at the right edge. The bar takes an explicit `title_width`
|
||||
// (the caller's content width) instead of `Fill` — a Fill child inside
|
||||
// the Shrink frame would blow the dialog out to the full screen.
|
||||
let title_text = iced::widget::text(title).size(15).color(Color {
|
||||
r: 0.88,
|
||||
g: 0.88,
|
||||
b: 0.88,
|
||||
a: 1.0,
|
||||
});
|
||||
let title_bar = stack![
|
||||
container(title_text)
|
||||
.width(Length::Fixed(title_width))
|
||||
.height(Length::Fixed(24.0))
|
||||
.align_x(iced::alignment::Horizontal::Center)
|
||||
.align_y(iced::alignment::Vertical::Center),
|
||||
container(row![grip, close].spacing(6).align_y(iced::Center))
|
||||
.width(Length::Fixed(title_width))
|
||||
.height(Length::Fixed(24.0))
|
||||
.align_x(iced::alignment::Horizontal::Right)
|
||||
.align_y(iced::alignment::Vertical::Center),
|
||||
];
|
||||
|
||||
let panel_style = |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(PANEL)),
|
||||
|
|
|
|||
Loading…
Reference in a new issue