// BEDIT — block editor as a dedicated "space" tab (issue #261). // // Workflow: // 1. BEDIT: user picks an INSERT (or has one pre-selected). // 2. A block-editor space opens: rendering, picking and new geometry are // scoped to the block's record, so ONLY the block's own (block-local) // entities are drawn — no INSERT transform, they show exactly as stored. // 3. The user edits them with normal commands. Because they ARE the block // definition's entities, edits are live. // 4. Save Block (BEDIT_SAVE): keep the edits and leave the block space. // Discard (BEDIT_DISCARD): restore the block to its on-entry snapshot. // // Unlike REFEDIT (which copies entities into model space with the INSERT // transform and inverse-transforms them back on save), a BEDIT space edits the // block record in place, so there is no copy-back and no transform — the space // render filter (`current_layout_block_handle`) already draws only the block. use acadrust::{EntityType, Handle}; use glam::DVec3; use crate::command::{CadCommand, CmdResult}; use crate::modules::{IconKind, ModuleEvent, ToolDef}; /// Right-edge side-toolbar buttons shown while a BEDIT block-editor space is /// active: save the block edits and leave, or discard them. pub fn block_edit_tools() -> Vec { vec![ ToolDef { id: "BEDIT_SAVE", label: "Save Block", icon: IconKind::Svg(include_bytes!("../../../../assets/icons/mt_ok.svg")), event: ModuleEvent::Command("BEDIT_SAVE".to_string()), }, ToolDef { id: "BEDIT_DISCARD", label: "Discard Block Edit", icon: IconKind::Svg(include_bytes!("../../../../assets/icons/mt_cancel.svg")), event: ModuleEvent::Command("BEDIT_DISCARD".to_string()), }, ] } // ── Session state (held in DocumentTab) ─────────────────────────────────── /// Active BEDIT block-editor session. Lives in `DocumentTab::block_edit`. #[derive(Clone)] pub struct BlockEditSession { /// Name of the block being edited (also the space-tab label). pub block_name: String, /// Handle of the block record whose entities are being edited. pub br_handle: Handle, /// Space to return to when the block editor closes (the layout that was /// active when BEDIT began). pub return_layout: String, /// The block's block-local entities captured on entry, so Discard can /// restore the definition (structural Block/BlockEnd/AttDef excluded). pub snapshot: Vec, /// Camera state of the space that was active when BEDIT began, restored /// on Save/Discard so the view returns exactly where it was (#425). pub return_camera: crate::scene::view::camera::Camera, } // ── BEDIT pick command ───────────────────────────────────────────────────── /// Step 1: wait for the user to pick a single INSERT entity to edit. pub struct BlockEditPickCommand; impl BlockEditPickCommand { pub fn new() -> Self { Self } } impl CadCommand for BlockEditPickCommand { fn name(&self) -> &'static str { "BEDIT" } fn prompt(&self) -> String { "BEDIT Select block reference to edit:".into() } fn needs_entity_pick(&self) -> bool { true } fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult { if handle.is_null() { return CmdResult::NeedPoint; } // Signal the host to open the block-editor space for this INSERT. CmdResult::Relaunch(format!("BEDIT_BEGIN:{}", handle.value()), vec![handle]) } fn on_point(&mut self, _pt: DVec3) -> CmdResult { CmdResult::NeedPoint } fn on_enter(&mut self) -> CmdResult { CmdResult::Cancel } } // ── Autocomplete registry ───────────────────────────────── inventory::submit!(crate::command::CommandRegistration { names: &["BEDIT"] }); // BlockEditPickCommand inventory::submit!(crate::command::CommandRegistration { names: &["BEDIT_SAVE"] }); inventory::submit!(crate::command::CommandRegistration { names: &["BEDIT_DISCARD"] });