feat(array): ARRAYEDIT Source edits what an associative array repeats

ARRAYEDIT picks an associative array (or takes the preselected one) and
its Source option opens the objects every item repeats in the Block
Editor; saving there updates all items at once. Counts and spacings stay
in Properties.

Refs #1109

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Hakan SevenandClaude Opus 5.5 committed 2026-10-13 22:29:56 +03:00
1 parent 780b54489e
commit 881d237629
26 files changed
+187

No files matched your search

+39
View File
@@ -301,6 +301,45 @@ impl OpenCADStudio {
}
}
// ARRAYEDIT — edit an associative array; a preselected one skips
// the pick.
"ARRAYEDIT" => {
use crate::modules::draw::modify::arrayedit::ArrayEditCommand;
let selected = self.tabs[i].scene.selected_handles_in_order();
let array = match selected.as_slice() {
[handle] => Some(*handle),
_ => None,
};
self.tabs[i].scene.deselect_all();
let command = ArrayEditCommand::new(array);
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
}
// ARRAYEDIT Source: edit the objects the array repeats in the
// Block Editor; every item follows when it is saved.
cmd if cmd.starts_with("ARRAYEDIT_SOURCE:") => {
let handle = codec::Handle::new(
cmd["ARRAYEDIT_SOURCE:".len()..].parse().unwrap_or(0),
);
let document = &self.tabs[i].scene.document;
let source = crate::scene::assoc_array::array_of_insert(document, handle)
.and_then(|array| {
document
.block_records
.iter()
.find(|record| record.handle == array.source_block)
.map(|record| record.name.clone())
});
match source {
Some(name) => return Some(self.dispatch_command(&format!("BEDIT_OPEN:{name}"))),
None => self.command_line.push_error(
crate::t!("ARRAYEDIT: the selected object is not an associative array.")
.as_ref(),
),
}
}
// PASTESPEC — offer the copied objects now; the system clipboard's
// text joins the formats once read (Message::PasteSpecial).
"PASTESPEC" => {
+3
View File
@@ -802,6 +802,9 @@ pub(super) fn message_attribute(source: &str) -> Option<(&'static str, &'static
"BACKGROUND colour [Default/Black/DarkGray/Gray/LightGray/White] or R G B (0–255):" => Some(("file", "background-colour-default-black-darkgray-gray")),
"BASE Specify base point:" => Some(("insert", "base-specify-base-point")),
"BEDIT Select block reference to edit:" => Some(("modify", "bedit-select-block-reference-to-edit")),
"ARRAYEDIT Select array:" => Some(("modify", "arrayedit-select-array")),
"ARRAYEDIT Enter an option [Source/eXit] <eXit>:" => Some(("modify", "arrayedit-options")),
"ARRAYEDIT: the selected object is not an associative array." => Some(("modify", "arrayedit-not-an-array")),
"BEDIT: Block \"{}\" edit discarded." => Some(("inquiry", "bedit-block-edit-discarded")),
"BEDIT: Block \"{}\" saved. All references updated." => Some(("inquiry", "bedit-block-saved-all-references-updated")),
"BEDIT: Editing block \"{}\". Use Save Block or Discard to finish." => Some(("inquiry", "bedit-editing-block-use-save-block-or-discard")),
+78
View File
@@ -0,0 +1,78 @@
// ARRAYEDIT — edit an associative array. Counts and spacings are edited in
// Properties; Source opens the objects every item repeats in the Block
// Editor, and closing it updates all items at once.
use crate::command::{CadCommand, CmdOption, CmdResult};
use codec::Handle;
use glam::DVec3;
pub struct ArrayEditCommand {
array: Option<Handle>,
}
impl ArrayEditCommand {
pub fn new(array: Option<Handle>) -> Self {
Self { array }
}
}
impl CadCommand for ArrayEditCommand {
fn name(&self) -> &'static str {
"ARRAYEDIT"
}
fn prompt(&self) -> String {
match self.array {
None => crate::t!("ARRAYEDIT Select array:").into_owned(),
Some(_) => crate::t!("ARRAYEDIT Enter an option [Source/eXit] <eXit>:").into_owned(),
}
}
fn options(&self) -> Vec<CmdOption> {
match self.array {
None => Vec::new(),
Some(_) => vec![
CmdOption::new(crate::t!("Source").as_ref(), "S"),
CmdOption::new(crate::t!("eXit").as_ref(), "X"),
],
}
}
fn needs_entity_pick(&self) -> bool {
self.array.is_none()
}
fn on_entity_pick(&mut self, handle: Handle, _pt: DVec3) -> CmdResult {
if handle.is_null() {
return CmdResult::NeedPoint;
}
self.array = Some(handle);
CmdResult::NeedPoint
}
fn wants_text_input(&self) -> bool {
self.array.is_some()
}
fn on_text_input(&mut self, text: &str) -> Option<CmdResult> {
let array = self.array?;
match text.trim().trim_start_matches('_').to_ascii_uppercase().as_str() {
"S" | "SOURCE" => Some(CmdResult::Dispatch(format!(
"ARRAYEDIT_SOURCE:{}",
array.value()
))),
"X" | "EXIT" => Some(CmdResult::Cancel),
_ => None,
}
}
fn on_point(&mut self, _pt: DVec3) -> CmdResult {
CmdResult::NeedPoint
}
fn on_enter(&mut self) -> CmdResult {
CmdResult::Cancel
}
}
inventory::submit!(crate::command::CommandRegistration { names: &["ARRAYEDIT"] });
+1
View File
@@ -1,5 +1,6 @@
pub mod align;
pub mod array;
pub mod arrayedit;
pub mod attedit;
pub mod blend;
pub mod block_edit;