From 618536950879442a8c85160f81f62a40a513ede9 Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 9 Sep 2026 09:06:34 +0200 Subject: [PATCH] Add configurable block double-click editing modes --- src/app/mod.rs | 10 ++++++++++ src/app/settings.rs | 6 ++++++ src/app/update/file.rs | 4 ++++ src/app/update/mod.rs | 12 ++++++++++++ src/app/update/viewport.rs | 20 ++++++++++++++------ src/app/view/modal.rs | 2 ++ src/scene/entity.rs | 7 +++++++ src/ui/window/options.rs | 33 +++++++++++++++++++++++++++++++-- 8 files changed, 86 insertions(+), 8 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 057ddd875..f39aecf0e 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -500,6 +500,10 @@ pub(super) struct OpenCADStudio { cursor_size: i32, /// Selection-box size setting (PICKBOX, 0..=50). pick_box: i32, + /// Use REFEDIT rather than BEDIT when double-clicking an attribute-free block. + double_click_block_refedit: bool, + /// Open ATTEDIT when double-clicking a block with attributes. + double_click_block_attedit: bool, /// Drawing viewport cursor style (CURSORTYPE). cursor_type: settings::CursorType, /// Explicit crosshair colour; `None` retains automatic contrast. @@ -1937,6 +1941,10 @@ pub enum Message { CursorSizeChanged(i32), /// Set PICKBOX from the Selection-page slider. PickBoxChanged(i32), + /// Choose whether double-clicking a block starts BEDIT or REFEDIT. + DoubleClickBlockRefeditChanged(bool), + /// Choose whether double-clicking a block with attributes starts ATTEDIT. + DoubleClickBlockAtteditChanged(bool), /// Set CURSORTYPE from Options. CursorTypeChanged(settings::CursorType), /// Set the model-space lineweight preview scale from Options. @@ -3369,6 +3377,8 @@ impl OpenCADStudio { zoom_factor: 60, cursor_size: 5, pick_box: 3, + double_click_block_refedit: false, + double_click_block_attedit: true, cursor_type: settings::CursorType::Crosshair, crosshair_color: None, crosshair_color_input: String::new(), diff --git a/src/app/settings.rs b/src/app/settings.rs index a9ca0cb55..8076aee86 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -160,6 +160,10 @@ pub struct UserSettings { pub cursor_size: i32, /// PICKBOX: normalized visible-box and click-aperture size. pub pick_box: i32, + /// When true, double-clicking a block reference starts REFEDIT instead of BEDIT. + pub double_click_block_refedit: bool, + /// When true, double-clicking a block with attributes opens ATTEDIT. + pub double_click_block_attedit: bool, /// CURSORTYPE: crosshair or the platform pointer over the drawing. pub cursor_type: CursorType, /// Explicit crosshair RGB. `None` keeps automatic background contrast. @@ -307,6 +311,8 @@ impl Default for UserSettings { zoom_factor: 60, cursor_size: 5, pick_box: 3, + double_click_block_refedit: false, + double_click_block_attedit: true, cursor_type: CursorType::Crosshair, crosshair_color: None, lineweight_display_scale: 100, diff --git a/src/app/update/file.rs b/src/app/update/file.rs index bc7768031..b6be2aa33 100644 --- a/src/app/update/file.rs +++ b/src/app/update/file.rs @@ -365,6 +365,8 @@ impl OpenCADStudio { zoom_factor: self.zoom_factor, cursor_size: self.cursor_size, pick_box: self.pick_box, + double_click_block_refedit: self.double_click_block_refedit, + double_click_block_attedit: self.double_click_block_attedit, cursor_type: self.cursor_type, crosshair_color: self.crosshair_color, lineweight_display_scale: self.lineweight_display_scale, @@ -418,6 +420,8 @@ impl OpenCADStudio { self.zoom_factor = s.zoom_factor.clamp(3, 100); self.cursor_size = s.cursor_size.clamp(1, 100); self.pick_box = s.pick_box.clamp(0, 50); + self.double_click_block_refedit = s.double_click_block_refedit; + self.double_click_block_attedit = s.double_click_block_attedit; self.cursor_type = s.cursor_type; self.crosshair_color = s.crosshair_color; self.crosshair_color_input = s diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index a76158d93..05168a240 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -5894,6 +5894,18 @@ impl OpenCADStudio { Task::none() } + Message::DoubleClickBlockRefeditChanged(enabled) => { + self.double_click_block_refedit = enabled; + self.persist_settings_if_changed(); + Task::none() + } + + Message::DoubleClickBlockAtteditChanged(enabled) => { + self.double_click_block_attedit = enabled; + self.persist_settings_if_changed(); + Task::none() + } + Message::CursorTypeChanged(value) => { self.cursor_type = value; self.persist_settings_if_changed(); diff --git a/src/app/update/viewport.rs b/src/app/update/viewport.rs index 12c339a01..9432de250 100644 --- a/src/app/update/viewport.rs +++ b/src/app/update/viewport.rs @@ -4673,15 +4673,17 @@ impl OpenCADStudio { return self.begin_text_edit(handle); } // Double-clicking a block with attributes opens the - // attribute editor (edit its values); a block with - // no attributes opens the block editor (BEDIT) — a - // space tab scoped to the block's own geometry, so - // edits reflect in every instance. (#136, #192, #261) + // attribute editor (edit its values). For blocks without + // attributes, Options > Drawing chooses BEDIT (default) + // or in-place REFEDIT. (#136, #192, #261) let insert_has_attrs = matches!( self.tabs[i].scene.document.get_entity(handle), Some(AcadEntityType::Insert(ins)) if !ins.attributes.is_empty() ); - if insert_has_attrs && self.tabs[i].active_block_edit.is_none() { + if insert_has_attrs + && self.double_click_block_attedit + && self.tabs[i].active_block_edit.is_none() + { return Task::done(Message::AttrEditorOpen(handle)); } let is_insert = matches!( @@ -4689,8 +4691,14 @@ impl OpenCADStudio { Some(AcadEntityType::Insert(_)) ); if is_insert && self.tabs[i].refedit_session.is_none() { + let command = if self.double_click_block_refedit { + "REFEDIT_BEGIN" + } else { + "BEDIT_BEGIN" + }; return Task::done(Message::Command(format!( - "BEDIT_BEGIN:{}", + "{}:{}", + command, handle.value() ))); } diff --git a/src/app/view/modal.rs b/src/app/view/modal.rs index 799058559..f59831c73 100644 --- a/src/app/view/modal.rs +++ b/src/app/view/modal.rs @@ -187,6 +187,8 @@ impl OpenCADStudio { self.options_tab, self.cursor_size, self.pick_box, + self.double_click_block_refedit, + self.double_click_block_attedit, self.cursor_type, self.crosshair_color, &self.crosshair_color_input, diff --git a/src/scene/entity.rs b/src/scene/entity.rs index ca4609047..af068e6ad 100644 --- a/src/scene/entity.rs +++ b/src/scene/entity.rs @@ -298,6 +298,13 @@ impl Scene { }; if !handle.is_null() { + // New geometry drawn during REFEDIT belongs to the edited block, + // so it must stay bright along with the copied block entities. + // The remaining model-space entities are only visible as faded + // context. + if let Some(keep) = self.refedit_keep.as_mut() { + keep.insert(handle); + } self.invalidate_dependency_index(); if let Some(model) = hatch_seed { self.hatches.insert(handle, model); diff --git a/src/ui/window/options.rs b/src/ui/window/options.rs index 4a8d3f3b5..cf73444c3 100644 --- a/src/ui/window/options.rs +++ b/src/ui/window/options.rs @@ -13,6 +13,7 @@ pub enum OptionsTab { General, Display, Selection, + Drawing, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -37,6 +38,8 @@ pub fn view_window<'a>( active_tab: OptionsTab, cursor_size: i32, pick_box: i32, + double_click_block_refedit: bool, + double_click_block_attedit: bool, cursor_type: CursorType, crosshair_color: Option<[u8; 3]>, crosshair_color_input: &'a str, @@ -699,10 +702,35 @@ pub fn view_window<'a>( .spacing(0) .width(sizing.width); + let drawing = column![ + text(crate::t!("Block Edit")).size(15), + Space::new().height(10), + row![ + iced::widget::checkbox(double_click_block_refedit) + .on_toggle(Message::DoubleClickBlockRefeditChanged) + .size(15), + text(crate::t!("Double-click to edit block in-place (REFEDIT)")).size(12), + ] + .spacing(8) + .align_y(iced::Center), + Space::new().height(18), + row![ + iced::widget::checkbox(double_click_block_attedit) + .on_toggle(Message::DoubleClickBlockAtteditChanged) + .size(15), + text(crate::t!("Double-click attributed blocks to edit attributes (ATTEDIT)")).size(12), + ] + .spacing(8) + .align_y(iced::Center), + ] + .spacing(0) + .width(sizing.width); + let content: Element<'a, Message> = match active_tab { OptionsTab::General => general.into(), OptionsTab::Display => display_element.into(), OptionsTab::Selection => selection.into(), + OptionsTab::Drawing => drawing.into(), }; let tab_button = |label, tab| { @@ -716,6 +744,7 @@ pub fn view_window<'a>( tab_button(crate::t!("General"), OptionsTab::General), tab_button(crate::t!("Display"), OptionsTab::Display), tab_button(crate::t!("Selection"), OptionsTab::Selection), + tab_button(crate::t!("Drawing"), OptionsTab::Drawing), ] .spacing(6); @@ -732,9 +761,9 @@ pub fn view_window<'a>( .height(sizing.height); container(body) + .width(540) + .height(560) .style(container::rounded_box) .padding([16, 18]) - .width(sizing.width) - .height(sizing.height) .into() }