REFEDIT: Edit Reference dialog and unique names for xref objects

REFEDIT opens a dialog after the pick: the nesting chain as chips with a
preview of the chosen definition, all objects or picking them in the
drawing, attribute definitions (offered only when the definition has
them), locking objects outside the working set and unique names for an
external reference's named objects. The settings are remembered.

With unique names an xref's dependent layers, linetypes, text styles and
blocks are checked out as $N$NAME records of their own; without, under
the plain name, sharing a host record of that name. Save writes them back
under the reference's names, and the records made for the session are
removed when it closes. -REFEDIT uses the remembered settings.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ramox81andClaude Opus 5.5 committed 2026-10-14 10:39:28 +03:00
1 parent 592289f4fd
commit 92a3a8ee4d
33 files changed
+1061 -22

No files matched your search

+13
View File
@@ -6471,6 +6471,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = حدد المرجع:
.reference-not-found = لم يتم العثور على المرجع.
.not-valid-reference = الكائن ليس مرجعًا صالحًا
+13
View File
@@ -6459,6 +6459,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Изберете препратка:
.reference-not-found = Препратката не е намерена.
.not-valid-reference = Обектът не е валидна препратка
+13
View File
@@ -6456,6 +6456,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Vyberte referenci:
.reference-not-found = Reference nebyla nalezena.
.not-valid-reference = Objekt není platná reference
+13
View File
@@ -6457,6 +6457,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Referenz wählen:
.reference-not-found = Referenz nicht gefunden.
.not-valid-reference = Objekt ist keine gültige Referenz
+13
View File
@@ -6459,6 +6459,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Επιλέξτε αναφορά:
.reference-not-found = Η αναφορά δεν βρέθηκε.
.not-valid-reference = Το αντικείμενο δεν είναι έγκυρη αναφορά
+13
View File
@@ -6458,6 +6458,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Select reference:
.reference-not-found = Reference not found.
.not-valid-reference = Object is not a valid reference
+13
View File
@@ -6459,6 +6459,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Seleccione la referencia:
.reference-not-found = No se encuentra la referencia.
.not-valid-reference = El objeto no es una referencia válida
+13
View File
@@ -6456,6 +6456,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Valitse viittaus:
.reference-not-found = Viittausta ei löydy.
.not-valid-reference = Objekti ei ole kelvollinen viittaus
+13
View File
@@ -6459,6 +6459,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Sélectionnez la référence :
.reference-not-found = Référence introuvable.
.not-valid-reference = L'objet n'est pas une référence valide
+13
View File
@@ -6447,6 +6447,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = संदर्भ चुनें:
.reference-not-found = संदर्भ नहीं मिला।
.not-valid-reference = ऑब्जेक्ट मान्य संदर्भ नहीं है
+13
View File
@@ -6443,6 +6443,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Válassza ki a hivatkozást:
.reference-not-found = A hivatkozás nem található.
.not-valid-reference = Az objektum nem érvényes hivatkozás
+13
View File
@@ -6458,6 +6458,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Selezionare il riferimento:
.reference-not-found = Riferimento non trovato.
.not-valid-reference = L'oggetto non è un riferimento valido
+13
View File
@@ -6447,6 +6447,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = 参照を選択:
.reference-not-found = 参照が見つかりません。
.not-valid-reference = オブジェクトは有効な参照ではありません
+13
View File
@@ -6458,6 +6458,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = 참조 선택:
.reference-not-found = 참조를 찾을 수 없습니다.
.not-valid-reference = 객체가 유효한 참조가 아닙니다
+13
View File
@@ -6459,6 +6459,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = ເລືອກການອ້າງອີງ:
.reference-not-found = ບໍ່ພົບການອ້າງອີງ.
.not-valid-reference = ວັດຖຸບໍ່ແມ່ນການອ້າງອີງທີ່ຖືກຕ້ອງ
+13
View File
@@ -6457,6 +6457,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Selecteer referentie:
.reference-not-found = Referentie niet gevonden.
.not-valid-reference = Object is geen geldige referentie
+13
View File
@@ -6458,6 +6458,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Wybierz odnośnik:
.reference-not-found = Nie znaleziono odnośnika.
.not-valid-reference = Obiekt nie jest prawidłowym odnośnikiem
+13
View File
@@ -6459,6 +6459,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Selecione a referência:
.reference-not-found = Referência não encontrada.
.not-valid-reference = O objeto não é uma referência válida
+13
View File
@@ -6460,6 +6460,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = Выберите ссылку:
.reference-not-found = Ссылка не найдена.
.not-valid-reference = Объект не является допустимой ссылкой
+13
View File
@@ -6380,6 +6380,19 @@ view-background =
.select-image = Arka Plan Görüntüsü Seç
ref-edit =
.dialog-title = Referansı Düzenle
.dialog-reference = Düzenlenecek referans
.dialog-chain-hint = Seçilen noktadaki zincir; bir çipe tıklayarak düzeyi değiştirin.
.dialog-path = Yol:
.dialog-working-set = Çalışma kümesi
.dialog-all-objects = Tüm nesneler
.dialog-pick-objects = Çizimde seçeceğim
.dialog-attributes = Öznitelik tanımlarını da düzenle
.dialog-session = Oturum
.dialog-lock = Kümede olmayan nesneleri kilitle
.dialog-unique = Çakışan katman, stil ve blok adlarını benzersiz yap
.dialog-start = Düzenlemeye Başla
.dialog-help = Bir blok referansını veya dış referansı yerinde düzenlemek için açar.
.select-reference = Referans seçin:
.reference-not-found = Referans bulunamadı.
.not-valid-reference = Nesne geçerli bir referans değil
+13
View File
@@ -6442,6 +6442,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = 选择参照:
.reference-not-found = 找不到参照。
.not-valid-reference = 对象不是有效的参照
+13
View File
@@ -6454,6 +6454,19 @@ view-background =
.select-image = Select Background Image
ref-edit =
.dialog-title = Reference Edit
.dialog-reference = Reference to edit
.dialog-chain-hint = The chain at the picked point; click a chip to change the level.
.dialog-path = Path:
.dialog-working-set = Working set
.dialog-all-objects = All objects
.dialog-pick-objects = Pick in the drawing
.dialog-attributes = Also edit attribute definitions
.dialog-session = Session
.dialog-lock = Lock objects not in the working set
.dialog-unique = Give clashing layer, style and block names unique names
.dialog-start = Start Editing
.dialog-help = Checks out a block reference or an external reference for editing in place.
.select-reference = 選取參考:
.reference-not-found = 找不到參考。
.not-valid-reference = 物件不是有效的參考
+330 -18
View File
@@ -4,10 +4,11 @@
use crate::app::{Message, OpenCADStudio};
use crate::modules::draw::modify::refedit::{
apply_insert_inverse_transform, apply_insert_transform, level_hits,
nesting_chain, RefCloseCommand, RefEditOptionsCommand, RefEditPickCommand, RefEditSession,
RefSetCommand, NOT_CHECKED_OUT,
apply_insert_inverse_transform, apply_insert_transform, level_hits, nesting_chain,
rename_symbols, ChainLevel, RefCloseCommand, RefEditNames, RefEditOptionsCommand,
RefEditPickCommand, RefEditSession, RefSetCommand, SymbolKind, NOT_CHECKED_OUT,
};
use crate::ui::window::refedit_dialog::{RefEditDialogMsg, RefEditDialogState, RefEditLevel};
use codec::{EntityType, Handle};
use iced::Task;
use std::collections::HashSet;
@@ -142,25 +143,127 @@ impl OpenCADStudio {
.iter()
.map(|level| level_hits(&self.tabs[i].scene.document, level))
.collect();
let command = RefEditOptionsCommand::new(top, chain, hits);
let command = RefEditOptionsCommand::new(
top,
chain,
hits,
self.refedit_options.lock,
self.refedit_options.unique_names,
);
use crate::command::CadCommand;
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
} else {
// The dialog's defaults: the innermost reference, all objects,
// attribute definitions not displayed.
let path: Vec<String> = chain[1..]
.iter()
.map(|l| l.insert.value().to_string())
.collect();
let _ = self.dispatch_command(&format!(
"REFEDIT_BEGIN:{}:{}:0:",
top.value(),
path.join(",")
));
self.open_refedit_dialog(i, top, chain);
}
}
/// The Edit Reference dialog over the picked chain: the innermost level
/// chosen, the remembered settings.
fn open_refedit_dialog(&mut self, i: usize, top: Handle, chain: Vec<ChainLevel>) {
let scene = &self.tabs[i].scene;
let document = &scene.document;
let levels = chain
.iter()
.map(|level| {
let record = document.block_records.get(&level.block_name);
RefEditLevel {
insert: level.insert,
name: level.block_name.clone(),
wires: scene.block_preview_wires(&level.block_name),
has_attdefs: record.is_some_and(|br| {
br.entity_handles.iter().any(|h| {
matches!(document.get_entity(*h), Some(EntityType::AttributeDefinition(_)))
})
}),
xref_path: record
.filter(|br| br.flags.is_xref || br.flags.is_xref_overlay)
.map(|br| br.xref_path.clone())
.filter(|p| !p.is_empty()),
}
})
.collect::<Vec<_>>();
let options = &self.refedit_options;
self.refedit_dialog = Some(RefEditDialogState {
top,
level: levels.len().saturating_sub(1),
levels,
chain,
nested: options.nested,
attributes: options.attributes,
lock: options.lock,
unique: options.unique_names,
});
self.active_modal = Some(crate::app::ModalKind::RefEdit);
}
pub(in crate::app) fn update_refedit_dialog(&mut self, message: RefEditDialogMsg) -> Task<Message> {
let Some(state) = self.refedit_dialog.as_mut() else {
return Task::none();
};
match message {
RefEditDialogMsg::Level(level) => {
state.level = level.min(state.levels.len().saturating_sub(1))
}
RefEditDialogMsg::Nested(on) => state.nested = on,
RefEditDialogMsg::Attributes(on) => state.attributes = on,
RefEditDialogMsg::Lock(on) => state.lock = on,
RefEditDialogMsg::Unique(on) => state.unique = on,
RefEditDialogMsg::Help => {
self.command_line
.push_info(&crate::tr!("ref-edit", "dialog-help"));
}
RefEditDialogMsg::Ok => {
let Some(state) = self.refedit_dialog.take() else {
return Task::none();
};
self.close_active_modal();
let attributes = state.attributes && state.attributes_enabled();
self.refedit_options = crate::app::settings::RefEditOptions {
unique_names: state.unique,
attributes: state.attributes,
lock: state.lock,
nested: state.nested,
};
let i = self.active_tab;
if state.nested {
let hits = state
.chain
.iter()
.map(|level| level_hits(&self.tabs[i].scene.document, level))
.collect();
let command = RefEditOptionsCommand::nested_pick(
state.top,
state.chain,
hits,
state.level,
attributes,
state.lock,
state.unique,
);
use crate::command::CadCommand;
self.command_line.push_info(&command.prompt());
self.tabs[i].active_cmd = Some(Box::new(command));
} else {
let last = state.level.min(state.chain.len().saturating_sub(1));
let path: Vec<String> = state.chain[1..=last]
.iter()
.map(|l| l.insert.value().to_string())
.collect();
return self.dispatch_command(&format!(
"REFEDIT_BEGIN:{}:{}:{}::{}:{}",
state.top.value(),
path.join(","),
attributes as u8,
state.lock as u8,
state.unique as u8
));
}
}
}
Task::none()
}
/// `top[:path[:attributes[:nested]]]` — open the session.
fn begin_refedit(&mut self, i: usize, args: &str) {
let mut parts = args.split(':');
@@ -181,6 +284,10 @@ impl OpenCADStudio {
let path = handles(parts.next());
let attributes = parts.next() == Some("1");
let nested = handles(parts.next());
let lock = parts.next().map_or(self.refedit_options.lock, |v| v == "1");
let unique = parts
.next()
.map_or(self.refedit_options.unique_names, |v| v == "1");
if self.tabs[i].refedit_session.is_some() || self.reject_locked_edit(i, top) {
return;
}
@@ -206,6 +313,15 @@ impl OpenCADStudio {
});
}
let level = chain.last().expect("non-empty chain").clone();
// An external reference's dependent names (`XREF|NAME`) of the
// outermost xref in the chain.
let xref_prefix = chain.iter().find_map(|l| {
document
.block_records
.get(&l.block_name)
.filter(|br| br.flags.is_xref || br.flags.is_xref_overlay)
.map(|br| format!("{}|", br.name))
});
let Some(br) = document.block_records.get(&level.block_name) else {
return;
};
@@ -240,6 +356,8 @@ impl OpenCADStudio {
// Everything allocated from here on is part of the session.
handle_watermark: document.next_handle().max(document.header.handle_seed),
xref_path,
lock,
names: RefEditNames::default(),
};
let copies: Vec<EntityType> = extracted
.iter()
@@ -247,6 +365,15 @@ impl OpenCADStudio {
.collect();
self.push_undo_snapshot(i, "REFEDIT");
let mut session = session;
let copies = match xref_prefix {
Some(prefix) => {
let (copies, names) = self.refedit_host_names(i, copies, &prefix, unique);
session.names = names;
copies
}
None => copies,
};
let mut temp_handles = Vec::new();
for mut entity in copies {
apply_insert_transform(&mut entity, &session);
@@ -254,7 +381,6 @@ impl OpenCADStudio {
entity.common_mut().owner_handle = Handle::NULL;
temp_handles.push(self.tabs[i].scene.add_entity(entity));
}
let mut session = session;
session.temp_handles = temp_handles.clone();
self.tabs[i].refedit_session = Some(session);
self.tabs[i].refedit_closed.clear();
@@ -262,7 +388,7 @@ impl OpenCADStudio {
self.tabs[i]
.scene
.set_refedit_keep(Some(temp_handles.iter().copied().collect::<rustc_hash::FxHashSet<_>>()));
self.tabs[i].scene.refedit_lock = true;
self.tabs[i].scene.refedit_lock = lock;
self.tabs[i].scene.deselect_all();
self.tabs[i].dirty = true;
// No active command — the definition's objects are edited freely
@@ -370,6 +496,10 @@ impl OpenCADStudio {
.iter()
.filter_map(|h| self.tabs[i].scene.document.get_entity(*h).cloned())
.map(|mut entity| {
// Back to the reference's own dependent names.
rename_symbols(&mut entity, &|kind, name| {
session.names.original(kind, name).map(str::to_string)
});
apply_insert_inverse_transform(&mut entity, &session);
let mut entity = normalize_entity_for_block(entity);
entity.common_mut().handle = Handle::NULL;
@@ -452,6 +582,7 @@ impl OpenCADStudio {
}
fn finish_refedit_close(&mut self, i: usize, session: RefEditSession) {
self.drop_refedit_names(i, &session.names);
self.tabs[i].refedit_closed.push(session);
self.tabs[i].scene.refedit_lock = false;
self.tabs[i].scene.set_refedit_keep(None);
@@ -581,8 +712,8 @@ impl OpenCADStudio {
.map(|e| e.common().handle)
.filter(|h| session.in_working_set(*h))
.collect();
self.tabs[i].scene.refedit_lock = session.lock;
self.tabs[i].refedit_session = Some(session);
self.tabs[i].scene.refedit_lock = true;
self.tabs[i].scene.set_refedit_keep(Some(keep));
}
}
@@ -600,4 +731,185 @@ impl OpenCADStudio {
_ => {}
}
}
/// The working set's objects of an external reference under host names:
/// `XREF|NAME` becomes `$N$NAME` (the first free N) with records of its
/// own when `unique`, else `NAME`, sharing a host record of that name when
/// one exists. Missing records are copied from the reference's.
fn refedit_host_names(
&mut self,
i: usize,
mut copies: Vec<EntityType>,
prefix: &str,
unique: bool,
) -> (Vec<EntityType>, RefEditNames) {
let document = &mut self.tabs[i].scene.document;
let deps = crate::app::ClipboardDeps::capture(document, &copies);
let strip = |name: &str| -> Option<String> {
(name.len() > prefix.len()
&& name.is_char_boundary(prefix.len())
&& name[..prefix.len()].eq_ignore_ascii_case(prefix))
.then(|| name[prefix.len()..].to_string())
};
let exists = |document: &codec::CadDocument, kind: SymbolKind, name: &str| match kind {
SymbolKind::Layer => document.layers.contains(name),
SymbolKind::Linetype => document.line_types.contains(name),
SymbolKind::TextStyle => document.text_styles.contains(name),
SymbolKind::Block => document.block_records.get(name).is_some(),
};
let wanted: Vec<(SymbolKind, String)> = deps
.layers
.iter()
.map(|r| (SymbolKind::Layer, r.name.clone()))
.chain(deps.linetypes.iter().map(|r| (SymbolKind::Linetype, r.name.clone())))
.chain(deps.text_styles.iter().map(|r| (SymbolKind::TextStyle, r.name.clone())))
.chain(deps.blocks.iter().map(|b| (SymbolKind::Block, b.name.clone())))
.collect();
let mut names = RefEditNames::default();
for (kind, original) in wanted {
let Some(base) = strip(&original) else {
continue;
};
let taken = |names: &RefEditNames, candidate: &str| {
exists(document, kind, candidate)
|| names
.renamed
.iter()
.any(|(k, used, _)| *k == kind && used.eq_ignore_ascii_case(candidate))
};
// Layer 0 is the host's own in every drawing.
let used = if kind == SymbolKind::Layer && base == "0" {
base
} else if unique {
(0u32..)
.map(|n| format!("${n}${base}"))
.find(|candidate| !taken(&names, candidate))
.expect("a free name")
} else {
base
};
if !exists(document, kind, &used)
&& !names
.created
.iter()
.any(|(k, n)| *k == kind && n.eq_ignore_ascii_case(&used))
{
names.created.push((kind, used.clone()));
}
names.renamed.push((kind, used, original));
}
let used_for = |kind: SymbolKind, name: &str| {
names
.renamed
.iter()
.find(|(k, _, original)| *k == kind && original.eq_ignore_ascii_case(name))
.map(|(_, used, _)| used.clone())
};
let created = |kind: SymbolKind, name: &str| -> Option<String> {
used_for(kind, name).filter(|used| {
names
.created
.iter()
.any(|(k, n)| *k == kind && n.eq_ignore_ascii_case(used))
})
};
for record in &deps.layers {
if let Some(used) = created(SymbolKind::Layer, &record.name) {
let mut copy = record.clone();
copy.name = used;
copy.flags.xref_dependent = false;
copy.xref_block_record_handle = Handle::NULL;
if let Some(linetype) = used_for(SymbolKind::Linetype, &copy.line_type) {
copy.line_type = linetype;
}
copy.handle = document.allocate_handle();
let _ = document.layers.add(copy);
}
}
for record in &deps.linetypes {
if let Some(used) = created(SymbolKind::Linetype, &record.name) {
let mut copy = record.clone();
copy.name = used;
copy.xref_dependent = false;
copy.xref_block_record_handle = Handle::NULL;
copy.handle = document.allocate_handle();
let _ = document.line_types.add(copy);
}
}
for record in &deps.text_styles {
if let Some(used) = created(SymbolKind::TextStyle, &record.name) {
let mut copy = record.clone();
copy.name = used;
copy.xref_dependent = false;
copy.xref_block_record_handle = Handle::NULL;
copy.handle = document.allocate_handle();
let _ = document.text_styles.add(copy);
}
}
let rename = |kind: SymbolKind, name: &str| used_for(kind, name);
let blocks: Vec<crate::app::BlockDef> = deps
.blocks
.iter()
.filter_map(|def| {
let used = created(SymbolKind::Block, &def.name)?;
let mut entities = def.entities.clone();
for entity in &mut entities {
rename_symbols(entity, &rename);
}
Some(crate::app::BlockDef {
name: used,
base_point: def.base_point,
entities,
})
})
.collect();
crate::app::command_driver::clipboard::merge_block_defs(document, &blocks);
for entity in &mut copies {
rename_symbols(entity, &rename);
}
(copies, names)
}
/// Removes the host records made for a session that nothing uses any more.
fn drop_refedit_names(&mut self, i: usize, names: &RefEditNames) {
for (kind, name) in names.created.iter().rev() {
let scene = &mut self.tabs[i].scene;
let in_use = scene.document.entities().any(|entity| {
let common = entity.common();
match kind {
SymbolKind::Layer => common.layer.eq_ignore_ascii_case(name),
SymbolKind::Linetype => common.linetype.eq_ignore_ascii_case(name),
SymbolKind::TextStyle => match entity {
EntityType::Text(t) => t.style.eq_ignore_ascii_case(name),
EntityType::MText(t) => t.style.eq_ignore_ascii_case(name),
EntityType::AttributeDefinition(a) => {
a.text_style.eq_ignore_ascii_case(name)
}
_ => false,
},
SymbolKind::Block => matches!(
entity,
EntityType::Insert(insert) if insert.block_name.eq_ignore_ascii_case(name)
),
}
});
if in_use {
continue;
}
match kind {
SymbolKind::Layer => {
scene.document.layers.remove(name);
}
SymbolKind::Linetype => {
scene.document.line_types.remove(name);
}
SymbolKind::TextStyle => {
scene.document.text_styles.remove(name);
}
SymbolKind::Block => {
let _ = crate::scene::block_service::erase(scene, name);
}
}
}
}
}
+31
View File
@@ -104,6 +104,7 @@ pub(super) const NAMES: &[&str] = &[
"external_change_cancel",
"pdf_dialog_ok",
"view_background",
"refedit_dialog",
"pdf_layer_toggle",
"pc_manager_toggle",
"pc_manager",
@@ -1884,6 +1885,36 @@ impl OpenCADStudio {
})
}
"ribbon_dropdown" => Message::ToggleRibbonDropdown(string(req, "value")?.into()),
// The Edit Reference dialog, one field per call: "level=<n>" (0 =
// the picked reference), "nested=0|1" (pick the working set in the
// drawing), "attributes=0|1", "lock=0|1", "unique=0|1", "ok" or
// "cancel".
"refedit_dialog" => {
use crate::ui::window::refedit_dialog::RefEditDialogMsg as R;
if self.active_modal != Some(crate::app::ModalKind::RefEdit) {
return Err(failure("no_dialog", "The Reference Edit dialog is not open"));
}
let spec = string(req, "value")?;
let (key, value) = spec.split_once('=').unwrap_or((spec, ""));
let flag = || match value {
"1" => Ok(true),
"0" => Ok(false),
_ => Err(failure("invalid_value", spec)),
};
if key == "cancel" {
Message::CloseModal
} else {
Message::RefEditDialog(match key {
"ok" => R::Ok,
"level" => R::Level(value.parse().map_err(|_| failure("invalid_value", spec))?),
"nested" => R::Nested(flag()?),
"attributes" => R::Attributes(flag()?),
"lock" => R::Lock(flag()?),
"unique" => R::Unique(flag()?),
_ => return Err(failure("invalid_value", "Unknown Reference Edit field")),
})
}
}
// The -VIEW Background dialog, one field per call: "type=gradient",
// "color|top|middle|bottom=r,g,b", "three=1", "rotation=30",
// "image=<path>", "position=center|stretch|tile", "aspect=1", "offset=x,y" (pixels), "scale=x,y",
+9
View File
@@ -582,6 +582,8 @@ pub(super) struct OpenCADStudio {
pc_section: Option<crate::ui::window::pdf_dialogs::PcSectionState>,
/// -VIEW sEttings Background dialog.
view_background: Option<crate::ui::window::pdf_dialogs::ViewBackgroundState>,
/// The Edit Reference dialog (REFEDIT).
refedit_dialog: Option<crate::ui::window::refedit_dialog::RefEditDialogState>,
underlay_layers: Option<crate::ui::window::pdf_dialogs::UnderlayLayersState>,
pdf_import_settings: Option<crate::modules::insert::pdf_import::PdfImportSettings>,
pdf_import_file: Option<crate::ui::window::pdf_dialogs::PdfImportFileState>,
@@ -790,6 +792,8 @@ pub(super) struct OpenCADStudio {
block_freq: std::collections::HashMap<String, u32>,
/// BLOCKMRULIST: how many recent blocks the palette keeps (0–100).
pub(crate) block_mru_list: u8,
/// The Edit Reference dialog's remembered settings.
pub(crate) refedit_options: crate::app::settings::RefEditOptions,
/// BLOCKREDEFINEMODE (0–2).
pub(crate) block_redefine_mode: u8,
/// BLOCKNAVIGATE: the Libraries tab's start folder ("." for none).
@@ -2153,6 +2157,7 @@ pub enum ModalKind {
PointCloudColorMap,
PcSection,
ViewBackground,
RefEdit,
UnderlayLayers,
PdfImportSettings,
PdfImportFile,
@@ -4348,6 +4353,8 @@ pub enum Message {
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
AttdefDialog(crate::ui::window::attdef_dialog::AttdefDialogMsg),
FieldDialog(crate::ui::window::field_dialog::FieldDialogMsg),
/// An edit in the Edit Reference dialog.
RefEditDialog(crate::ui::window::refedit_dialog::RefEditDialogMsg),
/// A click or search in the Point Cloud Manager.
PcManager(crate::ui::window::pc_manager::PcManagerMsg),
Count(crate::ui::window::count_palette::CountMsg),
@@ -4559,6 +4566,7 @@ impl OpenCADStudio {
point_cloud_color_map: None,
pc_section: None,
view_background: None,
refedit_dialog: None,
underlay_layers: None,
pdf_import_settings: None,
pdf_import_file: None,
@@ -4658,6 +4666,7 @@ impl OpenCADStudio {
block_mru: Vec::new(),
block_freq: std::collections::HashMap::new(),
block_mru_list: 50,
refedit_options: Default::default(),
block_redefine_mode: 1,
block_navigate: ".".to_string(),
insname: String::new(),
+28
View File
@@ -652,6 +652,9 @@ pub struct UserSettings {
/// BLOCKNAVIGATE (default ".").
#[serde(default = "default_block_navigate")]
pub block_navigate: String,
/// The Edit Reference dialog's settings.
#[serde(default)]
pub refedit: RefEditOptions,
/// XFADECTL: how far objects outside the REFEDIT working set fade (0–90, default 70).
#[serde(default = "default_xfadectl")]
pub xfadectl: u8,
@@ -687,6 +690,30 @@ pub struct PaletteBlockRef {
pub drawing: bool,
}
/// The Edit Reference dialog's settings, remembered between sessions: give
/// an external reference's clashing named objects unique names, check out
/// attribute definitions, lock what is outside the working set, pick the
/// working set's objects in the drawing.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(default)]
pub struct RefEditOptions {
pub unique_names: bool,
pub attributes: bool,
pub lock: bool,
pub nested: bool,
}
impl Default for RefEditOptions {
fn default() -> Self {
Self {
unique_names: true,
attributes: false,
lock: true,
nested: false,
}
}
}
/// The Blocks palette's insertion options.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BlockInsertOptions {
@@ -938,6 +965,7 @@ impl Default for UserSettings {
block_mru_list: 50,
block_redefine_mode: 1,
block_navigate: ".".to_string(),
refedit: RefEditOptions::default(),
xfadectl: 70,
count_color: 3,
count_error_color: 1,
+2
View File
@@ -873,6 +873,7 @@ impl OpenCADStudio {
block_mru_list: self.block_mru_list,
block_redefine_mode: self.block_redefine_mode,
block_navigate: self.block_navigate.clone(),
refedit: self.refedit_options.clone(),
xfadectl: crate::scene::cache::block_cache::refedit_fade_ctl() as u8,
count_color: self.count_palette.color,
count_error_color: self.count_palette.error_color,
@@ -1036,6 +1037,7 @@ impl OpenCADStudio {
self.block_redefine_mode = s.block_redefine_mode.min(2);
self.block_navigate = s.block_navigate.clone();
crate::scene::cache::block_cache::set_refedit_fade_ctl(i32::from(s.xfadectl.min(90)));
self.refedit_options = s.refedit.clone();
self.count_palette.color = s.count_color;
self.count_palette.error_color = s.count_error_color;
self.count_palette.service = s.count_service;
+4
View File
@@ -2274,6 +2274,7 @@ impl OpenCADStudio {
Message::PdfDialog(message) => self.update_pdf_dialog(message),
Message::AttdefDialog(message) => self.on_attdef_dialog(message),
Message::FieldDialog(message) => self.on_field_dialog(message),
Message::RefEditDialog(message) => self.update_refedit_dialog(message),
Message::PcManager(message) => self.update_pc_manager(message),
Message::Count(message) => self.on_count(message),
Message::SheetSet(message) => self.on_sheet_set(message),
@@ -9487,6 +9488,9 @@ impl OpenCADStudio {
Task::none()
};
}
if self.active_modal == Some(super::ModalKind::RefEdit) {
self.refedit_dialog = None;
}
// Cancelling the section settings drops the waiting extraction.
if self.active_modal == Some(super::ModalKind::PcSection) {
crate::modules::insert::pc_extract::drop_job();
+7
View File
@@ -40,6 +40,7 @@ impl OpenCADStudio {
crate::tr!("view-background", "title"),
self.view_background.as_ref().map_or("", |s| s.view.as_str())
),
Some(K::RefEdit) => crate::tr!("ref-edit", "dialog-title"),
Some(K::UnderlayLayers) => crate::t!("Underlay Layers").into_owned(),
Some(K::PdfImportSettings) => crate::t!("PDF Import Settings").into_owned(),
Some(K::PdfImportFile) => crate::t!("Import PDF").into_owned(),
@@ -528,6 +529,12 @@ impl OpenCADStudio {
crate::ui::window::pdf_dialogs::view_background(state, flow)
})
}
super::super::ModalKind::RefEdit => {
let state = self.refedit_dialog.as_ref()?;
sized_flow(ex, 560, 470, |flow| {
crate::ui::window::refedit_dialog::view(state, flow)
})
}
super::super::ModalKind::UnderlayLayers => {
let state = self.underlay_layers.as_ref()?;
sized_flow(ex, 460, 520, |flow| {
+115 -3
View File
@@ -75,6 +75,79 @@ pub struct RefEditSession {
pub handle_watermark: u64,
/// The referenced file when the definition is an xref.
pub xref_path: Option<String>,
/// Objects outside the working set can't be selected while it is open.
pub lock: bool,
/// The external reference's named objects the working set uses under
/// host names, and the host records made for the session.
pub names: RefEditNames,
}
/// A kind of named object the working set's objects refer to.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolKind {
Layer,
Linetype,
TextStyle,
Block,
}
/// How an external reference's dependent names (`XREF|NAME`) are used in
/// the host while its objects are checked out: as `$0$NAME` records of
/// their own (unique names), or under the plain name, sharing a host record
/// of that name when there is one.
#[derive(Debug, Clone, Default)]
pub struct RefEditNames {
/// (kind, name used in the host, the reference's dependent name).
pub renamed: Vec<(SymbolKind, String, String)>,
/// Records added to the host for the session, removed when it closes.
pub created: Vec<(SymbolKind, String)>,
}
impl RefEditNames {
/// The reference's own name for a host name used in the session.
pub fn original(&self, kind: SymbolKind, name: &str) -> Option<&str> {
self.renamed
.iter()
.find(|(k, used, _)| *k == kind && used.eq_ignore_ascii_case(name))
.map(|(_, _, original)| original.as_str())
}
}
/// Rewrites every named-object reference of an entity through `map`
/// (layer, linetype, text style, block).
pub fn rename_symbols(entity: &mut EntityType, map: &dyn Fn(SymbolKind, &str) -> Option<String>) {
let common = entity.common_mut();
if let Some(name) = map(SymbolKind::Layer, &common.layer) {
common.layer = name;
common.layer_handle = None;
}
if let Some(name) = map(SymbolKind::Linetype, &common.linetype) {
common.linetype = name;
common.linetype_handle = None;
}
match entity {
EntityType::Insert(insert) => {
if let Some(name) = map(SymbolKind::Block, &insert.block_name) {
insert.block_name = name;
}
}
EntityType::Text(text) => {
if let Some(name) = map(SymbolKind::TextStyle, &text.style) {
text.style = name;
}
}
EntityType::MText(text) => {
if let Some(name) = map(SymbolKind::TextStyle, &text.style) {
text.style = name;
}
}
EntityType::AttributeDefinition(attdef) => {
if let Some(name) = map(SymbolKind::TextStyle, &attdef.text_style) {
attdef.text_style = name;
}
}
_ => {}
}
}
impl RefEditSession {
@@ -315,10 +388,23 @@ pub struct RefEditOptionsCommand {
level: usize,
step: OptionStep,
nested: Vec<Handle>,
/// The session settings (lock objects outside the working set, unique
/// names for an external reference's named objects).
lock: bool,
unique: bool,
/// Set when the Edit Reference dialog already chose the attribute
/// option: the nested pick then opens the session directly.
attributes: Option<bool>,
}
impl RefEditOptionsCommand {
pub fn new(top: Handle, chain: Vec<ChainLevel>, hits: Vec<LevelHits>) -> Self {
pub fn new(
top: Handle,
chain: Vec<ChainLevel>,
hits: Vec<LevelHits>,
lock: bool,
unique: bool,
) -> Self {
let level = chain.len().saturating_sub(1);
Self {
top,
@@ -327,9 +413,30 @@ impl RefEditOptionsCommand {
level,
step: OptionStep::Nesting,
nested: Vec::new(),
lock,
unique,
attributes: None,
}
}
/// `Select nested objects:` for the dialog's "pick in the drawing"
/// working set at `level`.
pub fn nested_pick(
top: Handle,
chain: Vec<ChainLevel>,
hits: Vec<LevelHits>,
level: usize,
attributes: bool,
lock: bool,
unique: bool,
) -> Self {
let mut command = Self::new(top, chain, hits, lock, unique);
command.level = level.min(command.chain.len().saturating_sub(1));
command.step = OptionStep::Nested;
command.attributes = Some(attributes);
command
}
fn begin(&self, attributes: bool) -> CmdResult {
let path: Vec<String> = self.chain[1..=self.level]
.iter()
@@ -338,11 +445,13 @@ impl RefEditOptionsCommand {
let nested: Vec<String> = self.nested.iter().map(|h| h.value().to_string()).collect();
CmdResult::Relaunch(
format!(
"REFEDIT_BEGIN:{}:{}:{}:{}",
"REFEDIT_BEGIN:{}:{}:{}:{}:{}:{}",
self.top.value(),
path.join(","),
attributes as u8,
nested.join(",")
nested.join(","),
self.lock as u8,
self.unique as u8
),
vec![],
)
@@ -450,6 +559,9 @@ impl CadCommand for RefEditOptionsCommand {
if n == 0 {
return CmdResult::CancelWithMessage(t!("0 items selected").into_owned());
}
if let Some(attributes) = self.attributes {
return self.begin(attributes);
}
self.step = OptionStep::Attributes;
CmdResult::ReportMeasurement(if n == 1 {
t!("1 item selected").into_owned()
+1
View File
@@ -29,6 +29,7 @@ pub mod plot;
pub mod plugin_manager;
pub mod print_all;
pub mod recovery;
pub mod refedit_dialog;
pub mod sheet_set;
pub mod shortcuts;
pub mod update_notice;
+1 -1
View File
@@ -302,7 +302,7 @@ pub(crate) fn well_style(theme: &Theme) -> container::Style {
}
/// A titled card.
fn card<'a>(title: String, content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
pub(crate) fn card<'a>(title: String, content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
container(
column![
text(title.to_uppercase()).size(10).style(accent_text),
+247
View File
@@ -0,0 +1,247 @@
//! Edit Reference dialog (REFEDIT): the nesting level to check out, shown as
//! a chain of chips beside a preview of the chosen definition, how the
//! working set is filled, whether attribute definitions come along, and the
//! session settings (lock what is outside the working set, give clashing
//! named objects of an external reference unique names).
use iced::widget::{button, column, container, row, text, toggler, Space};
use iced::{Border, Element, Fill, Length, Theme};
use crate::app::Message;
use crate::scene::model::wire_model::WireModel;
use crate::ui::style::common::muted_style;
use crate::ui::style::form::{button_style, dialog_button};
use crate::ui::window::pdf_dialogs::{card, well_style};
/// One edit in the Edit Reference dialog.
#[derive(Debug, Clone)]
pub enum RefEditDialogMsg {
/// The nesting level picked in the chain (0 = the selected reference).
Level(usize),
/// Pick the working set's objects in the drawing (else all of them).
Nested(bool),
Attributes(bool),
Lock(bool),
Unique(bool),
Help,
Ok,
}
fn msg(m: RefEditDialogMsg) -> Message {
Message::RefEditDialog(m)
}
/// One level of the nesting chain as the dialog shows it.
pub struct RefEditLevel {
/// The reference's handle: the picked object for level 0, the
/// definition-owned reference below it.
pub insert: codec::Handle,
pub name: String,
/// The definition's preview.
pub wires: Vec<WireModel>,
/// The definition holds attribute definitions (the attribute switch is
/// only offered then).
pub has_attdefs: bool,
/// The referenced file of an external reference.
pub xref_path: Option<String>,
}
pub struct RefEditDialogState {
pub top: codec::Handle,
/// The picked chain, outermost first.
pub chain: Vec<crate::modules::draw::modify::refedit::ChainLevel>,
pub levels: Vec<RefEditLevel>,
pub level: usize,
pub nested: bool,
pub attributes: bool,
pub lock: bool,
pub unique: bool,
}
impl RefEditDialogState {
/// The attribute switch counts only where the chosen definition has
/// attribute definitions.
pub fn attributes_enabled(&self) -> bool {
self.levels.get(self.level).is_some_and(|l| l.has_attdefs)
}
}
fn label<'a>(value: String, enabled: bool) -> Element<'a, Message> {
text(value)
.size(12)
.style(move |theme: &Theme| {
if enabled {
text::Style::default()
} else {
text::Style {
color: Some(theme.palette().background.strong.color),
}
}
})
.width(Fill)
.into()
}
/// A labelled switch on one row; inactive while `on_toggle` is None.
fn switch_row<'a>(
name: String,
value: bool,
on_toggle: Option<fn(bool) -> RefEditDialogMsg>,
) -> Element<'a, Message> {
let enabled = on_toggle.is_some();
let mut switch = toggler(value).size(18.0);
if let Some(on) = on_toggle {
switch = switch.on_toggle(move |v| msg(on(v)));
}
row![label(name, enabled), switch]
.spacing(8)
.align_y(iced::Center)
.into()
}
/// A level of the chain as a pill; the chosen one framed in the accent colour.
fn level_chip<'a>(name: &str, index: usize, chosen: bool) -> Element<'a, Message> {
button(text(name.to_string()).size(12))
.on_press(msg(RefEditDialogMsg::Level(index)))
.padding([5, 12])
.style(move |theme: &Theme, status| {
let mut style = button_style(false)(theme, status);
style.border = Border {
width: if chosen { 1.5 } else { 1.0 },
radius: 14.0.into(),
color: if chosen {
theme.palette().primary.base.color
} else {
theme.palette().background.strong.color
},
};
if chosen {
style.text_color = theme.palette().primary.base.color;
}
style
})
.into()
}
pub fn view<'a>(
state: &'a RefEditDialogState,
sizing: crate::ui::modal::ModalSizing,
) -> Element<'a, Message> {
let current = state.levels.get(state.level);
let preview: Element<'a, Message> = container(match current {
Some(level) => crate::ui::window::block_palette::preview_canvas(&level.wires, 104.0),
None => Space::new().into(),
})
.width(Length::Fixed(150.0))
.height(Length::Fixed(104.0))
.padding(4)
.style(well_style)
.into();
let mut chips = row![].spacing(6).align_y(iced::Center);
for (index, level) in state.levels.iter().enumerate() {
if index > 0 {
chips = chips.push(text("›").size(13).style(muted_style));
}
chips = chips.push(level_chip(&level.name, index, index == state.level));
}
let mut about = column![text(crate::tr!("ref-edit", "dialog-chain-hint"))
.size(11)
.style(muted_style)]
.spacing(4);
if let Some(path) = current.and_then(|l| l.xref_path.as_deref()) {
// A long path keeps its end, where the file name is.
const MAX: usize = 64;
let count = path.chars().count();
let shown = if count > MAX {
format!("…{}", path.chars().skip(count - (MAX - 1)).collect::<String>())
} else {
path.to_string()
};
about = about.push(
text(format!("{} {}", crate::tr!("ref-edit", "dialog-path"), shown))
.size(11)
.style(muted_style),
);
}
let reference = card(
crate::tr!("ref-edit", "dialog-reference"),
row![
preview,
column![iced::widget::scrollable(chips).direction(
iced::widget::scrollable::Direction::Horizontal(
iced::widget::scrollable::Scrollbar::new().width(4).scroller_width(4)
)
), about]
.spacing(8)
.width(Fill)
]
.spacing(12),
);
let segment = |nested: bool, name: String| {
button(text(name).size(12).width(Fill).align_x(iced::Center))
.on_press(msg(RefEditDialogMsg::Nested(nested)))
.style(button_style(state.nested == nested))
.padding([6, 8])
.width(Fill)
};
let method = container(
row![
segment(false, crate::tr!("ref-edit", "dialog-all-objects")),
segment(true, crate::tr!("ref-edit", "dialog-pick-objects")),
]
.spacing(2),
)
.padding(2)
.width(Fill)
.style(well_style);
let attributes_enabled = state.attributes_enabled();
let working_set = card(
crate::tr!("ref-edit", "dialog-working-set"),
column![
method,
switch_row(
crate::tr!("ref-edit", "dialog-attributes"),
state.attributes && attributes_enabled,
attributes_enabled.then_some(RefEditDialogMsg::Attributes as fn(bool) -> _),
),
]
.spacing(10),
);
let session = card(
crate::tr!("ref-edit", "dialog-session"),
column![
switch_row(
crate::tr!("ref-edit", "dialog-lock"),
state.lock,
Some(RefEditDialogMsg::Lock),
),
switch_row(
crate::tr!("ref-edit", "dialog-unique"),
state.unique,
Some(RefEditDialogMsg::Unique),
),
]
.spacing(10),
);
let footer = row![
button(text("?").size(12))
.on_press(msg(RefEditDialogMsg::Help))
.style(button_style(false))
.padding([5, 11]),
Space::new().width(Fill),
dialog_button(crate::t!("Cancel"), Message::CloseModal, false),
dialog_button(
std::borrow::Cow::Owned(crate::tr!("ref-edit", "dialog-start")),
msg(RefEditDialogMsg::Ok),
true
),
]
.spacing(6)
.align_y(iced::Center);
column![reference, working_set, session, footer]
.spacing(10)
.padding([10, 12])
.width(sizing.width)
.into()
}