Block Definition dialog: preview, empty-block band, open in editor

The name card draws the selected objects, or the existing definition the
name picks. OK with no objects shows an in-window band offering to select
objects or to define an empty block. A switch opens the new block in the
block editor once it is defined; it is remembered with the other dialog
choices. Automation gains block_def_dialog.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-15 09:50:35 +03:00
1 parent 39c3e9af58
commit d69a5f0ac4
30 files changed
+329 -13

No files matched your search

+8
View File
@@ -810,6 +810,14 @@ impl OpenCADStudio {
self.refresh_properties();
self.note_recent_block(&name);
self.refresh_block_palette();
// The Block Definition dialog asked to edit the new block.
if self
.block_definition_open_editor
.take()
.is_some_and(|wanted| wanted.eq_ignore_ascii_case(&name))
{
return Some(self.dispatch_command(&format!("BEDIT_OPEN:{name}")));
}
}
Err(err) => {
self.discard_last_undo_entry(i);
+32
View File
@@ -61,7 +61,11 @@ impl OpenCADStudio {
remembered.objects_on_screen = state.objects_specify_onscreen;
remembered.mode = state.object_mode;
remembered.allow_exploding = state.allow_exploding;
remembered.open_in_editor = state.open_in_editor;
}
self.block_definition_open_editor = state
.open_in_editor
.then(|| state.name.trim().to_string());
self.persist_settings_if_changed();
let i = self.active_tab;
let base_point = state.parse_base_point();
@@ -122,6 +126,32 @@ impl OpenCADStudio {
)
}
/// Draw the selected objects, or else the existing definition the name
/// picks, in the Block Definition dialog's name card.
pub(in crate::app) fn refresh_block_definition_preview(&mut self) {
let scene = &self.tabs[self.active_tab].scene;
let Some(state) = self.block_definition.as_mut() else {
return;
};
state.preview = if state.selected_handles.is_empty() {
let name = state.name.trim();
state
.existing_names
.iter()
.find(|existing| existing.eq_ignore_ascii_case(name))
.map(|existing| scene.block_preview_wires(existing))
.unwrap_or_default()
} else {
state.empty_prompt = false;
let entities: Vec<_> = state
.selected_handles
.iter()
.filter_map(|handle| scene.document.get_entity(*handle).cloned())
.collect();
scene.wires_for_entities(&entities)
};
}
pub(super) fn dispatch_blocks(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
match cmd {
// ── BASE — drawing insertion base point ───────────────────────
@@ -469,6 +499,7 @@ impl OpenCADStudio {
),
);
self.active_modal = Some(crate::app::ModalKind::BlockDefinition);
self.refresh_block_definition_preview();
}
cmd if cmd.starts_with("BLOCK_POINT_PICKED ") => {
@@ -504,6 +535,7 @@ impl OpenCADStudio {
}
self.tabs[i].active_cmd = None;
self.active_modal = Some(crate::app::ModalKind::BlockDefinition);
self.refresh_block_definition_preview();
}
cmd if cmd.starts_with("WBLOCK_POINT_PICKED ") => {
+21
View File
@@ -118,6 +118,7 @@ pub(super) const NAMES: &[&str] = &[
"ribbon_dropdown",
"keytips",
"dialog_ok",
"block_def_dialog",
"attdef_dialog",
"field_dialog",
"sheet_set",
@@ -1502,6 +1503,26 @@ impl OpenCADStudio {
}
return Ok(Task::none());
}
// The open Block Definition dialog: `name=…`, `open_in_editor=0|1`,
// `base_on_screen=0|1`, `objects_on_screen=0|1`, `create_empty`,
// `select_objects`.
"block_def_dialog" => {
if self.block_definition.is_none() {
return Err(failure("no_dialog", "The Block Definition dialog is not open"));
}
let value = string(req, "value")?;
let (key, v) = value.split_once('=').unwrap_or((value, ""));
let on = v == "1";
match key {
"name" => Message::BlockDefNameSelect(v.to_string()),
"open_in_editor" => Message::BlockDefOpenInEditor(on),
"base_on_screen" => Message::BlockDefBaseOnScreen(on),
"objects_on_screen" => Message::BlockDefObjectsOnScreen(on),
"create_empty" => Message::BlockDefCreateEmpty,
"select_objects" => Message::BlockDefSelectObjects,
_ => return Err(failure("invalid_value", "Unknown block definition field")),
}
}
// One field of the open attribute definition dialog: `tag=…`,
// `prompt=…`, `default=…`, `justify=MC`, `style=…`, `height=…`,
// `rotation=…`, `width=…`, `on_screen=0|1`, `x=…` / `y=…` / `z=…`,
+8
View File
@@ -577,6 +577,9 @@ pub(super) struct OpenCADStudio {
drawing_units: Option<crate::ui::window::drawing_units::State>,
/// Working copy of the Block Definition dialog; `None` while it is closed.
block_definition: Option<crate::ui::window::block_definition::BlockDefinitionState>,
/// The block the Block Definition dialog defines with "Open in block
/// editor" on; it opens once the definition is committed.
block_definition_open_editor: Option<String>,
block_edit_picker: Option<crate::ui::window::block_edit_picker::State>,
block_save_as: Option<crate::ui::window::block_save_as::State>,
/// PDF dialogs' working copies; `None` while closed.
@@ -3441,6 +3444,10 @@ pub enum Message {
BlockDefConfirmRedefine(bool),
BlockDefDismissError,
BlockDefHelp,
/// Block Definition: open the new block in the block editor.
BlockDefOpenInEditor(bool),
/// Block Definition: define the block with no objects.
BlockDefCreateEmpty,
/// Write Block (WBLOCK) dialog messages
WblockSourceMode(crate::ui::window::wblock::WblockSourceMode),
WblockBlockName(String),
@@ -4602,6 +4609,7 @@ impl OpenCADStudio {
layer_translator: None,
drawing_units: None,
block_definition: None,
block_definition_open_editor: None,
block_edit_picker: None,
block_save_as: None,
pdf_attach: None,
+19 -8
View File
@@ -5686,6 +5686,7 @@ impl OpenCADStudio {
state.error_message = None;
state.confirm_redefine = None;
}
self.refresh_block_definition_preview();
Task::none()
}
Message::BlockDefNameSelect(chosen) => {
@@ -5701,6 +5702,7 @@ impl OpenCADStudio {
);
state.set_base_point(glam::DVec3::new(base.x, base.y, base.z));
}
self.refresh_block_definition_preview();
Task::none()
}
Message::BlockDefBaseOnScreen(val) => {
@@ -5821,6 +5823,12 @@ impl OpenCADStudio {
}
Task::none()
}
Message::BlockDefOpenInEditor(on) => {
if let Some(state) = self.block_definition.as_mut() {
state.open_in_editor = on;
}
Task::none()
}
Message::BlockDefHelp => {
self.command_line.push_info(
crate::t!("BLOCK creates a block definition from objects you select.").as_ref(),
@@ -5836,7 +5844,8 @@ impl OpenCADStudio {
}
self.commit_block_definition(true)
}
Message::BlockDefApply => {
m @ (Message::BlockDefApply | Message::BlockDefCreateEmpty) => {
let create_empty = matches!(m, Message::BlockDefCreateEmpty);
let Some(state) = self.block_definition.as_mut() else {
return Task::none();
};
@@ -5859,15 +5868,16 @@ impl OpenCADStudio {
Some(crate::t!("Block name cannot start with '*'.").into_owned());
return Task::none();
}
if !state.objects_specify_onscreen && state.selected_handles.is_empty() {
state.error_message = Some(
crate::t!(
"No objects selected. You must select objects to define a block."
)
.into_owned(),
);
// No objects: the band offers to select some or to define
// an empty block.
if !state.objects_specify_onscreen
&& state.selected_handles.is_empty()
&& !create_empty
{
state.empty_prompt = true;
return Task::none();
}
state.empty_prompt = false;
let i = self.active_tab;
if self.tabs[i]
.scene
@@ -7394,6 +7404,7 @@ impl OpenCADStudio {
.collect();
block_def.error_message = None;
self.active_modal = Some(super::ModalKind::BlockDefinition);
self.refresh_block_definition_preview();
} else if let Some(ref mut wblock) = self.wblock {
wblock.selected_handles = self.tabs[i]
.scene
+1 -1
View File
@@ -505,7 +505,7 @@ impl OpenCADStudio {
}
super::super::ModalKind::BlockDefinition => {
let state = self.block_definition.as_ref()?;
sized_flow(ex, 580, 390, |flow| {
sized_flow(ex, 600, 560, |flow| {
crate::ui::window::block_definition::view_window(state, flow)
})
}