feat: MLineStyle manager (MLSTYLE command + dialog)
- MLSTYLE command: LIST, NEW, SET, DEL subcommands - Multiline Style Manager dialog: style list, element details (offset, color, linetype), Set Current / New / Delete buttons - MLINE/ML command now reads document header.multiline_style to apply the current style to newly created MLine entities - MlineCommand.with_style() constructor for style-aware multiline drawing Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7e6de57c8d
commit
34076b26bb
5 changed files with 407 additions and 6 deletions
|
|
@ -392,7 +392,8 @@ impl H7CAD {
|
||||||
|
|
||||||
"MLINE"|"ML" => {
|
"MLINE"|"ML" => {
|
||||||
use crate::modules::home::draw::mline::MlineCommand;
|
use crate::modules::home::draw::mline::MlineCommand;
|
||||||
let cmd_obj = MlineCommand::new();
|
let style = self.tabs[i].scene.document.header.multiline_style.clone();
|
||||||
|
let cmd_obj = MlineCommand::with_style(style);
|
||||||
self.command_line.push_info(&cmd_obj.prompt());
|
self.command_line.push_info(&cmd_obj.prompt());
|
||||||
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
self.tabs[i].active_cmd = Some(Box::new(cmd_obj));
|
||||||
}
|
}
|
||||||
|
|
@ -2084,6 +2085,115 @@ impl H7CAD {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── DimStyle management ───────────────────────────────────────
|
// ── DimStyle management ───────────────────────────────────────
|
||||||
|
// MLSTYLE — Multiline Style Manager.
|
||||||
|
// Usage:
|
||||||
|
// MLSTYLE — open dialog
|
||||||
|
// MLSTYLE LIST / ? — list all multiline styles
|
||||||
|
// MLSTYLE NEW <name> — create a new style
|
||||||
|
// MLSTYLE SET <name> — set current multiline style
|
||||||
|
// MLSTYLE DEL <name> — delete a style (not Standard)
|
||||||
|
cmd if cmd == "MLSTYLE" || cmd.starts_with("MLSTYLE ") => {
|
||||||
|
use acadrust::objects::{MLineStyle, ObjectType};
|
||||||
|
let raw_rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");
|
||||||
|
let parts: Vec<&str> = raw_rest.split_whitespace().collect();
|
||||||
|
let sub = parts.first().map(|s| s.to_uppercase()).unwrap_or_default();
|
||||||
|
match sub.as_str() {
|
||||||
|
"" | "DIALOG" | "UI" => {
|
||||||
|
return Task::done(Message::MlStyleDialogOpen);
|
||||||
|
}
|
||||||
|
"LIST" | "?" => {
|
||||||
|
let doc = &self.tabs[i].scene.document;
|
||||||
|
let current = &doc.header.multiline_style;
|
||||||
|
let styles: Vec<String> = doc.objects.values()
|
||||||
|
.filter_map(|o| if let ObjectType::MLineStyle(s) = o { Some(s) } else { None })
|
||||||
|
.map(|s| {
|
||||||
|
let cur = if &s.name == current { " (current)" } else { "" };
|
||||||
|
format!("{} [{}]{}",
|
||||||
|
s.name,
|
||||||
|
s.elements.len(),
|
||||||
|
cur)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
if styles.is_empty() {
|
||||||
|
self.command_line.push_output("No multiline styles.");
|
||||||
|
} else {
|
||||||
|
self.command_line.push_output(&format!("MLineStyles:\n {}", styles.join("\n ")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"NEW" | "N" => {
|
||||||
|
let name = parts.get(1).copied().unwrap_or("").to_string();
|
||||||
|
if name.is_empty() {
|
||||||
|
self.command_line.push_error("Usage: MLSTYLE NEW <name>");
|
||||||
|
} else {
|
||||||
|
let doc = &self.tabs[i].scene.document;
|
||||||
|
let exists = doc.objects.values().any(|o| {
|
||||||
|
matches!(o, ObjectType::MLineStyle(s) if s.name.eq_ignore_ascii_case(&name))
|
||||||
|
});
|
||||||
|
if exists {
|
||||||
|
self.command_line.push_error(&format!("MLSTYLE: '{}' already exists.", name));
|
||||||
|
} else {
|
||||||
|
self.push_undo_snapshot(i, "MLSTYLE NEW");
|
||||||
|
let mut style = MLineStyle::standard();
|
||||||
|
style.name = name.clone();
|
||||||
|
let nh = acadrust::Handle::new(self.tabs[i].scene.document.next_handle());
|
||||||
|
style.handle = nh;
|
||||||
|
self.tabs[i].scene.document.objects.insert(
|
||||||
|
nh, ObjectType::MLineStyle(style)
|
||||||
|
);
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.command_line.push_output(&format!("MLSTYLE: '{}' created.", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"SET" | "S" => {
|
||||||
|
let name = parts.get(1).copied().unwrap_or("").to_string();
|
||||||
|
if name.is_empty() {
|
||||||
|
self.command_line.push_error("Usage: MLSTYLE SET <name>");
|
||||||
|
} else {
|
||||||
|
let doc = &self.tabs[i].scene.document;
|
||||||
|
let exists = doc.objects.values().any(|o| {
|
||||||
|
matches!(o, ObjectType::MLineStyle(s) if s.name.eq_ignore_ascii_case(&name))
|
||||||
|
});
|
||||||
|
if exists {
|
||||||
|
self.push_undo_snapshot(i, "MLSTYLE SET");
|
||||||
|
self.tabs[i].scene.document.header.multiline_style = name.clone();
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.command_line.push_output(&format!("MLSTYLE: current style set to '{}'.", name));
|
||||||
|
} else {
|
||||||
|
self.command_line.push_error(&format!("MLSTYLE: '{}' not found.", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"DEL" | "DELETE" => {
|
||||||
|
let name = parts.get(1).copied().unwrap_or("").to_string();
|
||||||
|
if name.is_empty() || name.eq_ignore_ascii_case("Standard") {
|
||||||
|
self.command_line.push_error("Cannot delete the Standard style.");
|
||||||
|
} else {
|
||||||
|
let doc = &self.tabs[i].scene.document;
|
||||||
|
let handle = doc.objects.iter()
|
||||||
|
.find_map(|(&h, o)| {
|
||||||
|
if let ObjectType::MLineStyle(s) = o {
|
||||||
|
if s.name.eq_ignore_ascii_case(&name) { Some(h) } else { None }
|
||||||
|
} else { None }
|
||||||
|
});
|
||||||
|
if let Some(h) = handle {
|
||||||
|
self.push_undo_snapshot(i, "MLSTYLE DEL");
|
||||||
|
self.tabs[i].scene.document.objects.remove(&h);
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.command_line.push_output(&format!("MLSTYLE: '{}' deleted.", name));
|
||||||
|
} else {
|
||||||
|
self.command_line.push_error(&format!("MLSTYLE: '{}' not found.", name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.command_line.push_error(
|
||||||
|
"Usage: MLSTYLE [LIST|NEW <name>|SET <name>|DEL <name>]"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cmd if cmd == "DIMSTYLE" || cmd == "DDIM" || cmd.starts_with("DIMSTYLE ") || cmd.starts_with("DDIM ") => {
|
cmd if cmd == "DIMSTYLE" || cmd == "DDIM" || cmd.starts_with("DIMSTYLE ") || cmd.starts_with("DDIM ") => {
|
||||||
use acadrust::tables::DimStyle;
|
use acadrust::tables::DimStyle;
|
||||||
let raw_rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");
|
let raw_rest = cmd.split_once(' ').map(|(_, r)| r.trim()).unwrap_or("");
|
||||||
|
|
|
||||||
|
|
@ -90,6 +90,10 @@ pub(super) struct H7CAD {
|
||||||
/// Currently loaded CTB/STB table (None = no override).
|
/// Currently loaded CTB/STB table (None = no override).
|
||||||
active_plot_style: Option<crate::io::plot_style::PlotStyleTable>,
|
active_plot_style: Option<crate::io::plot_style::PlotStyleTable>,
|
||||||
|
|
||||||
|
// ── MLineStyle Dialog ─────────────────────────────────────────────────
|
||||||
|
mlstyle_open: bool,
|
||||||
|
mlstyle_selected: String,
|
||||||
|
|
||||||
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
||||||
dimstyle_open: bool,
|
dimstyle_open: bool,
|
||||||
/// Name of the style currently shown in the dialog.
|
/// Name of the style currently shown in the dialog.
|
||||||
|
|
@ -335,6 +339,13 @@ pub enum Message {
|
||||||
PlotStyleLoaded(Option<crate::io::plot_style::PlotStyleTable>),
|
PlotStyleLoaded(Option<crate::io::plot_style::PlotStyleTable>),
|
||||||
/// Clear the active plot style table.
|
/// Clear the active plot style table.
|
||||||
PlotStyleClear,
|
PlotStyleClear,
|
||||||
|
// ── MLineStyle Dialog ─────────────────────────────────────────────────
|
||||||
|
MlStyleDialogOpen,
|
||||||
|
MlStyleDialogClose,
|
||||||
|
MlStyleDialogSelect(String),
|
||||||
|
MlStyleDialogSetCurrent,
|
||||||
|
MlStyleDialogNew,
|
||||||
|
MlStyleDialogDelete,
|
||||||
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
// ── DimStyle Dialog ───────────────────────────────────────────────────
|
||||||
DimStyleDialogOpen,
|
DimStyleDialogOpen,
|
||||||
DimStyleDialogClose,
|
DimStyleDialogClose,
|
||||||
|
|
@ -396,6 +407,9 @@ impl H7CAD {
|
||||||
page_setup_scale: "Fit".to_string(),
|
page_setup_scale: "Fit".to_string(),
|
||||||
// Plot style
|
// Plot style
|
||||||
active_plot_style: None,
|
active_plot_style: None,
|
||||||
|
// MLineStyle dialog
|
||||||
|
mlstyle_open: false,
|
||||||
|
mlstyle_selected: "Standard".to_string(),
|
||||||
// DimStyle dialog
|
// DimStyle dialog
|
||||||
dimstyle_open: false,
|
dimstyle_open: false,
|
||||||
dimstyle_selected: "Standard".to_string(),
|
dimstyle_selected: "Standard".to_string(),
|
||||||
|
|
|
||||||
|
|
@ -2126,6 +2126,98 @@ impl H7CAD {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── MLineStyle Dialog ─────────────────────────────────────────────
|
||||||
|
Message::MlStyleDialogOpen => {
|
||||||
|
use acadrust::objects::ObjectType;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let cur = self.tabs[i].scene.document.header.multiline_style.clone();
|
||||||
|
let exists = self.tabs[i].scene.document.objects.values().any(|o| {
|
||||||
|
matches!(o, ObjectType::MLineStyle(s) if s.name == cur)
|
||||||
|
});
|
||||||
|
self.mlstyle_selected = if exists {
|
||||||
|
cur
|
||||||
|
} else {
|
||||||
|
self.tabs[i].scene.document.objects.values()
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
Message::MlStyleDialogClose => {
|
||||||
|
self.mlstyle_open = false;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::MlStyleDialogSelect(name) => {
|
||||||
|
self.mlstyle_selected = name;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::MlStyleDialogSetCurrent => {
|
||||||
|
use acadrust::objects::ObjectType;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let name = self.mlstyle_selected.clone();
|
||||||
|
let exists = self.tabs[i].scene.document.objects.values().any(|o| {
|
||||||
|
matches!(o, ObjectType::MLineStyle(s) if s.name == name)
|
||||||
|
});
|
||||||
|
if exists {
|
||||||
|
self.push_undo_snapshot(i, "MLSTYLE SET");
|
||||||
|
self.tabs[i].scene.document.header.multiline_style = name.clone();
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
self.command_line.push_output(&format!("Current multiline style: {}", name));
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::MlStyleDialogNew => {
|
||||||
|
use acadrust::objects::ObjectType;
|
||||||
|
let i = self.active_tab;
|
||||||
|
// Generate a unique name.
|
||||||
|
let doc = &self.tabs[i].scene.document;
|
||||||
|
let mut n = 1u32;
|
||||||
|
let base = "MLS";
|
||||||
|
let new_name = loop {
|
||||||
|
let candidate = format!("{}{}", base, n);
|
||||||
|
let taken = doc.objects.values().any(|o| {
|
||||||
|
matches!(o, ObjectType::MLineStyle(s) if s.name.eq_ignore_ascii_case(&candidate))
|
||||||
|
});
|
||||||
|
if !taken { break candidate; }
|
||||||
|
n += 1;
|
||||||
|
};
|
||||||
|
self.push_undo_snapshot(i, "MLSTYLE NEW");
|
||||||
|
let mut style = acadrust::objects::MLineStyle::standard();
|
||||||
|
style.name = new_name.clone();
|
||||||
|
let nh = acadrust::Handle::new(self.tabs[i].scene.document.next_handle());
|
||||||
|
style.handle = nh;
|
||||||
|
self.tabs[i].scene.document.objects.insert(nh, ObjectType::MLineStyle(style));
|
||||||
|
self.mlstyle_selected = new_name;
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
Message::MlStyleDialogDelete => {
|
||||||
|
use acadrust::objects::ObjectType;
|
||||||
|
let i = self.active_tab;
|
||||||
|
let name = self.mlstyle_selected.clone();
|
||||||
|
if name.eq_ignore_ascii_case("Standard") {
|
||||||
|
self.command_line.push_error("Cannot delete the Standard style.");
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
let handle = self.tabs[i].scene.document.objects.iter()
|
||||||
|
.find_map(|(&h, o)| {
|
||||||
|
if let ObjectType::MLineStyle(s) = o {
|
||||||
|
if s.name == name { Some(h) } else { None }
|
||||||
|
} else { None }
|
||||||
|
});
|
||||||
|
if let Some(h) = handle {
|
||||||
|
self.push_undo_snapshot(i, "MLSTYLE DEL");
|
||||||
|
self.tabs[i].scene.document.objects.remove(&h);
|
||||||
|
// Select first remaining style.
|
||||||
|
self.mlstyle_selected = self.tabs[i].scene.document.objects.values()
|
||||||
|
.find_map(|o| if let ObjectType::MLineStyle(s) = o { Some(s.name.clone()) } else { None })
|
||||||
|
.unwrap_or_else(|| "Standard".to_string());
|
||||||
|
self.tabs[i].dirty = true;
|
||||||
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
|
||||||
// ── DimStyle Dialog ───────────────────────────────────────────────
|
// ── DimStyle Dialog ───────────────────────────────────────────────
|
||||||
Message::DimStyleDialogOpen => {
|
Message::DimStyleDialogOpen => {
|
||||||
let i = self.active_tab;
|
let i = self.active_tab;
|
||||||
|
|
|
||||||
179
src/app/view.rs
179
src/app/view.rs
|
|
@ -227,6 +227,21 @@ impl H7CAD {
|
||||||
iced::widget::Space::new().width(0).height(0).into()
|
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<String> = 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()
|
||||||
|
};
|
||||||
|
|
||||||
let dimstyle_layer: Element<'_, Message> = if self.dimstyle_open {
|
let dimstyle_layer: Element<'_, Message> = if self.dimstyle_open {
|
||||||
let tab = &self.tabs[self.active_tab];
|
let tab = &self.tabs[self.active_tab];
|
||||||
let styles: Vec<String> = tab.scene.document.dim_styles
|
let styles: Vec<String> = tab.scene.document.dim_styles
|
||||||
|
|
@ -241,7 +256,7 @@ impl H7CAD {
|
||||||
iced::widget::Space::new().width(0).height(0).into()
|
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, dimstyle_layer].into()
|
stack![main_ui, self.app_menu.view(), snap_layer, dropdown_layer, layout_ctx_layer, page_setup_layer, mlstyle_layer, dimstyle_layer].into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subscription(&self) -> Subscription<Message> {
|
pub fn subscription(&self) -> Subscription<Message> {
|
||||||
|
|
@ -1021,3 +1036,165 @@ fn dimstyle_overlay<'a>(
|
||||||
|
|
||||||
stack![catcher, positioned].into()
|
stack![catcher, positioned].into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── MLineStyle Dialog overlay ───────────────────────────────────────────────
|
||||||
|
|
||||||
|
fn mlstyle_overlay<'a>(
|
||||||
|
styles: Vec<String>,
|
||||||
|
selected: &'a str,
|
||||||
|
selected_style: Option<&'a acadrust::objects::MLineStyle>,
|
||||||
|
current_style: String,
|
||||||
|
) -> Element<'a, Message> {
|
||||||
|
use iced::Length::Shrink;
|
||||||
|
|
||||||
|
const PANEL_BG: Color = Color { r: 0.15, g: 0.15, b: 0.15, a: 1.0 };
|
||||||
|
const BORDER: Color = Color { r: 0.35, g: 0.35, b: 0.35, a: 1.0 };
|
||||||
|
const TEXT_COL: Color = Color { r: 0.88, g: 0.88, b: 0.88, a: 1.0 };
|
||||||
|
const DIM_COL: Color = Color { r: 0.55, g: 0.55, b: 0.55, a: 1.0 };
|
||||||
|
const ACCENT: Color = Color { r: 0.25, g: 0.50, b: 0.85, a: 1.0 };
|
||||||
|
const ACTIVE_BG: Color = Color { r: 0.20, g: 0.40, b: 0.70, a: 1.0 };
|
||||||
|
|
||||||
|
let btn_style = |accent: bool| move |_: &Theme, status: button::Status| button::Style {
|
||||||
|
background: Some(Background::Color(match (accent, status) {
|
||||||
|
(true, button::Status::Hovered | button::Status::Pressed) =>
|
||||||
|
Color { r: 0.20, g: 0.42, b: 0.72, a: 1.0 },
|
||||||
|
(false, button::Status::Hovered | button::Status::Pressed) =>
|
||||||
|
Color { r: 0.28, g: 0.28, b: 0.28, a: 1.0 },
|
||||||
|
(true, _) => ACCENT,
|
||||||
|
_ => Color { r: 0.22, g: 0.22, b: 0.22, a: 1.0 },
|
||||||
|
})),
|
||||||
|
text_color: TEXT_COL,
|
||||||
|
border: Border { color: BORDER, width: 1.0, radius: 4.0.into() },
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Style list.
|
||||||
|
let style_items: Vec<Element<'_, Message>> = styles.iter().map(|name| {
|
||||||
|
let is_sel = name.as_str() == selected;
|
||||||
|
let is_cur = *name == current_style;
|
||||||
|
let label = if is_cur {
|
||||||
|
format!("{} ◀", name)
|
||||||
|
} else {
|
||||||
|
name.clone()
|
||||||
|
};
|
||||||
|
button(text(label).size(11).color(TEXT_COL))
|
||||||
|
.on_press(Message::MlStyleDialogSelect(name.clone()))
|
||||||
|
.style(move |_: &Theme, st| button::Style {
|
||||||
|
background: Some(Background::Color(match (is_sel, st) {
|
||||||
|
(true, _) => ACTIVE_BG,
|
||||||
|
(false, button::Status::Hovered | button::Status::Pressed) =>
|
||||||
|
Color { r: 0.26, g: 0.26, b: 0.26, a: 1.0 },
|
||||||
|
_ => Color::TRANSPARENT,
|
||||||
|
})),
|
||||||
|
text_color: TEXT_COL,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.padding([3, 8])
|
||||||
|
.width(Fill)
|
||||||
|
.into()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let style_panel = container(
|
||||||
|
column(style_items).spacing(2)
|
||||||
|
)
|
||||||
|
.style(|_: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(Color { r: 0.12, g: 0.12, b: 0.12, a: 1.0 })),
|
||||||
|
border: Border { color: BORDER, width: 1.0, radius: 4.0.into() },
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.padding(4)
|
||||||
|
.width(160)
|
||||||
|
.height(240);
|
||||||
|
|
||||||
|
// Right panel: details for selected style.
|
||||||
|
let details: Element<'_, Message> = if let Some(s) = selected_style {
|
||||||
|
let info_row = |label: &'static str, val: String| -> Element<'_, Message> {
|
||||||
|
row![
|
||||||
|
text(label).size(11).color(DIM_COL).width(110),
|
||||||
|
text(val).size(11).color(TEXT_COL),
|
||||||
|
].spacing(8).align_y(iced::Center).into()
|
||||||
|
};
|
||||||
|
let elem_rows: Vec<Element<'_, Message>> = s.elements.iter().enumerate().map(|(idx, e)| {
|
||||||
|
let color_str: String = match &e.color {
|
||||||
|
acadrust::types::Color::ByLayer => "ByLayer".into(),
|
||||||
|
acadrust::types::Color::ByBlock => "ByBlock".into(),
|
||||||
|
acadrust::types::Color::Index(i) => format!("ACI {}", i),
|
||||||
|
acadrust::types::Color::Rgb { r, g, b } => format!("#{:02X}{:02X}{:02X}", r, g, b),
|
||||||
|
};
|
||||||
|
let lt = if e.linetype.is_empty() { "ByLayer" } else { e.linetype.as_str() };
|
||||||
|
row![
|
||||||
|
text(format!(" {}:", idx)).size(10).color(DIM_COL).width(20),
|
||||||
|
text(format!("{:+.3}", e.offset)).size(10).color(TEXT_COL).width(60),
|
||||||
|
text(color_str).size(10).color(TEXT_COL).width(80),
|
||||||
|
text(lt).size(10).color(TEXT_COL),
|
||||||
|
].spacing(4).align_y(iced::Center).into()
|
||||||
|
}).collect();
|
||||||
|
|
||||||
|
let mut col_items: Vec<Element<'_, Message>> = vec![
|
||||||
|
info_row("Name:", s.name.clone()),
|
||||||
|
info_row("Elements:", s.elements.len().to_string()),
|
||||||
|
text(" Off Color Ltype").size(10).color(DIM_COL).into(),
|
||||||
|
];
|
||||||
|
col_items.extend(elem_rows);
|
||||||
|
column(col_items).spacing(6).into()
|
||||||
|
} else {
|
||||||
|
text("No style selected.").size(11).color(DIM_COL).into()
|
||||||
|
};
|
||||||
|
|
||||||
|
let right_panel = column![
|
||||||
|
details,
|
||||||
|
Space::new().height(Fill),
|
||||||
|
row![
|
||||||
|
button(text("Set Current").size(11))
|
||||||
|
.on_press(Message::MlStyleDialogSetCurrent)
|
||||||
|
.style(btn_style(true))
|
||||||
|
.padding([5, 10]),
|
||||||
|
button(text("New").size(11))
|
||||||
|
.on_press(Message::MlStyleDialogNew)
|
||||||
|
.style(btn_style(false))
|
||||||
|
.padding([5, 10]),
|
||||||
|
button(text("Delete").size(11))
|
||||||
|
.on_press(Message::MlStyleDialogDelete)
|
||||||
|
.style(btn_style(false))
|
||||||
|
.padding([5, 10]),
|
||||||
|
].spacing(6),
|
||||||
|
]
|
||||||
|
.spacing(10)
|
||||||
|
.width(280)
|
||||||
|
.height(240);
|
||||||
|
|
||||||
|
let panel = container(
|
||||||
|
column![
|
||||||
|
row![
|
||||||
|
text("Multiline Style Manager").size(13).color(TEXT_COL),
|
||||||
|
Space::new().width(Fill),
|
||||||
|
button(text("✕").size(12).color(DIM_COL))
|
||||||
|
.on_press(Message::MlStyleDialogClose)
|
||||||
|
.style(|_: &Theme, _| button::Style {
|
||||||
|
background: Some(Background::Color(Color::TRANSPARENT)),
|
||||||
|
text_color: DIM_COL,
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.padding([2, 6]),
|
||||||
|
].align_y(iced::Center),
|
||||||
|
row![style_panel, right_panel].spacing(12).align_y(iced::Top),
|
||||||
|
].spacing(10).padding(16)
|
||||||
|
)
|
||||||
|
.style(|_: &Theme| container::Style {
|
||||||
|
background: Some(Background::Color(PANEL_BG)),
|
||||||
|
border: Border { color: BORDER, width: 1.0, radius: 6.0.into() },
|
||||||
|
..Default::default()
|
||||||
|
})
|
||||||
|
.width(Shrink);
|
||||||
|
|
||||||
|
let catcher = mouse_area(
|
||||||
|
container(iced::widget::Space::new().width(Fill).height(Fill))
|
||||||
|
).on_press(Message::MlStyleDialogClose);
|
||||||
|
|
||||||
|
let positioned = container(panel)
|
||||||
|
.width(Fill).height(Fill)
|
||||||
|
.align_x(iced::Alignment::Center)
|
||||||
|
.align_y(iced::Alignment::Center);
|
||||||
|
|
||||||
|
stack![catcher, positioned].into()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,11 +17,17 @@ pub struct MlineCommand {
|
||||||
points: Vec<Vec3>,
|
points: Vec<Vec3>,
|
||||||
scale: f64,
|
scale: f64,
|
||||||
waiting_scale: bool,
|
waiting_scale: bool,
|
||||||
|
style_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MlineCommand {
|
impl MlineCommand {
|
||||||
|
#[allow(dead_code)]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self { points: vec![], scale: 1.0, waiting_scale: false }
|
Self { points: vec![], scale: 1.0, waiting_scale: false, style_name: "Standard".into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_style(style_name: impl Into<String>) -> Self {
|
||||||
|
Self { points: vec![], scale: 1.0, waiting_scale: false, style_name: style_name.into() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -55,7 +61,7 @@ impl CadCommand for MlineCommand {
|
||||||
|
|
||||||
// Close command
|
// Close command
|
||||||
if (up == "C" || up == "CLOSE") && self.points.len() >= 3 {
|
if (up == "C" || up == "CLOSE") && self.points.len() >= 3 {
|
||||||
let entity = build_mline(&self.points, self.scale, true);
|
let entity = build_mline(&self.points, self.scale, true, &self.style_name);
|
||||||
return Some(CmdResult::CommitAndExit(entity));
|
return Some(CmdResult::CommitAndExit(entity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -85,7 +91,7 @@ impl CadCommand for MlineCommand {
|
||||||
if self.points.len() < 2 {
|
if self.points.len() < 2 {
|
||||||
return CmdResult::Cancel;
|
return CmdResult::Cancel;
|
||||||
}
|
}
|
||||||
let entity = build_mline(&self.points, self.scale, false);
|
let entity = build_mline(&self.points, self.scale, false, &self.style_name);
|
||||||
CmdResult::CommitAndExit(entity)
|
CmdResult::CommitAndExit(entity)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -104,12 +110,13 @@ impl CadCommand for MlineCommand {
|
||||||
line_weight_px: 1.0,
|
line_weight_px: 1.0,
|
||||||
snap_pts: vec![],
|
snap_pts: vec![],
|
||||||
tangent_geoms: vec![],
|
tangent_geoms: vec![],
|
||||||
|
aci: 0,
|
||||||
key_vertices: vec![],
|
key_vertices: vec![],
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_mline(pts: &[Vec3], scale: f64, closed: bool) -> EntityType {
|
fn build_mline(pts: &[Vec3], scale: f64, closed: bool, style_name: &str) -> EntityType {
|
||||||
let verts: Vec<Vector3> = pts.iter()
|
let verts: Vec<Vector3> = pts.iter()
|
||||||
.map(|p| Vector3::new(p.x as f64, p.z as f64, p.y as f64))
|
.map(|p| Vector3::new(p.x as f64, p.z as f64, p.y as f64))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
@ -119,5 +126,6 @@ fn build_mline(pts: &[Vec3], scale: f64, closed: bool) -> EntityType {
|
||||||
MLine::from_points(&verts)
|
MLine::from_points(&verts)
|
||||||
};
|
};
|
||||||
mline.scale_factor = scale;
|
mline.scale_factor = scale;
|
||||||
|
mline.style_name = style_name.to_string();
|
||||||
EntityType::MLine(mline)
|
EntityType::MLine(mline)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue