fix(layers): keep rename state consistent
Keep table keys, entity references, dependency caches, and current layer handles aligned across case-only and mixed-case renames. Refs #764 Refs #765
This commit is contained in:
parent
9c6adedc44
commit
37325f4d63
1 changed files with 135 additions and 30 deletions
|
|
@ -1103,7 +1103,8 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||||
.get(idx)
|
.get(idx)
|
||||||
.map(|l| l.name.clone())
|
.map(|l| l.name.clone())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
let case_only_rename = new_name.eq_ignore_ascii_case(&old_name);
|
let old_key = old_name.to_uppercase();
|
||||||
|
let case_only_rename = new_name.to_uppercase() == old_key;
|
||||||
if !new_name.is_empty()
|
if !new_name.is_empty()
|
||||||
&& new_name != old_name
|
&& new_name != old_name
|
||||||
&& (case_only_rename
|
&& (case_only_rename
|
||||||
|
|
@ -1121,19 +1122,30 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||||
if !nl.handle.is_valid() {
|
if !nl.handle.is_valid() {
|
||||||
nl.handle = self.tabs[i].scene.document.allocate_handle();
|
nl.handle = self.tabs[i].scene.document.allocate_handle();
|
||||||
}
|
}
|
||||||
|
let layer_handle = nl.handle;
|
||||||
let _ = self.tabs[i].scene.document.layers.add(nl);
|
let _ = self.tabs[i].scene.document.layers.add(nl);
|
||||||
for e in self.tabs[i].scene.document.entities_mut() {
|
for e in self.tabs[i].scene.document.entities_mut() {
|
||||||
if e.as_entity().layer() == old_name {
|
if e.as_entity().layer().to_uppercase() == old_key {
|
||||||
e.as_entity_mut().set_layer(new_name.clone());
|
e.as_entity_mut().set_layer(new_name.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.tabs[i]
|
self.tabs[i].scene.invalidate_dependency_index();
|
||||||
.active_layer
|
|
||||||
.eq_ignore_ascii_case(&old_name)
|
if self.tabs[i].active_layer.to_uppercase() == old_key {
|
||||||
{
|
|
||||||
self.tabs[i].active_layer = new_name.clone();
|
self.tabs[i].active_layer = new_name.clone();
|
||||||
|
}
|
||||||
|
if self.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.header
|
||||||
|
.current_layer_name
|
||||||
|
.to_uppercase()
|
||||||
|
== old_key
|
||||||
|
{
|
||||||
self.tabs[i].scene.document.header.current_layer_name =
|
self.tabs[i].scene.document.header.current_layer_name =
|
||||||
new_name.clone();
|
new_name.clone();
|
||||||
|
self.tabs[i].scene.document.header.current_layer_handle =
|
||||||
|
layer_handle;
|
||||||
}
|
}
|
||||||
self.tabs[i].dirty = true;
|
self.tabs[i].dirty = true;
|
||||||
}
|
}
|
||||||
|
|
@ -2617,6 +2629,8 @@ fn block_attr_prompts(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod layer_rename_tests {
|
mod layer_rename_tests {
|
||||||
use crate::app::OpenCADStudio;
|
use crate::app::OpenCADStudio;
|
||||||
|
use acadrust::entities::Line;
|
||||||
|
use acadrust::{EntityType, Handle};
|
||||||
|
|
||||||
fn app_with_editing_layer() -> OpenCADStudio {
|
fn app_with_editing_layer() -> OpenCADStudio {
|
||||||
let mut app = OpenCADStudio::new_for_test();
|
let mut app = OpenCADStudio::new_for_test();
|
||||||
|
|
@ -2625,52 +2639,143 @@ mod layer_rename_tests {
|
||||||
app
|
app
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
fn rename_layer(app: &mut OpenCADStudio, old_name: &str, new_name: &str) {
|
||||||
fn current_layer_rename_updates_active_layer() {
|
|
||||||
let mut app = app_with_editing_layer();
|
|
||||||
let i = app.active_tab;
|
let i = app.active_tab;
|
||||||
let idx = app.tabs[i]
|
|
||||||
.layers
|
|
||||||
.editing
|
|
||||||
.expect("new layer is being edited");
|
|
||||||
app.tabs[i].layers.selected = Some(idx);
|
|
||||||
let _ = app.on_layer_set_current();
|
|
||||||
|
|
||||||
app.tabs[i].layers.editing = Some(idx);
|
|
||||||
app.tabs[i].layers.edit_buf = "Renamed".to_string();
|
|
||||||
let _ = app.on_layer_rename_commit();
|
|
||||||
|
|
||||||
assert_eq!(app.tabs[i].active_layer, "Renamed");
|
|
||||||
assert_eq!(
|
|
||||||
app.tabs[i].scene.document.header.current_layer_name,
|
|
||||||
"Renamed"
|
|
||||||
);
|
|
||||||
assert_eq!(app.tabs[i].layers.current_layer, "Renamed");
|
|
||||||
assert_eq!(app.ribbon.active_layer, "Renamed");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn layer_rename_allows_case_only_change() {
|
|
||||||
let mut app = app_with_editing_layer();
|
|
||||||
let i = app.active_tab;
|
|
||||||
assert!(app.tabs[i].layers.editing.is_some());
|
|
||||||
app.tabs[i].layers.edit_buf = "TEST".to_string();
|
|
||||||
let _ = app.on_layer_rename_commit();
|
|
||||||
|
|
||||||
let idx = app.tabs[i]
|
let idx = app.tabs[i]
|
||||||
.layers
|
.layers
|
||||||
.layers
|
.layers
|
||||||
.iter()
|
.iter()
|
||||||
.position(|layer| layer.name == "TEST")
|
.position(|layer| layer.name == old_name)
|
||||||
.expect("first rename succeeded");
|
.expect("layer exists in panel");
|
||||||
app.tabs[i].layers.editing = Some(idx);
|
app.tabs[i].layers.editing = Some(idx);
|
||||||
app.tabs[i].layers.edit_buf = "test".to_string();
|
app.tabs[i].layers.edit_buf = new_name.to_string();
|
||||||
let _ = app.on_layer_rename_commit();
|
let _ = app.on_layer_rename_commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layer_rename_allows_ascii_case_only_change() {
|
||||||
|
let mut app = app_with_editing_layer();
|
||||||
|
let i = app.active_tab;
|
||||||
|
let old_name = app.tabs[i].layers.edit_buf.clone();
|
||||||
|
rename_layer(&mut app, &old_name, "TEST");
|
||||||
|
let handle = app.tabs[i].scene.document.layers.get("TEST").unwrap().handle;
|
||||||
|
|
||||||
|
rename_layer(&mut app, "TEST", "test");
|
||||||
|
|
||||||
|
let layer = app.tabs[i].scene.document.layers.get("test").unwrap();
|
||||||
|
assert_eq!(layer.name, "test");
|
||||||
|
assert_eq!(layer.handle, handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layer_rename_allows_unicode_case_only_change() {
|
||||||
|
let mut app = app_with_editing_layer();
|
||||||
|
let i = app.active_tab;
|
||||||
|
let old_name = app.tabs[i].layers.edit_buf.clone();
|
||||||
|
rename_layer(&mut app, &old_name, "é");
|
||||||
|
|
||||||
|
rename_layer(&mut app, "é", "É");
|
||||||
|
|
||||||
assert!(app.tabs[i].scene.document.layers.get("test").is_some());
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
app.tabs[i].scene.document.layers.get("test").unwrap().name,
|
app.tabs[i].scene.document.layers.get("É").unwrap().name,
|
||||||
"test"
|
"É"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn current_layer_rename_updates_name_and_allocated_handle() {
|
||||||
|
let mut app = app_with_editing_layer();
|
||||||
|
let i = app.active_tab;
|
||||||
|
let idx = app.tabs[i].layers.editing.expect("new layer is being edited");
|
||||||
|
let old_name = app.tabs[i].layers.layers[idx].name.clone();
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.layers
|
||||||
|
.get_mut(&old_name)
|
||||||
|
.unwrap()
|
||||||
|
.handle = Handle::NULL;
|
||||||
|
app.tabs[i].layers.selected = Some(idx);
|
||||||
|
let _ = app.on_layer_set_current();
|
||||||
|
|
||||||
|
rename_layer(&mut app, &old_name, "Renamed");
|
||||||
|
|
||||||
|
let layer = app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.layers
|
||||||
|
.get("Renamed")
|
||||||
|
.unwrap();
|
||||||
|
assert!(layer.handle.is_valid());
|
||||||
|
assert_eq!(app.tabs[i].active_layer, "Renamed");
|
||||||
|
assert_eq!(app.tabs[i].layers.current_layer, "Renamed");
|
||||||
|
assert_eq!(app.ribbon.active_layer, "Renamed");
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i].scene.document.header.current_layer_name,
|
||||||
|
"Renamed"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i].scene.document.header.current_layer_handle,
|
||||||
|
layer.handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layer_rename_keeps_independent_current_layer_mirrors() {
|
||||||
|
let mut app = app_with_editing_layer();
|
||||||
|
let i = app.active_tab;
|
||||||
|
let old_name = app.tabs[i].layers.edit_buf.clone();
|
||||||
|
rename_layer(&mut app, &old_name, "A");
|
||||||
|
let _ = app.on_layer_new();
|
||||||
|
let b_idx = app.tabs[i].layers.editing.expect("new layer is being edited");
|
||||||
|
app.tabs[i].layers.selected = Some(b_idx);
|
||||||
|
let _ = app.on_layer_set_current();
|
||||||
|
let header_name = app.tabs[i].scene.document.header.current_layer_name.clone();
|
||||||
|
let header_handle = app.tabs[i].scene.document.header.current_layer_handle;
|
||||||
|
app.tabs[i].active_layer = "A".to_string();
|
||||||
|
|
||||||
|
rename_layer(&mut app, "A", "Renamed");
|
||||||
|
|
||||||
|
assert_eq!(app.tabs[i].active_layer, "Renamed");
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i].scene.document.header.current_layer_name,
|
||||||
|
header_name
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i].scene.document.header.current_layer_handle,
|
||||||
|
header_handle
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn layer_rename_updates_mixed_case_entity_references() {
|
||||||
|
let mut app = app_with_editing_layer();
|
||||||
|
let i = app.active_tab;
|
||||||
|
let old_name = app.tabs[i].layers.edit_buf.clone();
|
||||||
|
rename_layer(&mut app, &old_name, "TEST");
|
||||||
|
let mut line = Line::new();
|
||||||
|
line.common.layer = "test".to_string();
|
||||||
|
let handle = app.tabs[i].scene.add_entity(EntityType::Line(line));
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.invalidate_layer_dependencies(&["TEST".to_string()]);
|
||||||
|
|
||||||
|
rename_layer(&mut app, "TEST", "Renamed");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.document
|
||||||
|
.get_entity(handle)
|
||||||
|
.unwrap()
|
||||||
|
.common()
|
||||||
|
.layer,
|
||||||
|
"Renamed"
|
||||||
|
);
|
||||||
|
let epoch = app.tabs[i].scene.geometry_epoch;
|
||||||
|
app.tabs[i]
|
||||||
|
.scene
|
||||||
|
.invalidate_layer_dependencies(&["Renamed".to_string()]);
|
||||||
|
assert_ne!(app.tabs[i].scene.geometry_epoch, epoch);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue