Merge pull request #1793 from ramox81/codex/block-definition-command-line

Block Definition: command-line forms, block record data and dialog defaults

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-15 00:14:21 +03:00
commit 838e397a6c
23 files changed
+1860 -435

No files matched your search

+1 -8
View File
@@ -629,14 +629,7 @@ impl OpenCADStudio {
self.block_palette.source_tab_id = Some(self.tabs[i].id);
self.block_palette.source_block_epoch = self.tabs[i].scene.block_epoch;
let doc = &self.tabs[i].scene.document;
let annotative = |name: &str| {
doc.block_records.get(name).is_some_and(|br| {
matches!(
doc.get_entity(br.block_entity_handle),
Some(codec::EntityType::Block(b)) if b.common.extended_data.records().iter().any(|r| r.application_name == "AcadAnnotative")
)
})
};
let annotative = |name: &str| crate::scene::annotative::block_is_annotative(doc, name);
let entries: Vec<BlockEntry> = names
.into_iter()
.map(|name| BlockEntry {
+116
View File
@@ -768,6 +768,8 @@ impl OpenCADStudio {
/// Snapshot the persisted UI preferences from live state.
pub(in crate::app) fn current_settings(&self) -> crate::app::settings::UserSettings {
// One copy: the guard must not be held across the literal below.
let block_defaults = *crate::modules::insert::block_cmdline::defaults();
crate::app::settings::UserSettings {
spacemouse: self.spacemouse_preferences,
dyn_input: self.dyn_input,
@@ -795,6 +797,14 @@ impl OpenCADStudio {
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,
block_base_on_screen: block_defaults.base_on_screen,
block_objects_on_screen: block_defaults.objects_on_screen,
block_object_mode: crate::modules::insert::block_cmdline::mode_code(block_defaults.mode),
block_allow_exploding: block_defaults.allow_exploding,
block_open_in_editor: block_defaults.open_in_editor,
wblock_object_mode: crate::modules::insert::block_cmdline::mode_code(
block_defaults.wblock_mode,
),
field_display: crate::entities::field::display(),
xref_fade: crate::scene::cache::block_cache::xref_fade_ctl(),
file_dia: crate::sys::file_dia(),
@@ -920,6 +930,16 @@ impl OpenCADStudio {
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;
{
use crate::modules::insert::block_cmdline as bc;
let mut remembered = bc::defaults();
remembered.base_on_screen = s.block_base_on_screen;
remembered.objects_on_screen = s.block_objects_on_screen;
remembered.mode = bc::mode_from_code(s.block_object_mode);
remembered.allow_exploding = s.block_allow_exploding;
remembered.open_in_editor = s.block_open_in_editor;
remembered.wblock_mode = bc::mode_from_code(s.wblock_object_mode);
}
crate::entities::field::set_display(s.field_display);
crate::scene::cache::block_cache::set_xref_fade_ctl(s.xref_fade);
self.ribbon.xref_fade = crate::scene::cache::block_cache::xref_fade_ctl();
@@ -2336,6 +2356,102 @@ impl OpenCADStudio {
])
}
/// Write `source` to `path` (WBLOCK and -WBLOCK): an existing block, the
/// whole drawing, or objects, which then are kept, converted to a block
/// named after the file, or deleted.
pub(in crate::app) fn wblock_write(
&mut self,
i: usize,
source: crate::modules::insert::block_cmdline::WblockSource,
path: std::path::PathBuf,
unit: i16,
) -> Task<Message> {
let mut path = path;
if path.extension().is_none() {
path.set_extension("dwg");
}
let document = self.tabs[i].scene.document_for_save();
if let crate::modules::insert::block_cmdline::WblockSource::Objects {
handles,
base,
mode,
} = &source
{
match mode {
crate::ui::window::block_definition::BlockObjectMode::Retain => {}
crate::ui::window::block_definition::BlockObjectMode::Delete => {
self.push_undo_snapshot(i, "WBLOCK");
self.tabs[i].scene.erase_entities(handles);
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
self.refresh_properties();
}
crate::ui::window::block_definition::BlockObjectMode::Convert => {
let name = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("WBLOCK")
.to_string();
self.push_undo_snapshot(i, "WBLOCK");
let ucs = self.tabs[i].ucs_xform();
let options = crate::scene::CreateBlockOptions {
name,
handles: handles.clone(),
base_point: *base,
world_to_block: ucs.to_ucs_transform_at(*base),
block_to_world: ucs.to_wcs_transform_at(*base),
mode: crate::ui::window::block_definition::BlockObjectMode::Convert,
annotative: false,
match_orientation: false,
scale_uniformly: false,
allow_exploding: true,
unit,
description: String::new(),
hyperlink_url: String::new(),
hyperlink_desc: String::new(),
redefine: true,
};
let _ = self.tabs[i].scene.create_block_with_options(options);
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
self.refresh_properties();
}
}
}
let display = match &source {
crate::modules::insert::block_cmdline::WblockSource::Block(name) => name.clone(),
_ => "*".to_string(),
};
let worker_path = path.clone();
background_task(
crate::t!("WBLOCK save"),
move || {
let out_doc = match source {
crate::modules::insert::block_cmdline::WblockSource::Block(name) => {
let mut d =
crate::modules::insert::wblock::extract_block_to_doc(&document, &name)
.map_err(|e| e.to_string())?;
d.header.insertion_units = unit;
d
}
crate::modules::insert::block_cmdline::WblockSource::Drawing => {
let mut d = document.clone();
d.header.insertion_units = unit;
d
}
crate::modules::insert::block_cmdline::WblockSource::Objects { handles, base, .. } => {
crate::modules::insert::wblock::extract_entities_to_doc_with_base(
&document, &handles, base, unit,
)
.map_err(|e| e.to_string())?
}
};
crate::io::save(&out_doc, &worker_path).map_err(|e| e.to_string())
},
move |result| Message::WblockWriteFinished(display, path, result),
)
}
pub(super) fn on_wblock_save_result_some(
&mut self,
block_name: String,
+37 -100
View File
@@ -1837,10 +1837,15 @@ impl OpenCADStudio {
Message::WblockSave(block_name) => {
let name = block_name.clone();
let title = if block_name == "-" {
crate::t!("Create Drawing File").into_owned()
} else {
crate::t!("Save Block As").into_owned()
};
Task::perform(
async move {
let path = crate::sys::file_dialog()
.set_title(crate::t!("Save Block As").as_ref())
.set_title(&title)
.set_file_name("block.dwg")
.add_filter(crate::t!("DWG Files").as_ref(), &["dwg"])
.save_file()
@@ -1852,6 +1857,10 @@ impl OpenCADStudio {
)
}
// -WBLOCK with FILEDIA 1: the file came from the dialog, the
// command line asks for the rest.
Message::WblockSaveResult(block_name, Some(path)) if block_name == "-" => self
.dispatch_command(&format!("_-WBLOCKFILE {}", path.display())),
Message::WblockSaveResult(block_name, Some(path)) => {
self.on_wblock_save_result_some(block_name, path)
}
@@ -1859,14 +1868,12 @@ impl OpenCADStudio {
Message::WblockSaveResult(_, None) => Task::none(),
Message::WblockWriteFinished(block_name, path, result) => {
match result {
Ok(()) => self.command_line.push_output(
crate::tf!("WBLOCK Saved \"{block_name}\" → \"{}\"", path.display())
.as_ref(),
),
Err(error) => self
.command_line
.push_error(crate::tf!("WBLOCK save failed: {error}").as_ref()),
// A written file is not reported (as in the reference); a
// failed write is.
let _ = (&block_name, &path);
if let Err(error) = result {
self.command_line
.push_error(crate::tf!("WBLOCK save failed: {error}").as_ref());
}
Task::none()
}
@@ -5820,13 +5827,14 @@ impl OpenCADStudio {
};
let name = state.name.trim().to_string();
if name.is_empty() {
state.error_message =
Some(crate::t!("Block name cannot be empty.").into_owned());
state.error_message = Some(
crate::t!("The name must be at least one character long.").into_owned(),
);
return Task::none();
}
if let Some(_ch) = state.invalid_name_char() {
state.error_message = Some(
crate::t!("Block name cannot contain: \\ / : * ? \" < > | = `")
crate::t!("The name contains unsupported characters. The following characters are not supported: \\ < > / ? \" : ; * | , = `")
.into_owned(),
);
return Task::none();
@@ -6064,96 +6072,25 @@ impl OpenCADStudio {
let state = self.wblock.take().unwrap();
self.active_modal = None;
let document = self.tabs[i].scene.document_for_save();
let source_mode = state.source_mode;
let block_name = state.block_name.trim().to_string();
let handles = state.selected_handles.clone();
let base_point = state.parse_base_point();
let unit = state.unit;
let object_mode = state.object_mode;
if source_mode == WblockSourceMode::Objects {
match object_mode {
WblockObjectMode::Retain => {}
WblockObjectMode::Delete => {
self.push_undo_snapshot(i, "WBLOCK");
self.tabs[i].scene.erase_entities(&handles);
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
self.refresh_properties();
}
WblockObjectMode::Convert => {
let block_name_for_conv = path
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("WBLOCK")
.to_string();
self.push_undo_snapshot(i, "WBLOCK");
let ucs = self.tabs[i].ucs_xform();
let world_to_block = ucs.to_ucs_transform_at(base_point);
let block_to_world = ucs.to_wcs_transform_at(base_point);
let options = crate::scene::CreateBlockOptions {
name: block_name_for_conv,
handles: handles.clone(),
base_point,
world_to_block,
block_to_world,
mode: crate::ui::window::block_definition::BlockObjectMode::Convert,
annotative: false,
match_orientation: false,
scale_uniformly: false,
allow_exploding: true,
unit,
description: String::new(),
hyperlink_url: String::new(),
hyperlink_desc: String::new(),
redefine: true,
};
let _ = self.tabs[i].scene.create_block_with_options(options);
self.tabs[i].dirty = true;
self.tabs[i].scene.bump_geometry();
self.refresh_properties();
}
}
}
let worker_path = path.clone();
let display_name = match source_mode {
WblockSourceMode::Block => block_name.clone(),
WblockSourceMode::EntireDrawing => "*".to_string(),
WblockSourceMode::Objects => "*".to_string(),
use crate::modules::insert::block_cmdline::WblockSource;
use crate::ui::window::block_definition::BlockObjectMode;
let mode = match state.object_mode {
WblockObjectMode::Retain => BlockObjectMode::Retain,
WblockObjectMode::Convert => BlockObjectMode::Convert,
WblockObjectMode::Delete => BlockObjectMode::Delete,
};
let worker_display = display_name.clone();
file::background_task(
crate::t!("WBLOCK save"),
move || {
let out_doc = match source_mode {
WblockSourceMode::Block => {
let mut d = crate::modules::insert::wblock::extract_block_to_doc(
&document,
&block_name,
)
.map_err(|e| e.to_string())?;
d.header.insertion_units = unit;
d
}
WblockSourceMode::EntireDrawing => {
let mut d = document.clone();
d.header.insertion_units = unit;
d
}
WblockSourceMode::Objects => {
crate::modules::insert::wblock::extract_entities_to_doc_with_base(
&document, &handles, base_point, unit,
)
.map_err(|e| e.to_string())?
}
};
crate::io::save(&out_doc, &worker_path).map_err(|e| e.to_string())
crate::modules::insert::block_cmdline::defaults().wblock_mode = mode;
self.persist_settings_if_changed();
let source = match state.source_mode {
WblockSourceMode::Block => WblockSource::Block(state.block_name.trim().to_string()),
WblockSourceMode::EntireDrawing => WblockSource::Drawing,
WblockSourceMode::Objects => WblockSource::Objects {
handles: state.selected_handles.clone(),
base: state.parse_base_point(),
mode,
},
move |result| Message::WblockWriteFinished(worker_display, path, result),
)
};
self.wblock_write(i, source, path, state.unit)
}
Message::ToleranceDialogField(field) => {
if let Some(state) = self.geometric_tolerance.as_mut() {