forked from 1b-agent/opencadstudio-upstream
ATTDEF: dialog, -ATTDEF prompts, tag display, Properties and ribbon
Attribute Definition dialog and Edit Attribute Definition dialog, -ATTDEF with the reference prompts and modes (AFLAGS), tag shown in the drawing, justification-aware grips, reworked Properties with validation, Home Block slide-out and Insert Block Definition panel, and the codec patch for ATTDEF DXF output and the R2013+ solid wire cache. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
3dcebf0dc6
commit
c5790c74a9
48 files changed
+2501
-435
No files matched your search
+2
-1
@@ -80,7 +80,7 @@ const WEB_ALIAS_KEY: &str = "opencadstudio.aliases";
|
||||
/// migrated forward (see `introduced_at` / `migrate_aliases`). Version 1 shipped
|
||||
/// the pre-REDRAW table; version 2 introduced the REDRAW-family aliases; version 3
|
||||
/// introduced HB (HATCHTOBACK); version 4 introduced ER (EXTERNALREFERENCES).
|
||||
const DEFAULT_ALIASES_VERSION: u32 = 4;
|
||||
const DEFAULT_ALIASES_VERSION: u32 = 5;
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
const WEB_ALIAS_VERSION_KEY: &str = "opencadstudio.aliases.version";
|
||||
@@ -99,6 +99,7 @@ fn introduced_at(version: u32) -> &'static [(&'static str, &'static str)] {
|
||||
],
|
||||
3 => &[("HB", "HATCHTOBACK")],
|
||||
4 => &[("ER", "EXTERNALREFERENCES")],
|
||||
5 => &[("-ATT", "-ATTDEF")],
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
//! ATTDEF (dialog), -ATTDEF (command line), AFLAGS, and the Edit Attribute
|
||||
//! Definition dialog opened by double-click / TEXTEDIT / DDEDIT.
|
||||
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::draw::draw::attdef::{
|
||||
self as attdef, AttdefCommand, AttdefPickCommand, AttdefPlaceCommand, AttdefSpec, Justify,
|
||||
};
|
||||
use crate::ui::window::attdef_dialog::{
|
||||
tag_error, AttdefDialogMsg, AttdefDialogState, AttdefEditState,
|
||||
};
|
||||
use iced::Task;
|
||||
|
||||
fn short(v: f64) -> String {
|
||||
let t = format!("{v:.4}");
|
||||
t.trim_end_matches('0').trim_end_matches('.').to_string()
|
||||
}
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_attdef(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
if matches!(cmd, "ATTDEF" | "-ATTDEF") {
|
||||
// Placement measures the tag with its style's font.
|
||||
crate::entities::common::set_fixed_text_heights(&self.tabs[i].scene.document);
|
||||
}
|
||||
match cmd {
|
||||
"ATTDEF" => {
|
||||
self.open_attdef_dialog(i);
|
||||
Some(Task::none())
|
||||
}
|
||||
"-ATTDEF" => {
|
||||
let defaults =
|
||||
crate::scene::creation_style::current_text_defaults(&self.tabs[i].scene.document);
|
||||
let styles: Vec<_> = self.tabs[i].scene.document.text_styles.iter().cloned().collect();
|
||||
let command = AttdefCommand::new(
|
||||
"-ATTDEF",
|
||||
defaults.style_name,
|
||||
defaults.height,
|
||||
defaults.width_factor,
|
||||
defaults.oblique_angle,
|
||||
styles,
|
||||
);
|
||||
self.reset_command_start_state(i);
|
||||
self.command_line.push_output(&command.intro());
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
self.push_ucs_to_cmd(i);
|
||||
Some(Task::none())
|
||||
}
|
||||
cmd if cmd == "_ATTDEF_PICKED" || cmd.starts_with("_ATTDEF_PICKED ") => {
|
||||
let mut parts = cmd.split_whitespace().skip(1);
|
||||
if let (Some(kind), Some(value), Some(state)) =
|
||||
(parts.next(), parts.next(), self.attdef_dialog.as_mut())
|
||||
{
|
||||
match kind {
|
||||
"H" => state.height = value.to_string(),
|
||||
"R" => state.rotation = value.to_string(),
|
||||
_ => state.width = value.to_string(),
|
||||
}
|
||||
}
|
||||
if self.attdef_dialog.is_some() {
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
_ => self.dispatch_aflags(cmd),
|
||||
}
|
||||
}
|
||||
|
||||
/// AFLAGS: the session's attribute modes (0–63, default 16).
|
||||
fn dispatch_aflags(&mut self, cmd: &str) -> Option<Task<Message>> {
|
||||
let rest = cmd.strip_prefix("SETVAR ").map(str::trim).unwrap_or(cmd);
|
||||
let mut parts = rest.splitn(2, char::is_whitespace);
|
||||
if !parts.next().unwrap_or("").eq_ignore_ascii_case("AFLAGS") {
|
||||
return None;
|
||||
}
|
||||
let current = attdef::session().aflags;
|
||||
match parts.next().map(str::trim).filter(|v| !v.is_empty()) {
|
||||
None => {
|
||||
self.command_line
|
||||
.push_output(&format!("Enter new value for AFLAGS <{current}>:"));
|
||||
self.pending_setvar = Some("AFLAGS".to_string());
|
||||
}
|
||||
Some(v) => match v.parse::<u8>().ok().filter(|v| *v <= 63) {
|
||||
Some(v) => attdef::session().aflags = v,
|
||||
None => {
|
||||
self.command_line.push_error("Requires an integer between 0 and 63.");
|
||||
self.command_line
|
||||
.push_output(&format!("Enter new value for AFLAGS <{current}>:"));
|
||||
self.pending_setvar = Some("AFLAGS".to_string());
|
||||
}
|
||||
},
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
|
||||
fn open_attdef_dialog(&mut self, i: usize) {
|
||||
let document = &self.tabs[i].scene.document;
|
||||
let defaults = crate::scene::creation_style::current_text_defaults(document);
|
||||
let mut styles: Vec<String> = document
|
||||
.text_styles
|
||||
.iter()
|
||||
.filter(|s| !s.name.is_empty())
|
||||
.map(|s| s.name.clone())
|
||||
.collect();
|
||||
styles.sort_by_key(|s| s.to_lowercase());
|
||||
let session = attdef::session();
|
||||
self.attdef_dialog = Some(AttdefDialogState {
|
||||
aflags: session.aflags,
|
||||
annotative: session.annotative,
|
||||
tag: String::new(),
|
||||
prompt: String::new(),
|
||||
default: String::new(),
|
||||
justify: Justify::Left,
|
||||
style: defaults.style_name,
|
||||
styles,
|
||||
height: short(defaults.height),
|
||||
rotation: "0".into(),
|
||||
width: "0".into(),
|
||||
on_screen: true,
|
||||
coords: ["0".into(), "0".into(), "0".into()],
|
||||
align_below: false,
|
||||
can_align_below: session.last.is_some(),
|
||||
});
|
||||
drop(session);
|
||||
self.tabs[i].active_cmd = None;
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
|
||||
/// The Edit Attribute Definition dialog for `handle`.
|
||||
pub(in crate::app) fn open_attdef_edit(&mut self, handle: codec::Handle) {
|
||||
let i = self.active_tab;
|
||||
let Some(codec::EntityType::AttributeDefinition(a)) = self.tabs[i].scene.document.get_entity(handle)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
self.attdef_edit = Some(AttdefEditState {
|
||||
handle,
|
||||
tag: a.tag.clone(),
|
||||
prompt: a.prompt.clone(),
|
||||
default: a.default_value.clone(),
|
||||
constant: a.flags.constant,
|
||||
});
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDefEdit);
|
||||
}
|
||||
|
||||
pub(in crate::app) fn on_attdef_dialog(&mut self, m: AttdefDialogMsg) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
if let Some(edit) = self.attdef_edit.as_mut() {
|
||||
match m {
|
||||
AttdefDialogMsg::EditTag(v) => edit.tag = v,
|
||||
AttdefDialogMsg::EditPrompt(v) => edit.prompt = v,
|
||||
AttdefDialogMsg::EditDefault(v) => edit.default = v,
|
||||
AttdefDialogMsg::Help => self.command_line.push_info(
|
||||
crate::t!("Changes the tag, prompt and default value of an attribute definition.")
|
||||
.as_ref(),
|
||||
),
|
||||
AttdefDialogMsg::EditOk => return self.attdef_edit_ok(i),
|
||||
_ => {}
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
let Some(state) = self.attdef_dialog.as_mut() else {
|
||||
return Task::none();
|
||||
};
|
||||
match m {
|
||||
AttdefDialogMsg::Mode(bit, on) => {
|
||||
if on {
|
||||
state.aflags |= bit;
|
||||
} else {
|
||||
state.aflags &= !bit;
|
||||
}
|
||||
}
|
||||
AttdefDialogMsg::Annotative(v) => state.annotative = v,
|
||||
AttdefDialogMsg::Tag(v) => state.tag = v,
|
||||
AttdefDialogMsg::Prompt(v) => state.prompt = v,
|
||||
AttdefDialogMsg::Default(v) => state.default = v,
|
||||
AttdefDialogMsg::Justify(c) => state.justify = c.0,
|
||||
AttdefDialogMsg::Style(v) => state.style = v,
|
||||
AttdefDialogMsg::Height(v) => state.height = v,
|
||||
AttdefDialogMsg::Rotation(v) => state.rotation = v,
|
||||
AttdefDialogMsg::Width(v) => state.width = v,
|
||||
AttdefDialogMsg::OnScreen(v) => state.on_screen = v,
|
||||
AttdefDialogMsg::Coord(k, v) => state.coords[k] = v,
|
||||
AttdefDialogMsg::AlignBelow(v) => state.align_below = v,
|
||||
AttdefDialogMsg::PickHeight | AttdefDialogMsg::PickRotation | AttdefDialogMsg::PickWidth => {
|
||||
let (kind, current) = match m {
|
||||
AttdefDialogMsg::PickHeight => ("H", state.height.clone()),
|
||||
AttdefDialogMsg::PickRotation => ("R", state.rotation.clone()),
|
||||
_ => ("W", state.width.clone()),
|
||||
};
|
||||
// Keep the dialog's fields; it reopens when the value is in.
|
||||
self.active_modal = None;
|
||||
let command = AttdefPickCommand::new(kind, current);
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
self.push_ucs_to_cmd(i);
|
||||
return self.focus_cmd_input();
|
||||
}
|
||||
AttdefDialogMsg::Help => self.command_line.push_info(
|
||||
crate::t!("Defines an attribute: a tag, prompt and default value placed as text in a block definition.")
|
||||
.as_ref(),
|
||||
),
|
||||
AttdefDialogMsg::Ok => return self.attdef_dialog_ok(i),
|
||||
_ => {}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn attdef_dialog_ok(&mut self, i: usize) -> Task<Message> {
|
||||
let Some(state) = self.attdef_dialog.clone() else {
|
||||
return Task::none();
|
||||
};
|
||||
if let Some(error) = tag_error(&state.tag) {
|
||||
self.command_line.push_error(error);
|
||||
return Task::none();
|
||||
}
|
||||
let number = |s: &str| s.trim().parse::<f64>().ok().filter(|v| v.is_finite());
|
||||
let height = match number(&state.height) {
|
||||
Some(v) if v > 0.0 => v,
|
||||
_ => {
|
||||
self.command_line.push_error("Value must be positive and nonzero.");
|
||||
return Task::none();
|
||||
}
|
||||
};
|
||||
let Some(rotation) = number(&state.rotation) else {
|
||||
self.command_line.push_error("Requires valid numeric angle or second point.");
|
||||
return Task::none();
|
||||
};
|
||||
let width = number(&state.width).unwrap_or(0.0).max(0.0);
|
||||
let defaults =
|
||||
crate::scene::creation_style::current_text_defaults(&self.tabs[i].scene.document);
|
||||
let spec = AttdefSpec {
|
||||
aflags: state.aflags,
|
||||
annotative: state.annotative,
|
||||
tag: state.tag.clone(),
|
||||
prompt: state.prompt.clone(),
|
||||
value: state.default.clone(),
|
||||
justify: state.justify,
|
||||
style: state.style.clone(),
|
||||
height,
|
||||
rotation: rotation.to_radians(),
|
||||
width_factor: defaults.width_factor,
|
||||
oblique_angle: defaults.oblique_angle,
|
||||
boundary_width: width,
|
||||
line_spacing: 1.0,
|
||||
};
|
||||
self.attdef_dialog = None;
|
||||
self.close_active_modal();
|
||||
{
|
||||
let mut session = attdef::session();
|
||||
session.aflags = spec.aflags;
|
||||
session.annotative = spec.annotative;
|
||||
}
|
||||
self.reset_command_start_state(i);
|
||||
if state.align_below {
|
||||
let previous = attdef::session().last.clone();
|
||||
if let Some(previous) = previous {
|
||||
let entity = attdef::build_below(&spec, &previous);
|
||||
self.tabs[i].active_cmd = Some(Box::new(AttdefPlaceCommand::new(spec.clone())));
|
||||
attdef::remember(&spec, &entity);
|
||||
return self.apply_cmd_result(crate::command::CmdResult::CommitAndExit(entity));
|
||||
}
|
||||
}
|
||||
let plane = if self.tabs[i].editing_model_space() {
|
||||
self.tabs[i].ucs_xform().working_plane()
|
||||
} else {
|
||||
crate::command::WorkingPlane::default()
|
||||
};
|
||||
let mut command = AttdefPlaceCommand::new(spec);
|
||||
command.set_working_plane(plane);
|
||||
if state.on_screen {
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
return self.focus_cmd_input();
|
||||
}
|
||||
let local = glam::DVec3::new(
|
||||
number(&state.coords[0]).unwrap_or(0.0),
|
||||
number(&state.coords[1]).unwrap_or(0.0),
|
||||
number(&state.coords[2]).unwrap_or(0.0),
|
||||
);
|
||||
let result = command.on_point(plane.to_world(local));
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
self.apply_cmd_result(result)
|
||||
}
|
||||
|
||||
fn attdef_edit_ok(&mut self, i: usize) -> Task<Message> {
|
||||
let Some(state) = self.attdef_edit.clone() else {
|
||||
return Task::none();
|
||||
};
|
||||
if let Some(error) = tag_error(&state.tag) {
|
||||
self.command_line.push_error(error);
|
||||
return Task::none();
|
||||
}
|
||||
self.attdef_edit = None;
|
||||
self.close_active_modal();
|
||||
let changed = matches!(
|
||||
self.tabs[i].scene.document.get_entity(state.handle),
|
||||
Some(codec::EntityType::AttributeDefinition(a))
|
||||
if a.tag != attdef::normalize_tag(&state.tag) || a.prompt != state.prompt || a.default_value != state.default
|
||||
);
|
||||
if changed && !self.reject_locked_edit(i, state.handle) {
|
||||
self.push_undo_snapshot(i, "DDEDIT");
|
||||
if let Some(codec::EntityType::AttributeDefinition(a)) =
|
||||
self.tabs[i].scene.document.get_entity_mut(state.handle)
|
||||
{
|
||||
a.tag = attdef::normalize_tag(&state.tag);
|
||||
if !a.flags.constant {
|
||||
a.prompt = state.prompt.clone();
|
||||
}
|
||||
a.default_value = state.default.clone();
|
||||
}
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.bump_entities(&[(state.handle, crate::scene::ChangeKind::Modified)]);
|
||||
self.tabs[i].dirty = true;
|
||||
self.refresh_properties();
|
||||
}
|
||||
self.post_editor_closed(changed)
|
||||
}
|
||||
}
|
||||
@@ -79,21 +79,6 @@ impl OpenCADStudio {
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
}
|
||||
|
||||
"ATTDEF" => {
|
||||
use crate::modules::draw::draw::attdef::AttdefCommand;
|
||||
let defaults = crate::scene::creation_style::current_text_defaults(
|
||||
&self.tabs[i].scene.document,
|
||||
);
|
||||
let cmd = AttdefCommand::with_text_defaults(
|
||||
defaults.height,
|
||||
defaults.style_name,
|
||||
defaults.width_factor,
|
||||
defaults.oblique_angle,
|
||||
);
|
||||
self.command_line.push_info(&cmd.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(cmd));
|
||||
}
|
||||
|
||||
// Command-line attribute editing on selected Insert entities. Bare
|
||||
// ATTEDIT and the ATE alias launch the interactive editor instead
|
||||
// (see the ATTEDIT arm in the inquiry family); the dash form is the
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::scene::Scene;
|
||||
use iced::Task;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod attdef;
|
||||
mod blocks;
|
||||
mod xref_attach;
|
||||
mod dim;
|
||||
@@ -278,6 +279,9 @@ impl OpenCADStudio {
|
||||
if let Some(t) = self.dispatch_pc_extract(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_attdef(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_draw(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ pub(super) const NAMES: &[&str] = &[
|
||||
"ribbon_tab",
|
||||
"ribbon_dropdown",
|
||||
"dialog_ok",
|
||||
"attdef_dialog",
|
||||
"close_document",
|
||||
"toggle_properties",
|
||||
"toggle_layers",
|
||||
@@ -788,6 +789,47 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Ok(Task::none());
|
||||
}
|
||||
// One field of the open attribute definition dialog: `tag=…`,
|
||||
// `prompt=…`, `default=…`, `justify=MC`, `style=…`, `height=…`,
|
||||
// `rotation=…`, `width=…`, `on_screen=0|1`, `x=…` / `y=…` / `z=…`,
|
||||
// `mode=<bit>:0|1`, `annotative=0|1`, `align_below=0|1`.
|
||||
"attdef_dialog" => {
|
||||
use crate::ui::window::attdef_dialog::{AttdefDialogMsg as M, JustifyChoice};
|
||||
let value = string(req, "value")?;
|
||||
let (key, v) = value.split_once('=').unwrap_or((value, ""));
|
||||
let on = v == "1";
|
||||
let editing = self.active_modal == Some(crate::app::ModalKind::AttDefEdit);
|
||||
Message::AttdefDialog(match key {
|
||||
"tag" if editing => M::EditTag(v.into()),
|
||||
"prompt" if editing => M::EditPrompt(v.into()),
|
||||
"default" if editing => M::EditDefault(v.into()),
|
||||
"tag" => M::Tag(v.into()),
|
||||
"prompt" => M::Prompt(v.into()),
|
||||
"default" => M::Default(v.into()),
|
||||
"style" => M::Style(v.into()),
|
||||
"height" => M::Height(v.into()),
|
||||
"rotation" => M::Rotation(v.into()),
|
||||
"width" => M::Width(v.into()),
|
||||
"on_screen" => M::OnScreen(on),
|
||||
"annotative" => M::Annotative(on),
|
||||
"align_below" => M::AlignBelow(on),
|
||||
"x" => M::Coord(0, v.into()),
|
||||
"y" => M::Coord(1, v.into()),
|
||||
"z" => M::Coord(2, v.into()),
|
||||
"justify" => M::Justify(JustifyChoice(
|
||||
crate::modules::draw::draw::attdef::Justify::from_keyword(v)
|
||||
.ok_or_else(|| failure("invalid_value", "Unknown justification"))?,
|
||||
)),
|
||||
"mode" => {
|
||||
let (bit, state) = v.split_once(':').unwrap_or((v, "1"));
|
||||
let bit = bit
|
||||
.parse::<u8>()
|
||||
.map_err(|_| failure("invalid_value", "mode=<bit>:0|1"))?;
|
||||
M::Mode(bit, state == "1")
|
||||
}
|
||||
_ => return Err(failure("invalid_value", "Unknown attdef dialog field")),
|
||||
})
|
||||
}
|
||||
"ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()),
|
||||
"pdf_dialog_ok" => {
|
||||
use crate::ui::window::pdf_dialogs::PdfDialogMsg;
|
||||
@@ -808,6 +850,12 @@ impl OpenCADStudio {
|
||||
crate::ui::window::xref_attach::XrefAttachMsg::Apply,
|
||||
),
|
||||
Some(crate::app::ModalKind::BlockDefinition) => Message::BlockDefApply,
|
||||
Some(crate::app::ModalKind::AttDef) => Message::AttdefDialog(
|
||||
crate::ui::window::attdef_dialog::AttdefDialogMsg::Ok,
|
||||
),
|
||||
Some(crate::app::ModalKind::AttDefEdit) => Message::AttdefDialog(
|
||||
crate::ui::window::attdef_dialog::AttdefDialogMsg::EditOk,
|
||||
),
|
||||
_ => return Err(failure("no_dialog", "No dialog with an OK button is open")),
|
||||
},
|
||||
"close_document" => Message::TabClose(self.tabs[self.active_tab].id),
|
||||
|
||||
@@ -498,6 +498,9 @@ pub(super) struct OpenCADStudio {
|
||||
/// PDF dialogs' working copies; `None` while closed.
|
||||
pdf_attach: Option<crate::ui::window::pdf_dialogs::PdfAttachState>,
|
||||
point_cloud_attach: Option<crate::ui::window::pdf_dialogs::PointCloudAttachState>,
|
||||
/// ATTDEF dialog and Edit Attribute Definition dialog; `None` while closed.
|
||||
attdef_dialog: Option<crate::ui::window::attdef_dialog::AttdefDialogState>,
|
||||
attdef_edit: Option<crate::ui::window::attdef_dialog::AttdefEditState>,
|
||||
point_cloud_color_map: Option<crate::ui::window::pdf_dialogs::PointCloudColorMapState>,
|
||||
pc_section: Option<crate::ui::window::pdf_dialogs::PcSectionState>,
|
||||
underlay_layers: Option<crate::ui::window::pdf_dialogs::UnderlayLayersState>,
|
||||
@@ -1917,6 +1920,8 @@ pub enum ModalKind {
|
||||
XrefAttach,
|
||||
WriteBlock,
|
||||
GeometricTolerance,
|
||||
AttDef,
|
||||
AttDefEdit,
|
||||
DraftingSettings,
|
||||
AutoConstrainSettings,
|
||||
LayerStateEditor,
|
||||
@@ -3864,6 +3869,7 @@ pub enum Message {
|
||||
PdfImportPick,
|
||||
/// An edit in one of the PDF dialogs.
|
||||
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
|
||||
AttdefDialog(crate::ui::window::attdef_dialog::AttdefDialogMsg),
|
||||
/// A click or search in the Point Cloud Manager.
|
||||
PcManager(crate::ui::window::pc_manager::PcManagerMsg),
|
||||
PdfImportPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
||||
@@ -4057,6 +4063,8 @@ impl OpenCADStudio {
|
||||
block_definition: None,
|
||||
pdf_attach: None,
|
||||
point_cloud_attach: None,
|
||||
attdef_dialog: None,
|
||||
attdef_edit: None,
|
||||
point_cloud_color_map: None,
|
||||
pc_section: None,
|
||||
underlay_layers: None,
|
||||
|
||||
@@ -155,6 +155,13 @@ impl super::OpenCADStudio {
|
||||
self.open_tolerance_dialog(Some(target));
|
||||
return iced::Task::none();
|
||||
}
|
||||
if matches!(
|
||||
self.tabs[i].scene.document.get_entity(target),
|
||||
Some(EntityType::AttributeDefinition(_))
|
||||
) {
|
||||
self.open_attdef_edit(target);
|
||||
return iced::Task::none();
|
||||
}
|
||||
// Snapshot what we need before borrowing `self` mutably to open.
|
||||
let Some(entity) = self.tabs[i].scene.document.get_entity(target) else {
|
||||
return iced::Task::none();
|
||||
|
||||
@@ -3627,6 +3627,14 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
if field.starts_with("att_") {
|
||||
if let Err(message) =
|
||||
crate::entities::attribute::validate_definition_property(field, &val)
|
||||
{
|
||||
self.command_line.push_error(message);
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
if field.starts_with("ul_") {
|
||||
if let Err(message) =
|
||||
crate::entities::underlay::validate_property(field, &val)
|
||||
|
||||
@@ -1806,6 +1806,7 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
||||
Message::AttdefDialog(message) => self.on_attdef_dialog(message),
|
||||
Message::PcManager(message) => self.update_pc_manager(message),
|
||||
Message::RibbonSelectTab(idx) => {
|
||||
self.ribbon.select(idx);
|
||||
@@ -8496,6 +8497,15 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::CloseModal => {
|
||||
if matches!(
|
||||
self.active_modal,
|
||||
Some(super::ModalKind::AttDef | super::ModalKind::AttDefEdit)
|
||||
) {
|
||||
self.attdef_dialog = None;
|
||||
let editing = self.attdef_edit.take().is_some();
|
||||
self.close_active_modal();
|
||||
return if editing { self.post_editor_closed(false) } else { Task::none() };
|
||||
}
|
||||
// Cancelling the section settings drops the waiting extraction.
|
||||
if self.active_modal == Some(super::ModalKind::PcSection) {
|
||||
crate::modules::insert::pc_extract::drop_job();
|
||||
|
||||
@@ -38,6 +38,8 @@ impl OpenCADStudio {
|
||||
Some(K::XrefAttach) => crate::t!("Attach External Reference").into_owned(),
|
||||
Some(K::WriteBlock) => crate::t!("Write Block").into_owned(),
|
||||
Some(K::GeometricTolerance) => crate::t!("Geometric Tolerance").into_owned(),
|
||||
Some(K::AttDef) => crate::t!("Attribute Definition").into_owned(),
|
||||
Some(K::AttDefEdit) => crate::t!("Edit Attribute Definition").into_owned(),
|
||||
Some(K::DraftingSettings) => crate::t!("Drafting Settings").into_owned(),
|
||||
Some(K::AutoConstrainSettings) => crate::t!("Constraint Settings").into_owned(),
|
||||
Some(K::LayerStateEditor) => crate::tr!("modal", "edit-layer-state"),
|
||||
@@ -572,6 +574,14 @@ impl OpenCADStudio {
|
||||
crate::ui::window::wblock::view_window(state, flow)
|
||||
})
|
||||
}
|
||||
super::super::ModalKind::AttDef => {
|
||||
let state = self.attdef_dialog.as_ref()?;
|
||||
sized_flow(ex, 720, 520, |flow| crate::ui::window::attdef_dialog::view(state, flow))
|
||||
}
|
||||
super::super::ModalKind::AttDefEdit => {
|
||||
let state = self.attdef_edit.as_ref()?;
|
||||
sized_flow(ex, 460, 220, |flow| crate::ui::window::attdef_dialog::view_edit(state, flow))
|
||||
}
|
||||
super::super::ModalKind::GeometricTolerance => {
|
||||
let state = self.geometric_tolerance.as_ref()?;
|
||||
sized_flow(ex, 670, 530, |flow| {
|
||||
|
||||
Reference in new issue
Block a user