diff --git a/src/app/commands.rs b/src/app/commands.rs index bfca8ec95..f7ba9e32b 100644 --- a/src/app/commands.rs +++ b/src/app/commands.rs @@ -3288,7 +3288,10 @@ impl OpenCADStudio { self.command_line .push_error(&format!("LAYER: '{}' already exists.", name)); } else { - let layer = Layer::new(&name); + let mut layer = Layer::new(&name); + // Allocate a unique handle so the layer survives a + // DWG save (handle-based format; issue #67). + layer.handle = self.tabs[i].scene.document.allocate_handle(); let _ = self.tabs[i].scene.document.layers.add(layer); self.push_undo_snapshot(i, "LAYER NEW"); self.tabs[i].dirty = true; diff --git a/src/app/update.rs b/src/app/update.rs index 96660d16c..a4f67811e 100644 --- a/src/app/update.rs +++ b/src/app/update.rs @@ -1637,11 +1637,14 @@ impl OpenCADStudio { }; self.push_undo_snapshot(i, "LAYER NEW"); use acadrust::tables::layer::Layer as DocLayer; - let _ = self.tabs[i] - .scene - .document - .layers - .add(DocLayer::new(&new_name)); + // A layer needs a real handle or it is dropped on a DWG save + // (the format is handle-based; issue #67). + let mut dl = DocLayer::new(&new_name); + // `allocate_handle` advances the seed so the layer gets a + // unique handle; the non-advancing `next_handle` getter hands + // out the same value twice and the later object overwrites it. + dl.handle = self.tabs[i].scene.document.allocate_handle(); + let _ = self.tabs[i].scene.document.layers.add(dl); self.tabs[i].dirty = true; let doc_layers = self.tabs[i].scene.document.layers.clone(); let vp_info = self.tabs[i].scene.viewport_list(); @@ -1742,11 +1745,17 @@ impl OpenCADStudio { && !self.tabs[i].scene.document.layers.contains(&new_name) { self.push_undo_snapshot(i, "LAYER RENAME"); - if let Some(old_layer) = self.tabs[i].scene.document.layers.get(&old_name) { - use acadrust::tables::layer::Layer as DocLayer; - let mut nl = DocLayer::new(&new_name); - nl.color = old_layer.color.clone(); - nl.flags = old_layer.flags.clone(); + // Keep the whole record (handle, color, linetype, + // lineweight, flags) and only change the name, so the + // renamed layer still has a valid handle and survives a + // DWG save (issue #67). + if let Some(mut nl) = + self.tabs[i].scene.document.layers.get(&old_name).cloned() + { + nl.name = new_name.clone(); + if !nl.handle.is_valid() { + nl.handle = self.tabs[i].scene.document.allocate_handle(); + } let _ = self.tabs[i].scene.document.layers.add(nl); } self.tabs[i].scene.document.layers.remove(&old_name); diff --git a/src/io/mod.rs b/src/io/mod.rs index d81ea960a..db75de0c4 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -403,3 +403,44 @@ fn fix_dxf_dimension_rotations(doc: &mut CadDocument) { } } } + +#[cfg(test)] +mod layer_roundtrip_tests { + use super::*; + use acadrust::tables::layer::Layer as DocLayer; + + // Add `count` new layers the way the UI does (allocate_handle, then add), + // round-trip through `ext`, and return whether every one survived. + fn roundtrip_layers(ext: &str, count: usize) -> bool { + let mut doc = CadDocument::new(); + crate::linetypes::populate_document(&mut doc); + let names: Vec = (0..count).map(|n| format!("Layer{}", n + 1)).collect(); + for name in &names { + let mut dl = DocLayer::new(name); + dl.handle = doc.allocate_handle(); + doc.layers.add(dl).unwrap(); + } + let path = std::env::temp_dir().join(format!("ocs_layer_rt_{count}.{ext}")); + save_as_version(&doc, &path, acadrust::DxfVersion::AC1032).expect("save"); + let loaded = load_file(&path).expect("load"); + let _ = std::fs::remove_file(&path); + names.iter().all(|n| loaded.layers.contains(n)) + } + + #[test] + fn dwg_preserves_new_layer() { + assert!(roundtrip_layers("dwg", 1), "DWG dropped the new layer (issue #67)"); + } + + #[test] + fn dxf_preserves_new_layer() { + assert!(roundtrip_layers("dxf", 1), "DXF dropped the new layer"); + } + + // Each new layer must get a distinct handle, or they collide and all but + // the last are dropped on a handle-based DWG save (issue #67). + #[test] + fn dwg_preserves_multiple_new_layers() { + assert!(roundtrip_layers("dwg", 3), "DWG dropped colliding new layers (issue #67)"); + } +}