fix(xref): carry stable row identity in row messages

XrefManagerSelect/RightClick/RowOp/PathPick/FindReplace carried positional display indices that rescans could retarget. New XrefRowId{key,saved_path} (the identity refresh uses) in all 5 variants, resolved via row_index at dispatch with graceful no-op; panel selection stays index-based. display.index remains for view-time styling/lookup only. Tests: xref_rowop_unknown_id_is_noop (RED first); existing dialog/simulator/click tests migrated to ids. Full suite: 2044 passed / 0 failed / 25 ignored.
This commit is contained in:
Karim Jerbi committed 2026-10-11 02:17:18 +01:00
1 parent 7bbc5b0319
commit 6fa11887c7
4 files changed
+185 -89

No files matched your search

+43 -36
View File
@@ -2613,7 +2613,7 @@ impl OpenCADStudio {
self.refresh_xref_manager();
Task::none()
}
Message::XrefManagerSelect(index) => {
Message::XrefManagerSelect(id) => {
use crate::ui::window::xref_manager::SelectExtend;
// Normal GUI list behavior off the globally tracked
// modifiers (same source as the layer list): plain click
@@ -2625,12 +2625,12 @@ impl OpenCADStudio {
} else {
SelectExtend::Single
};
self.xref_manager.click_select(index, extend);
self.xref_manager.click_select(&id, extend);
Task::none()
}
Message::XrefRowRightClick(index) => {
Message::XrefRowRightClick(id) => {
self.xref_manager.row_change_path_open = false;
self.xref_manager.right_click_select(index);
self.xref_manager.right_click_select(&id);
Task::none()
}
Message::XrefManagerToggleTree => {
@@ -2775,62 +2775,69 @@ impl OpenCADStudio {
self.xref_manager_op(op);
Task::none()
}
Message::XrefRowOp(index, op) => {
Message::XrefRowOp(id, op) => {
// Open is not a palette op — it navigates to the file.
if op == crate::ui::window::xref_manager::XrefPaletteOp::Open {
self.xref_manager.right_click_select(index);
self.xref_manager.right_click_select(&id);
self.xref_manager.row_change_path_open = false;
if let Some(entry) = self.xref_manager.entries.get(index) {
if let Some(found) = entry.found_at.clone() {
let is_dwg = found.to_ascii_lowercase().ends_with(".dwg")
|| found.to_ascii_lowercase().ends_with(".dxf");
self.command_line.push_output(crate::tf!("XOPEN: opening \"{}\".", found).as_ref());
if is_dwg {
return self.update(Message::OpenRecent(std::path::PathBuf::from(found)));
if let Some(index) = self.xref_manager.row_index(&id) {
if let Some(entry) = self.xref_manager.entries.get(index) {
if let Some(found) = entry.found_at.clone() {
let is_dwg = found.to_ascii_lowercase().ends_with(".dwg")
|| found.to_ascii_lowercase().ends_with(".dxf");
self.command_line.push_output(crate::tf!("XOPEN: opening \"{}\".", found).as_ref());
if is_dwg {
return self.update(Message::OpenRecent(std::path::PathBuf::from(found)));
} else {
#[cfg(not(target_arch = "wasm32"))]
let _ = open::that_detached(&found);
}
} else {
#[cfg(not(target_arch = "wasm32"))]
let _ = open::that_detached(&found);
self.command_line.push_error(
crate::t!("File not found — check the saved path.").as_ref(),
);
}
} else {
self.command_line.push_error(
crate::t!("File not found — check the saved path.").as_ref(),
);
}
}
return Task::none();
}
// Attach prompts the file dialog to pick a drawing to attach.
if matches!(op, crate::ui::window::xref_manager::XrefPaletteOp::Attach) {
self.xref_manager.right_click_select(index);
self.xref_manager.right_click_select(&id);
self.xref_manager.row_change_path_open = false;
self.command_line.push_output(crate::t!("XATTACH").as_ref());
return self.update(Message::XAttachPick);
}
// Row-scoped op: select the row first, then run the op.
self.xref_manager.right_click_select(index);
self.xref_manager.right_click_select(&id);
self.xref_manager.row_change_path_open = false;
self.xref_manager_op(op);
Task::none()
}
Message::XrefRowPathPick(index) => {
self.xref_manager.right_click_select(index);
Message::XrefRowPathPick(id) => {
self.xref_manager.right_click_select(&id);
self.xref_manager.row_change_path_open = false;
self.update(Message::XrefPathPick)
}
Message::XrefRowFindReplacePrompt(index) => {
self.xref_manager.right_click_select(index);
Message::XrefRowFindReplacePrompt(id) => {
self.xref_manager.right_click_select(&id);
self.xref_manager.row_change_path_open = false;
let prefill = if let Some(entry) = self.xref_manager.entries.get(index) {
let saved = &entry.saved_path;
if let Some(parent) = std::path::Path::new(saved).parent().and_then(|p| p.to_str()) {
if !parent.is_empty() {
format!("XREF Path Find \"{}\" ", parent)
} else {
"XREF Path Find ".to_string()
}
} else {
"XREF Path Find ".to_string()
}
let prefill = if let Some(index) = self.xref_manager.row_index(&id) {
self.xref_manager.entries.get(index).map_or_else(
|| "XREF Path Find ".to_string(),
|entry| {
let saved = &entry.saved_path;
if let Some(parent) = std::path::Path::new(saved).parent().and_then(|p| p.to_str()) {
if !parent.is_empty() {
format!("XREF Path Find \"{}\" ", parent)
} else {
"XREF Path Find ".to_string()
}
} else {
"XREF Path Find ".to_string()
}
},
)
} else {
"XREF Path Find ".to_string()
};