diff --git a/locales/ar-SA/opencadstudio.ftl b/locales/ar-SA/opencadstudio.ftl index 1bdca29d6..99a821bea 100644 --- a/locales/ar-SA/opencadstudio.ftl +++ b/locales/ar-SA/opencadstudio.ftl @@ -6620,3 +6620,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/bg-BG/opencadstudio.ftl b/locales/bg-BG/opencadstudio.ftl index 01d6458d0..528e8ea27 100644 --- a/locales/bg-BG/opencadstudio.ftl +++ b/locales/bg-BG/opencadstudio.ftl @@ -6608,3 +6608,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/cs-CZ/opencadstudio.ftl b/locales/cs-CZ/opencadstudio.ftl index 641d25bde..382990834 100644 --- a/locales/cs-CZ/opencadstudio.ftl +++ b/locales/cs-CZ/opencadstudio.ftl @@ -6605,3 +6605,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/de-DE/opencadstudio.ftl b/locales/de-DE/opencadstudio.ftl index fabe8978e..11d1488bd 100644 --- a/locales/de-DE/opencadstudio.ftl +++ b/locales/de-DE/opencadstudio.ftl @@ -6606,3 +6606,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/el-GR/opencadstudio.ftl b/locales/el-GR/opencadstudio.ftl index 2909b23af..b2a8b4c96 100644 --- a/locales/el-GR/opencadstudio.ftl +++ b/locales/el-GR/opencadstudio.ftl @@ -6608,3 +6608,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/en-US/opencadstudio.ftl b/locales/en-US/opencadstudio.ftl index 152cc6375..318730cea 100644 --- a/locales/en-US/opencadstudio.ftl +++ b/locales/en-US/opencadstudio.ftl @@ -6607,3 +6607,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/es-ES/opencadstudio.ftl b/locales/es-ES/opencadstudio.ftl index 2104bc40c..531f8c66c 100644 --- a/locales/es-ES/opencadstudio.ftl +++ b/locales/es-ES/opencadstudio.ftl @@ -6608,3 +6608,9 @@ ucs-follow = .prompt = UCSFOLLOW: introduce 0 (conservar vista) o 1 (seguir SCP) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activa una ventana de modelo; no se aplica al espacio papel. .invalid = UCSFOLLOW: solo se permite 0 o 1. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/fi-FI/opencadstudio.ftl b/locales/fi-FI/opencadstudio.ftl index dadf2e21e..88499a240 100644 --- a/locales/fi-FI/opencadstudio.ftl +++ b/locales/fi-FI/opencadstudio.ftl @@ -6605,3 +6605,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/fr-FR/opencadstudio.ftl b/locales/fr-FR/opencadstudio.ftl index 2bb29a725..dca7b0895 100644 --- a/locales/fr-FR/opencadstudio.ftl +++ b/locales/fr-FR/opencadstudio.ftl @@ -6608,3 +6608,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/hi-IN/opencadstudio.ftl b/locales/hi-IN/opencadstudio.ftl index 775ce9d2d..a0d4f3060 100644 --- a/locales/hi-IN/opencadstudio.ftl +++ b/locales/hi-IN/opencadstudio.ftl @@ -6596,3 +6596,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/hu-HU/opencadstudio.ftl b/locales/hu-HU/opencadstudio.ftl index dacea1c4c..4b497a1c8 100644 --- a/locales/hu-HU/opencadstudio.ftl +++ b/locales/hu-HU/opencadstudio.ftl @@ -6592,3 +6592,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/it-IT/opencadstudio.ftl b/locales/it-IT/opencadstudio.ftl index ec7e22f51..585a3cae0 100644 --- a/locales/it-IT/opencadstudio.ftl +++ b/locales/it-IT/opencadstudio.ftl @@ -6607,3 +6607,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/ja-JP/opencadstudio.ftl b/locales/ja-JP/opencadstudio.ftl index f11fc5204..8b768c62e 100644 --- a/locales/ja-JP/opencadstudio.ftl +++ b/locales/ja-JP/opencadstudio.ftl @@ -6596,3 +6596,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/ko-KR/opencadstudio.ftl b/locales/ko-KR/opencadstudio.ftl index d010218d4..d96d7a167 100644 --- a/locales/ko-KR/opencadstudio.ftl +++ b/locales/ko-KR/opencadstudio.ftl @@ -6607,3 +6607,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/lo-LA/opencadstudio.ftl b/locales/lo-LA/opencadstudio.ftl index 0156752fd..26732ea02 100644 --- a/locales/lo-LA/opencadstudio.ftl +++ b/locales/lo-LA/opencadstudio.ftl @@ -6608,3 +6608,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/nl-NL/opencadstudio.ftl b/locales/nl-NL/opencadstudio.ftl index 42c14ea0c..021b22467 100644 --- a/locales/nl-NL/opencadstudio.ftl +++ b/locales/nl-NL/opencadstudio.ftl @@ -6606,3 +6606,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/pl-PL/opencadstudio.ftl b/locales/pl-PL/opencadstudio.ftl index 35cfbf57f..e8ff9d4a4 100644 --- a/locales/pl-PL/opencadstudio.ftl +++ b/locales/pl-PL/opencadstudio.ftl @@ -6607,3 +6607,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/pt-BR/opencadstudio.ftl b/locales/pt-BR/opencadstudio.ftl index c49e81d75..bf5c64b9e 100644 --- a/locales/pt-BR/opencadstudio.ftl +++ b/locales/pt-BR/opencadstudio.ftl @@ -6608,3 +6608,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/ru-RU/opencadstudio.ftl b/locales/ru-RU/opencadstudio.ftl index 683169314..c25ebccbf 100644 --- a/locales/ru-RU/opencadstudio.ftl +++ b/locales/ru-RU/opencadstudio.ftl @@ -6609,3 +6609,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/tr-TR/opencadstudio.ftl b/locales/tr-TR/opencadstudio.ftl index 6cf148f60..4f8e88cf4 100644 --- a/locales/tr-TR/opencadstudio.ftl +++ b/locales/tr-TR/opencadstudio.ftl @@ -6529,3 +6529,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Oluşturduktan sonra blok düzenleyicide aç + .remembered = Ekranda belirt, nesne seçeneği, dağıtmaya izin ve bu anahtar sonraki açılışta hatırlanır. + .no-objects = Blok için nesne seçilmedi. + .create-empty = Boş blok oluştur diff --git a/locales/zh-CN/opencadstudio.ftl b/locales/zh-CN/opencadstudio.ftl index 384adc93c..fbb6a693b 100644 --- a/locales/zh-CN/opencadstudio.ftl +++ b/locales/zh-CN/opencadstudio.ftl @@ -6591,3 +6591,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/locales/zh-TW/opencadstudio.ftl b/locales/zh-TW/opencadstudio.ftl index 9afc4ece7..b33d8b1f2 100644 --- a/locales/zh-TW/opencadstudio.ftl +++ b/locales/zh-TW/opencadstudio.ftl @@ -6603,3 +6603,9 @@ ucs-follow = .prompt = UCSFOLLOW: enter 0 (keep view) or 1 (follow UCS) <__ocs_fmt_0__>: .paper-space = UCSFOLLOW: activate a model viewport; paper space cannot follow the UCS. .invalid = UCSFOLLOW: only 0 or 1 is allowed. + +block-def-dialog = + .open-in-editor = Open in block editor after creating + .remembered = Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time. + .no-objects = No objects are selected for the block. + .create-empty = Create empty block diff --git a/src/app/command_driver/entity_commit.rs b/src/app/command_driver/entity_commit.rs index 6efce0043..559961a35 100644 --- a/src/app/command_driver/entity_commit.rs +++ b/src/app/command_driver/entity_commit.rs @@ -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); diff --git a/src/app/commands/blocks.rs b/src/app/commands/blocks.rs index e59fdbb38..a10432569 100644 --- a/src/app/commands/blocks.rs +++ b/src/app/commands/blocks.rs @@ -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,59 @@ 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 mut wires = Vec::new(); + for handle in &state.selected_handles { + match scene.document.get_entity(*handle) { + // A reference is drawn from its definition, placed by the + // reference's transform. + Some(codec::EntityType::Insert(insert)) => { + let base = crate::scene::render_graph::block_base_point( + &scene.document, + &insert.block_name, + ); + let xform = + crate::scene::render_graph::insert_transform_with_base(insert, base); + for mut wire in scene.block_preview_wires(&insert.block_name) { + for p in &mut wire.points { + if p.iter().all(|v| v.is_finite()) { + let q = xform.apply(codec::types::Vector3::new( + f64::from(p[0]), + f64::from(p[1]), + f64::from(p[2]), + )); + *p = [q.x as f32, q.y as f32, q.z as f32]; + } + } + wires.push(wire); + } + } + Some(entity) => { + wires.extend(scene.wires_for_entities(std::slice::from_ref(entity))) + } + None => {} + } + } + wires + }; + } + pub(super) fn dispatch_blocks(&mut self, cmd: &str, i: usize) -> Option> { match cmd { // ── BASE — drawing insertion base point ─────────────────────── @@ -469,6 +526,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 +562,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 ") => { diff --git a/src/app/control/actions.rs b/src/app/control/actions.rs index 345ae4af0..ce75ab662 100644 --- a/src/app/control/actions.rs +++ b/src/app/control/actions.rs @@ -120,6 +120,7 @@ pub(super) const NAMES: &[&str] = &[ "ribbon_dropdown", "keytips", "dialog_ok", + "block_def_dialog", "attdef_dialog", "field_dialog", "sheet_set", @@ -1504,6 +1505,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=…`, diff --git a/src/app/mod.rs b/src/app/mod.rs index 22ed7bf44..cbda679f2 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -581,6 +581,9 @@ pub(super) struct OpenCADStudio { drawing_units: Option, /// Working copy of the Block Definition dialog; `None` while it is closed. block_definition: Option, + /// 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, block_edit_picker: Option, block_save_as: Option, /// PDF dialogs' working copies; `None` while closed. @@ -3463,6 +3466,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), @@ -4625,6 +4632,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, diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 0e2792ccd..24e279ede 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -5750,6 +5750,7 @@ impl OpenCADStudio { state.error_message = None; state.confirm_redefine = None; } + self.refresh_block_definition_preview(); Task::none() } Message::BlockDefNameSelect(chosen) => { @@ -5765,6 +5766,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) => { @@ -5886,6 +5888,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(), @@ -5901,7 +5909,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(); }; @@ -5924,15 +5933,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 @@ -7459,6 +7469,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 diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index cae7c00d6..a6c6a4e3d 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -506,7 +506,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) }) } diff --git a/src/locale_catalog.rs b/src/locale_catalog.rs index 35ceea96d..d421411a5 100644 --- a/src/locale_catalog.rs +++ b/src/locale_catalog.rs @@ -3,6 +3,10 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static str)> { match source { + "Open in block editor after creating" => Some(("block-def-dialog", "open-in-editor")), + "Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time." => Some(("block-def-dialog", "remembered")), + "No objects are selected for the block." => Some(("block-def-dialog", "no-objects")), + "Create empty block" => Some(("block-def-dialog", "create-empty")), "{} entities added" => Some(("ref-edit", "entities-added")), "Use REFCLOSE or the Refedit toolbar to end reference editing session." => Some(("ref-edit", "use-refclose")), "Specify opposite corner:" => Some(("ref-edit", "opposite-corner")), diff --git a/src/ui/window/block_definition.rs b/src/ui/window/block_definition.rs index 8f76f5331..e500f96c0 100644 --- a/src/ui/window/block_definition.rs +++ b/src/ui/window/block_definition.rs @@ -11,6 +11,7 @@ use iced::widget::{ use iced::{Background, Border, Element, Fill, Length, Theme}; use crate::app::Message; +use crate::scene::WireModel; use crate::modules::draw::units; use crate::t; use crate::ui::style::common::muted_style; @@ -73,6 +74,14 @@ pub struct BlockDefinitionState { pub error_message: Option, pub confirm_redefine: Option, pub desc_placeholder: String, + /// The selected objects, or the chosen existing definition, as drawn in + /// the name card. + pub preview: Vec, + /// OK was pressed with no objects: the band offers to select some or to + /// define an empty block. + pub empty_prompt: bool, + /// Open the new block in the block editor once it is defined. + pub open_in_editor: bool, } impl Clone for BlockDefinitionState { @@ -100,6 +109,9 @@ impl Clone for BlockDefinitionState { error_message: self.error_message.clone(), confirm_redefine: self.confirm_redefine.clone(), desc_placeholder: self.desc_placeholder.clone(), + preview: self.preview.clone(), + empty_prompt: self.empty_prompt, + open_in_editor: self.open_in_editor, } } } @@ -132,6 +144,9 @@ impl BlockDefinitionState { error_message: None, confirm_redefine: None, desc_placeholder: crate::t!("Enter block description...").into_owned(), + preview: Vec::new(), + empty_prompt: false, + open_in_editor: remembered.open_in_editor, } } @@ -219,6 +234,45 @@ pub(crate) fn labeled_checkbox<'a>( .into() } +/// The in-window warning shown when OK is pressed with no objects: select +/// some, or define an empty block. +fn empty_band<'a>() -> Element<'a, Message> { + container( + row![ + crate::ui::icons::semantic(ICON_WARN, 15.0), + text(t!("No objects are selected for the block.")) + .size(11) + .width(Fill), + button(text(t!("Select objects")).size(11)) + .on_press(Message::BlockDefSelectObjects) + .style(button_style(false)) + .padding([4, 12]), + button(text(t!("Create empty block")).size(11)) + .on_press(Message::BlockDefCreateEmpty) + .style(button_style(false)) + .padding([4, 12]), + ] + .spacing(10) + .align_y(iced::Center), + ) + .padding([7, 10]) + .width(Fill) + .style(|_: &Theme| { + // Amber: the theme's warning colour is not a caution tone in every theme. + let warn = iced::Color::from_rgb8(0xD9, 0xA4, 0x00); + container::Style { + background: Some(Background::Color(warn.scale_alpha(0.16))), + border: Border { + width: 1.0, + radius: 6.0.into(), + color: warn, + }, + ..Default::default() + } + }) + .into() +} + pub fn view_window<'a>( state: &'a BlockDefinitionState, sizing: crate::ui::modal::ModalSizing, @@ -274,7 +328,21 @@ pub fn view_window<'a>( name_column = name_column.push(hint); } - let name_section = name_column; + // The name card carries a live preview of the selected objects, or of + // the existing definition the name picks. + let preview = container(super::block_palette::preview_canvas(&state.preview, 86.0)) + .padding(3) + .width(Length::Fixed(150.0)) + .height(Length::Fixed(92.0)) + .style(crate::ui::window::pdf_dialogs::well_style); + let name_section = container( + row![name_column, preview] + .spacing(12) + .align_y(iced::Center), + ) + .padding([8, 10]) + .width(Fill) + .style(crate::ui::window::pdf_dialogs::card_style); // ── Group: Base Point ───────────────────────────────────────────────────── let base_onscreen = labeled_checkbox( @@ -587,16 +655,49 @@ pub fn view_window<'a>( // ── Footer Bar ──────────────────────────────────────────────────────────── let actions = row![ + button(text("?").size(12)) + .on_press(Message::BlockDefHelp) + .style(button_style(false)) + .padding([3, 10]), Space::new().width(Fill), - dialog_button(t!("OK"), Message::BlockDefApply, true), dialog_button(t!("Cancel"), Message::CloseModal, false), - dialog_button(t!("Help"), Message::BlockDefHelp, false), + dialog_button(t!("OK"), Message::BlockDefApply, true), ] .spacing(8) .align_y(iced::Center); + // ── Open in the block editor ───────────────────────────────────────────── + let editor_card = container( + column![ + row![ + iced::widget::toggler(state.open_in_editor) + .on_toggle(Message::BlockDefOpenInEditor) + .size(16.0), + text(t!("Open in block editor after creating")).size(11), + ] + .spacing(8) + .align_y(iced::Center), + text(t!( + "Specify On-screen, the object option, Allow exploding and this switch are remembered for the next time." + )) + .size(10) + .style(muted_style), + ] + .spacing(4), + ) + .padding([8, 10]) + .width(Fill) + .style(crate::ui::window::pdf_dialogs::card_style); + // ── Error Banner ────────────────────────────────────────────────────────── - let mut main_column = column![name_section, main_body, actions,] + let mut main_column = column![name_section]; + if state.empty_prompt { + main_column = main_column.push(empty_band()); + } + let mut main_column = main_column + .push(main_body) + .push(editor_card) + .push(actions) .spacing(6) .padding([8, 10]) .width(sizing.width);