forked from 1b-agent/opencadstudio-upstream
Merge PR #1585 from ramox81/codex/field-command: FIELD command and Field dialog; attribute definition field buttons
The PR pinned the codec to its fork through [patch]; codec #75 is merged upstream and main already points at it, so the patch section is dropped. The two windows-sys features it adds (Globalization, Identity) stay.
This commit is contained in:
commit
0de1e91f99
45 files changed
+2456
-15
No files matched your search
@@ -226,8 +226,17 @@ impl OpenCADStudio {
|
||||
self.tabs[i].scene.document.header.text_height = attribute.height;
|
||||
}
|
||||
}
|
||||
// A field the committed text shows (FIELD, an ATTDEF Default from the
|
||||
// Field dialog) is attached once the entity has its handle; the field
|
||||
// objects are not part of the add delta, so the step is a snapshot.
|
||||
let text_field = self.tabs[i].active_cmd.as_ref().and_then(|cmd| cmd.text_field());
|
||||
let label = self.history_label_from_active_cmd(i, "ENTITY");
|
||||
let delta_safe = self.delta_add_safe(i, &entity);
|
||||
let delta_safe = text_field.is_none() && self.delta_add_safe(i, &entity);
|
||||
let field_value = match &entity {
|
||||
codec::EntityType::MText(m) => m.value.clone(),
|
||||
codec::EntityType::AttributeDefinition(a) => a.default_value.clone(),
|
||||
_ => String::new(),
|
||||
};
|
||||
let pending = self.begin_undo(i, label, 1, delta_safe);
|
||||
let is_associative_dimension = matches!(
|
||||
entity,
|
||||
@@ -247,6 +256,14 @@ impl OpenCADStudio {
|
||||
.attach_dimension_association(handle, sources);
|
||||
}
|
||||
}
|
||||
if let (Some(handle), Some(field)) = (committed, text_field) {
|
||||
crate::entities::field::set_text_field(
|
||||
&mut self.tabs[i].scene.document,
|
||||
handle,
|
||||
Some(field),
|
||||
&field_value,
|
||||
);
|
||||
}
|
||||
if let Some(handle) = committed {
|
||||
self.apply_continuous_constraints(i, &[handle]);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ impl OpenCADStudio {
|
||||
if let Some(state) = self.attdef_dialog.as_mut() {
|
||||
if let Some(value) = value {
|
||||
state.default = value;
|
||||
state.field = None;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
@@ -134,6 +135,7 @@ impl OpenCADStudio {
|
||||
align_below: false,
|
||||
can_align_below: session.last.is_some(),
|
||||
error: None,
|
||||
field: None,
|
||||
});
|
||||
drop(session);
|
||||
self.tabs[i].active_cmd = None;
|
||||
@@ -154,6 +156,8 @@ impl OpenCADStudio {
|
||||
default: a.default_value.clone(),
|
||||
constant: a.flags.constant,
|
||||
error: None,
|
||||
field: None,
|
||||
field_changed: false,
|
||||
});
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDefEdit);
|
||||
}
|
||||
@@ -164,7 +168,15 @@ impl OpenCADStudio {
|
||||
match m {
|
||||
AttdefDialogMsg::EditTag(v) => edit.tag = v,
|
||||
AttdefDialogMsg::EditPrompt(v) => edit.prompt = v,
|
||||
AttdefDialogMsg::EditDefault(v) => edit.default = v,
|
||||
AttdefDialogMsg::EditDefault(v) => {
|
||||
edit.default = v;
|
||||
edit.field = None;
|
||||
edit.field_changed = true;
|
||||
}
|
||||
AttdefDialogMsg::EditInsertField => {
|
||||
self.active_modal = None;
|
||||
self.open_field_dialog(crate::ui::window::field_dialog::FieldTarget::AttdefEdit);
|
||||
}
|
||||
AttdefDialogMsg::Help => self.command_line.push_info(
|
||||
crate::t!("Changes the tag, prompt and default value of an attribute definition.")
|
||||
.as_ref(),
|
||||
@@ -189,7 +201,15 @@ impl OpenCADStudio {
|
||||
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::Default(v) => {
|
||||
state.default = v;
|
||||
state.field = None;
|
||||
}
|
||||
AttdefDialogMsg::InsertField => {
|
||||
self.active_modal = None;
|
||||
self.open_field_dialog(crate::ui::window::field_dialog::FieldTarget::AttdefDefault);
|
||||
return Task::none();
|
||||
}
|
||||
AttdefDialogMsg::Justify(c) => state.justify = c.0,
|
||||
AttdefDialogMsg::Style(v) => state.style = v,
|
||||
AttdefDialogMsg::Height(v) => state.height = v,
|
||||
@@ -279,6 +299,7 @@ impl OpenCADStudio {
|
||||
oblique_angle: defaults.oblique_angle,
|
||||
boundary_width: width,
|
||||
line_spacing: 1.0,
|
||||
field: state.field.clone(),
|
||||
};
|
||||
self.attdef_dialog = None;
|
||||
self.close_active_modal();
|
||||
@@ -288,6 +309,8 @@ impl OpenCADStudio {
|
||||
session.annotative = spec.annotative;
|
||||
session.on_screen = state.on_screen;
|
||||
}
|
||||
// The choice is kept with the user settings, as the reference keeps it.
|
||||
self.persist_settings_if_changed();
|
||||
self.reset_command_start_state(i);
|
||||
if state.align_below {
|
||||
let previous = attdef::session().last.clone();
|
||||
@@ -336,7 +359,7 @@ impl OpenCADStudio {
|
||||
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
|
||||
);
|
||||
) || state.field_changed;
|
||||
if changed && !self.reject_locked_edit(i, state.handle) {
|
||||
self.push_undo_snapshot(i, "DDEDIT");
|
||||
if let Some(codec::EntityType::AttributeDefinition(a)) =
|
||||
@@ -348,6 +371,14 @@ impl OpenCADStudio {
|
||||
}
|
||||
a.default_value = state.default.clone();
|
||||
}
|
||||
if state.field_changed {
|
||||
crate::entities::field::set_text_field(
|
||||
&mut self.tabs[i].scene.document,
|
||||
state.handle,
|
||||
state.field.clone(),
|
||||
&state.default,
|
||||
);
|
||||
}
|
||||
self.tabs[i]
|
||||
.scene
|
||||
.bump_entities(&[(state.handle, crate::scene::ChangeKind::Modified)]);
|
||||
|
||||
@@ -0,0 +1,228 @@
|
||||
//! FIELD and the Field dialog behind the ƒ buttons of the attribute
|
||||
//! definition dialogs.
|
||||
|
||||
use crate::app::{Message, OpenCADStudio};
|
||||
use crate::command::CadCommand;
|
||||
use crate::modules::annotate::field_cmd::{FieldObjectPickCommand, FieldPlaceCommand};
|
||||
use crate::ui::window::field_dialog::{
|
||||
fields_of, object_properties, FieldDialogMsg, FieldDialogState, FieldTarget, DATE_FORMATS,
|
||||
NAMED_TYPES,
|
||||
};
|
||||
use iced::Task;
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(super) fn dispatch_field(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
||||
match cmd {
|
||||
"FIELD" => {
|
||||
self.open_field_dialog(FieldTarget::NewText);
|
||||
Some(Task::none())
|
||||
}
|
||||
cmd if cmd == "_FIELD_OBJECT" || cmd.starts_with("_FIELD_OBJECT ") => {
|
||||
let handle = cmd
|
||||
.split_whitespace()
|
||||
.nth(1)
|
||||
.and_then(|h| u64::from_str_radix(h, 16).ok())
|
||||
.map(codec::Handle::new);
|
||||
if let Some(state) = self.field_dialog.as_mut() {
|
||||
if let Some(entity) = handle.and_then(|h| self.tabs[i].scene.document.get_entity(h)) {
|
||||
let props = object_properties(entity);
|
||||
state.object = Some((entity.common().handle, crate::t!(crate::entities::names::ui_name(entity)).into_owned()));
|
||||
state.object_prop = props.first().copied();
|
||||
state.object_props = props;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::Field);
|
||||
self.refresh_field_preview();
|
||||
}
|
||||
Some(Task::none())
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Open the Field dialog for `target`.
|
||||
pub(in crate::app) fn open_field_dialog(&mut self, target: FieldTarget) {
|
||||
let mut state = FieldDialogState::new(target);
|
||||
state.examples = DATE_FORMATS
|
||||
.iter()
|
||||
.map(|f| self.field_value(&format!("\\AcVar Date \\f \"{f}\""), &[]))
|
||||
.collect();
|
||||
self.field_dialog = Some(state);
|
||||
self.fill_named_objects();
|
||||
self.refresh_field_preview();
|
||||
self.active_modal = Some(crate::app::ModalKind::Field);
|
||||
}
|
||||
|
||||
fn field_value(&self, code: &str, objects: &[codec::Handle]) -> String {
|
||||
crate::entities::field::evaluate(&self.tabs[self.active_tab].scene.document, code, objects, None)
|
||||
.unwrap_or_else(|| "----".to_string())
|
||||
}
|
||||
|
||||
fn refresh_field_preview(&mut self) {
|
||||
let Some(state) = self.field_dialog.as_ref() else {
|
||||
return;
|
||||
};
|
||||
let (code, objects) = state.code();
|
||||
let preview = if state.complete() { self.field_value(&code, &objects) } else { String::new() };
|
||||
if let Some(state) = self.field_dialog.as_mut() {
|
||||
state.preview = preview;
|
||||
}
|
||||
}
|
||||
|
||||
/// The names of the chosen named-object type, sorted.
|
||||
fn fill_named_objects(&mut self) {
|
||||
let Some(state) = self.field_dialog.as_mut() else {
|
||||
return;
|
||||
};
|
||||
let document = &self.tabs[self.active_tab].scene.document;
|
||||
let mut names: Vec<(String, codec::Handle)> = match NAMED_TYPES[state.named_type] {
|
||||
"Block" => document
|
||||
.block_records
|
||||
.iter()
|
||||
.filter(|b| !b.name.starts_with('*'))
|
||||
.map(|b| (b.name.clone(), b.handle))
|
||||
.collect(),
|
||||
"Dimstyle" => document.dim_styles.iter().map(|s| (s.name.clone(), s.handle)).collect(),
|
||||
"Layer" => document.layers.iter().map(|l| (l.name.clone(), l.handle)).collect(),
|
||||
"Linetype" => document.line_types.iter().map(|l| (l.name.clone(), l.handle)).collect(),
|
||||
"Textstyle" => document
|
||||
.text_styles
|
||||
.iter()
|
||||
.filter(|s| !s.name.is_empty())
|
||||
.map(|s| (s.name.clone(), s.handle))
|
||||
.collect(),
|
||||
"View" => document.views.iter().map(|v| (v.name.clone(), v.handle)).collect(),
|
||||
_ => Vec::new(),
|
||||
};
|
||||
names.sort_by_key(|(n, _)| n.to_lowercase());
|
||||
state.named_names = names;
|
||||
state.named = None;
|
||||
}
|
||||
|
||||
pub(in crate::app) fn on_field_dialog(&mut self, m: FieldDialogMsg) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let Some(state) = self.field_dialog.as_mut() else {
|
||||
return Task::none();
|
||||
};
|
||||
match m {
|
||||
FieldDialogMsg::Category(c) => {
|
||||
state.category = c;
|
||||
let names = fields_of(c);
|
||||
if !names.contains(&state.name) {
|
||||
state.name = names.first().copied().unwrap_or("Date");
|
||||
}
|
||||
}
|
||||
FieldDialogMsg::Name(n) => {
|
||||
state.name = n;
|
||||
state.text_case = 0;
|
||||
}
|
||||
FieldDialogMsg::DateFormat(f) => state.date_format = f,
|
||||
FieldDialogMsg::DateExample(k) => {
|
||||
if let Some(f) = DATE_FORMATS.get(k) {
|
||||
state.date_format = f.to_string();
|
||||
}
|
||||
}
|
||||
FieldDialogMsg::TextCase(k) => state.text_case = k,
|
||||
FieldDialogMsg::FileParts(b) => state.file_parts = b,
|
||||
FieldDialogMsg::FileExtension(v) => state.file_extension = v,
|
||||
FieldDialogMsg::SizeUnit(k) => state.size_unit = k,
|
||||
FieldDialogMsg::SysVar(v) => state.sysvar = v,
|
||||
FieldDialogMsg::Diesel(v) => state.diesel = v,
|
||||
FieldDialogMsg::NamedType(k) => {
|
||||
state.named_type = k;
|
||||
self.fill_named_objects();
|
||||
}
|
||||
FieldDialogMsg::Named(k) => state.named = Some(k),
|
||||
FieldDialogMsg::ObjectProp(p) => state.object_prop = Some(p),
|
||||
FieldDialogMsg::Formula(v) => state.formula = v,
|
||||
FieldDialogMsg::FormulaFormat(k) => state.formula_format = k,
|
||||
FieldDialogMsg::FormulaPrecision(k) => state.formula_precision = k,
|
||||
FieldDialogMsg::Evaluate => {}
|
||||
FieldDialogMsg::HyperlinkText(v) => state.hyperlink_text = v,
|
||||
FieldDialogMsg::HyperlinkUrl(v) => state.hyperlink_url = v,
|
||||
FieldDialogMsg::BrowseHyperlink => {
|
||||
return Task::perform(
|
||||
async {
|
||||
rfd::AsyncFileDialog::new()
|
||||
.set_title(crate::t!("Select a file to link").as_ref())
|
||||
.pick_file()
|
||||
.await
|
||||
.map(|h| crate::sys::handle_path(&h))
|
||||
},
|
||||
|path| match path {
|
||||
Some(path) => Message::FieldDialog(FieldDialogMsg::HyperlinkUrl(path.to_string_lossy().into_owned())),
|
||||
None => Message::Noop,
|
||||
},
|
||||
);
|
||||
}
|
||||
FieldDialogMsg::PlotScale(k) => state.plot_scale = k,
|
||||
FieldDialogMsg::SelectObject => {
|
||||
// The dialog waits while one object is picked.
|
||||
self.active_modal = None;
|
||||
let command = FieldObjectPickCommand;
|
||||
self.command_line.push_info(&command.prompt());
|
||||
self.tabs[i].active_cmd = Some(Box::new(command));
|
||||
return Task::none();
|
||||
}
|
||||
FieldDialogMsg::Help => self.command_line.push_info(
|
||||
crate::t!("Inserts a field: text that updates from the drawing, the date or another source.").as_ref(),
|
||||
),
|
||||
FieldDialogMsg::Ok => return self.field_dialog_ok(i),
|
||||
}
|
||||
self.refresh_field_preview();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
fn field_dialog_ok(&mut self, i: usize) -> Task<Message> {
|
||||
let Some(state) = self.field_dialog.take() else {
|
||||
return Task::none();
|
||||
};
|
||||
let (code, objects) = state.code();
|
||||
let value = state.preview.clone();
|
||||
let field = Some((code, objects));
|
||||
match state.target {
|
||||
FieldTarget::NewText => {
|
||||
self.close_active_modal();
|
||||
let defaults =
|
||||
crate::scene::creation_style::current_text_defaults(&self.tabs[i].scene.document);
|
||||
let command = FieldPlaceCommand::new(
|
||||
value,
|
||||
field.expect("set above"),
|
||||
defaults.style_name,
|
||||
defaults.height,
|
||||
);
|
||||
self.reset_command_start_state(i);
|
||||
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();
|
||||
}
|
||||
FieldTarget::AttdefDefault => {
|
||||
if let Some(dialog) = self.attdef_dialog.as_mut() {
|
||||
dialog.default = value;
|
||||
dialog.field = field;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDef);
|
||||
}
|
||||
FieldTarget::AttdefEdit => {
|
||||
if let Some(edit) = self.attdef_edit.as_mut() {
|
||||
edit.default = value;
|
||||
edit.field = field;
|
||||
edit.field_changed = true;
|
||||
}
|
||||
self.active_modal = Some(crate::app::ModalKind::AttDefEdit);
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// The dialogs a Field dialog was opened from come back when it closes.
|
||||
pub(in crate::app) fn close_field_dialog(&mut self) {
|
||||
let target = self.field_dialog.take().map(|s| s.target);
|
||||
self.close_active_modal();
|
||||
self.active_modal = match target {
|
||||
Some(FieldTarget::AttdefDefault) if self.attdef_dialog.is_some() => Some(crate::app::ModalKind::AttDef),
|
||||
Some(FieldTarget::AttdefEdit) if self.attdef_edit.is_some() => Some(crate::app::ModalKind::AttDefEdit),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ use iced::Task;
|
||||
use std::path::PathBuf;
|
||||
|
||||
mod attdef;
|
||||
mod field;
|
||||
mod blocks;
|
||||
mod xref_attach;
|
||||
mod dim;
|
||||
@@ -292,6 +293,9 @@ impl OpenCADStudio {
|
||||
if let Some(t) = self.dispatch_attdef(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_field(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
if let Some(t) = self.dispatch_draw(cmd, i) {
|
||||
return Some(t);
|
||||
}
|
||||
@@ -664,6 +668,7 @@ inventory::submit!(crate::command::CommandRegistration {
|
||||
"DGNFRAME",
|
||||
"PDFOSNAP",
|
||||
"UOSNAP",
|
||||
"FIELDDISPLAY",
|
||||
"PDFIMPORTMODE",
|
||||
"PDFIMPORTFILTER",
|
||||
"PDFIMPORTLAYERS",
|
||||
|
||||
@@ -1025,6 +1025,7 @@ impl OpenCADStudio {
|
||||
| "DWFOSNAP"
|
||||
| "DGNOSNAP"
|
||||
| "UOSNAP"
|
||||
| "FIELDDISPLAY"
|
||||
| "PDFIMPORTMODE"
|
||||
| "PDFIMPORTFILTER"
|
||||
| "PDFIMPORTLAYERS"
|
||||
@@ -1232,6 +1233,40 @@ impl OpenCADStudio {
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if name == "FIELDDISPLAY" {
|
||||
let current = i16::from(crate::entities::field::display());
|
||||
match value.as_deref().map(|v| v.trim().parse::<i16>().ok().filter(|v| (0..=1).contains(v))) {
|
||||
Some(Some(mode)) => {
|
||||
if current != mode {
|
||||
crate::entities::field::set_display(mode == 1);
|
||||
// A profile setting: redraw the fields of every open drawing.
|
||||
for tab in &mut self.tabs {
|
||||
let changes: Vec<_> = tab
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter(|e| crate::entities::field::hosts_field(&tab.scene.document, e))
|
||||
.map(|e| (e.common().handle, crate::scene::ChangeKind::Modified))
|
||||
.collect();
|
||||
if !changes.is_empty() {
|
||||
tab.scene.bump_entities(&changes);
|
||||
}
|
||||
}
|
||||
self.save_config();
|
||||
}
|
||||
}
|
||||
Some(None) => {
|
||||
self.command_line.push_error(crate::t!("Requires 0 or 1 only.").as_ref());
|
||||
self.command_line.push_output(&format!("Enter new value for FIELDDISPLAY <{current}>:"));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
None => {
|
||||
self.command_line.push_output(&format!("Enter new value for FIELDDISPLAY <{current}>:"));
|
||||
self.pending_setvar = Some(name.clone());
|
||||
}
|
||||
}
|
||||
return Some(self.finish_dispatch(cmd));
|
||||
}
|
||||
if matches!(name.as_str(), "PDFOSNAP" | "DWFOSNAP" | "DGNOSNAP" | "UOSNAP") {
|
||||
use crate::scene::model::pdf_vector as pv;
|
||||
use codec::entities::UnderlayType;
|
||||
|
||||
@@ -81,6 +81,7 @@ pub(super) const NAMES: &[&str] = &[
|
||||
"ribbon_dropdown",
|
||||
"dialog_ok",
|
||||
"attdef_dialog",
|
||||
"field_dialog",
|
||||
"close_document",
|
||||
"toggle_properties",
|
||||
"toggle_layers",
|
||||
@@ -958,6 +959,8 @@ impl OpenCADStudio {
|
||||
"align_below" => M::AlignBelow(on),
|
||||
"edit_value" => M::EditValue,
|
||||
"dismiss" => M::DismissError,
|
||||
"insert_field" if editing => M::EditInsertField,
|
||||
"insert_field" => M::InsertField,
|
||||
"x" => M::Coord(0, v.into()),
|
||||
"y" => M::Coord(1, v.into()),
|
||||
"z" => M::Coord(2, v.into()),
|
||||
@@ -975,6 +978,42 @@ impl OpenCADStudio {
|
||||
_ => return Err(failure("invalid_value", "Unknown attdef dialog field")),
|
||||
})
|
||||
}
|
||||
// One field of the open Field dialog: `category=<index>`, `name=Date`,
|
||||
// `date=yyyy-MM-dd`, `case=0..4`, `file=1|2|3`, `ext=0|1`, `size=0..2`,
|
||||
// `sysvar=dimscale`, `diesel=…`, `named_type=<index>`, `named=<index>`,
|
||||
// `prop=Area`, `select_object`, `formula=(1+2)*3`, `formula_format=<index>`,
|
||||
// `precision=<index>`, `link_text=…`, `link_url=…`, `plot_scale=<index>`.
|
||||
"field_dialog" => {
|
||||
use crate::ui::window::field_dialog::{self as fd, FieldDialogMsg as F};
|
||||
let value = string(req, "value")?;
|
||||
let (key, v) = value.split_once('=').unwrap_or((value, ""));
|
||||
let index = || v.parse::<usize>().map_err(|_| failure("invalid_value", "index expected"));
|
||||
let known = |list: &[&'static str]| {
|
||||
list.iter().copied().find(|n| n.eq_ignore_ascii_case(v)).ok_or_else(|| failure("invalid_value", "unknown name"))
|
||||
};
|
||||
Message::FieldDialog(match key {
|
||||
"category" => F::Category(index()?),
|
||||
"name" => F::Name(known(&fd::fields_of(0))?),
|
||||
"date" => F::DateFormat(v.into()),
|
||||
"case" => F::TextCase(index()?),
|
||||
"file" => F::FileParts(v.parse::<u8>().map_err(|_| failure("invalid_value", "1|2|3"))?),
|
||||
"ext" => F::FileExtension(v == "1"),
|
||||
"size" => F::SizeUnit(index()?),
|
||||
"sysvar" => F::SysVar(v.into()),
|
||||
"diesel" => F::Diesel(v.into()),
|
||||
"named_type" => F::NamedType(index()?),
|
||||
"named" => F::Named(index()?),
|
||||
"prop" => F::ObjectProp(known(&["Area", "Center", "Circumference", "Diameter", "EndPoint", "Length", "Radius", "StartPoint"])?),
|
||||
"select_object" => F::SelectObject,
|
||||
"formula" => F::Formula(v.into()),
|
||||
"formula_format" => F::FormulaFormat(index()?),
|
||||
"precision" => F::FormulaPrecision(index()?),
|
||||
"link_text" => F::HyperlinkText(v.into()),
|
||||
"link_url" => F::HyperlinkUrl(v.into()),
|
||||
"plot_scale" => F::PlotScale(index()?),
|
||||
_ => return Err(failure("invalid_value", "Unknown field dialog key")),
|
||||
})
|
||||
}
|
||||
"ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()),
|
||||
"pdf_dialog_ok" => {
|
||||
use crate::ui::window::pdf_dialogs::PdfDialogMsg;
|
||||
@@ -1001,6 +1040,9 @@ impl OpenCADStudio {
|
||||
Some(crate::app::ModalKind::AttDefEdit) => Message::AttdefDialog(
|
||||
crate::ui::window::attdef_dialog::AttdefDialogMsg::EditOk,
|
||||
),
|
||||
Some(crate::app::ModalKind::Field) => Message::FieldDialog(
|
||||
crate::ui::window::field_dialog::FieldDialogMsg::Ok,
|
||||
),
|
||||
_ => return Err(failure("no_dialog", "No dialog with an OK button is open")),
|
||||
},
|
||||
"close_document" => Message::TabClose(self.tabs[self.active_tab].id),
|
||||
|
||||
@@ -501,6 +501,8 @@ pub(super) struct OpenCADStudio {
|
||||
/// 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>,
|
||||
/// Field dialog working copy; `None` while closed.
|
||||
field_dialog: Option<crate::ui::window::field_dialog::FieldDialogState>,
|
||||
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>,
|
||||
@@ -1938,6 +1940,7 @@ pub enum ModalKind {
|
||||
GeometricTolerance,
|
||||
AttDef,
|
||||
AttDefEdit,
|
||||
Field,
|
||||
DraftingSettings,
|
||||
AutoConstrainSettings,
|
||||
LayerStateEditor,
|
||||
@@ -3891,6 +3894,7 @@ pub enum Message {
|
||||
/// An edit in one of the PDF dialogs.
|
||||
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
|
||||
AttdefDialog(crate::ui::window::attdef_dialog::AttdefDialogMsg),
|
||||
FieldDialog(crate::ui::window::field_dialog::FieldDialogMsg),
|
||||
/// 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>),
|
||||
@@ -4086,6 +4090,7 @@ impl OpenCADStudio {
|
||||
point_cloud_attach: None,
|
||||
attdef_dialog: None,
|
||||
attdef_edit: None,
|
||||
field_dialog: None,
|
||||
point_cloud_color_map: None,
|
||||
pc_section: None,
|
||||
underlay_layers: None,
|
||||
|
||||
@@ -406,6 +406,10 @@ pub struct UserSettings {
|
||||
/// IMAGEFRAME of the profile: used by new drawings and by drawings that
|
||||
/// carry no image variables of their own.
|
||||
pub image_frame: i16,
|
||||
/// The Attribute Definition dialog's "Specify on-screen" choice.
|
||||
pub attdef_on_screen: bool,
|
||||
/// FIELDDISPLAY: fields show on a gray background.
|
||||
pub field_display: bool,
|
||||
/// Nested-copy symbol handling: false inserts, true binds.
|
||||
pub ncopy_bind: bool,
|
||||
/// Last Options page; unknown saved names fall back without rejecting the config.
|
||||
@@ -784,6 +788,8 @@ impl Default for UserSettings {
|
||||
right_click_hold_ms: 250,
|
||||
grip_object_limit: DEFAULT_GRIP_OBJECT_LIMIT,
|
||||
image_frame: 1,
|
||||
attdef_on_screen: true,
|
||||
field_display: true,
|
||||
ncopy_bind: false,
|
||||
cursor_type: CursorType::Crosshair,
|
||||
crosshair_color: None,
|
||||
|
||||
@@ -676,6 +676,8 @@ impl OpenCADStudio {
|
||||
right_click_hold_ms: self.right_click_hold_ms,
|
||||
grip_object_limit: self.grip_object_limit,
|
||||
image_frame: crate::scene::frame::profile_image_mode(),
|
||||
attdef_on_screen: crate::modules::draw::draw::attdef::session().on_screen,
|
||||
field_display: crate::entities::field::display(),
|
||||
ncopy_bind: self.ncopy_bind,
|
||||
cursor_type: self.cursor_type,
|
||||
crosshair_color: self.crosshair_color,
|
||||
@@ -774,6 +776,8 @@ impl OpenCADStudio {
|
||||
self.right_click_hold_ms = super::super::settings::clamp_right_click_hold_ms(s.right_click_hold_ms);
|
||||
self.grip_object_limit = s.grip_object_limit.clamp(0, 32767);
|
||||
crate::scene::frame::set_profile_image_mode(s.image_frame);
|
||||
crate::modules::draw::draw::attdef::session().on_screen = s.attdef_on_screen;
|
||||
crate::entities::field::set_display(s.field_display);
|
||||
self.ncopy_bind = s.ncopy_bind;
|
||||
self.cursor_type = s.cursor_type;
|
||||
self.crosshair_color = s.crosshair_color;
|
||||
|
||||
@@ -1817,6 +1817,7 @@ impl OpenCADStudio {
|
||||
|
||||
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
||||
Message::AttdefDialog(message) => self.on_attdef_dialog(message),
|
||||
Message::FieldDialog(message) => self.on_field_dialog(message),
|
||||
Message::PcManager(message) => self.update_pc_manager(message),
|
||||
Message::RibbonSelectTab(idx) => {
|
||||
self.ribbon.select(idx);
|
||||
@@ -8520,6 +8521,10 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
Message::CloseModal => {
|
||||
if self.active_modal == Some(super::ModalKind::Field) {
|
||||
self.close_field_dialog();
|
||||
return Task::none();
|
||||
}
|
||||
if matches!(
|
||||
self.active_modal,
|
||||
Some(super::ModalKind::AttDef | super::ModalKind::AttDefEdit)
|
||||
|
||||
@@ -40,6 +40,7 @@ impl OpenCADStudio {
|
||||
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::Field) => crate::t!("Field").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"),
|
||||
@@ -579,6 +580,10 @@ impl OpenCADStudio {
|
||||
let state = self.attdef_dialog.as_ref()?;
|
||||
sized_flow(ex, 720, 520, |flow| crate::ui::window::attdef_dialog::view(state, flow))
|
||||
}
|
||||
super::super::ModalKind::Field => {
|
||||
let state = self.field_dialog.as_ref()?;
|
||||
sized_flow(ex, 760, 660, |flow| crate::ui::window::field_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))
|
||||
|
||||
Reference in new issue
Block a user