forked from 1b-agent/opencadstudio-upstream
fix(layers): deleting a non-empty layer warns and removes its objects (#237)
Deleting a layer only dropped the layer record, leaving its objects in the
file still tagged with the (now-gone) layer name. Now:
- Empty layer → deleted straight away.
- Non-empty layer → a warning modal ("Layer X is not empty — N objects;
deleting also removes them. Continue?") with Delete Objects / Cancel.
Confirming erases every object on the layer, then removes the layer, as
one undo step. The layer record is dropped before the erase so the
locked-layer guard can't keep its objects.
- Layer "0" and the current layer can't be deleted (reported on the
command line) instead of silently leaving a broken state.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
e3d1780a69
commit
2712bb80e4
5 files changed
+146
-17
No files matched your search
+72
-17
@@ -623,30 +623,85 @@ pub(super) fn on_tab_close(&mut self, idx: usize) -> Task<Message> {
|
||||
|
||||
pub(super) fn on_layer_delete(&mut self) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
if let Some(idx) = self.tabs[i].layers.selected {
|
||||
let name = self.tabs[i]
|
||||
.layers
|
||||
.layers
|
||||
.get(idx)
|
||||
.map(|l| l.name.clone())
|
||||
.unwrap_or_default();
|
||||
if name == "0" {
|
||||
return Task::none();
|
||||
}
|
||||
let Some(idx) = self.tabs[i].layers.selected else {
|
||||
return Task::none();
|
||||
};
|
||||
let name = self.tabs[i]
|
||||
.layers
|
||||
.layers
|
||||
.get(idx)
|
||||
.map(|l| l.name.clone())
|
||||
.unwrap_or_default();
|
||||
if name.is_empty() {
|
||||
return Task::none();
|
||||
}
|
||||
if name == "0" {
|
||||
self.command_line
|
||||
.push_error("Layer \"0\" cannot be deleted.");
|
||||
return Task::none();
|
||||
}
|
||||
if name == self.tabs[i].scene.document.header.current_layer_name {
|
||||
self.command_line
|
||||
.push_error("Cannot delete the current layer. Set another layer current first.");
|
||||
return Task::none();
|
||||
}
|
||||
// Count objects on the layer. Empty → delete straight away;
|
||||
// non-empty → warn first (deleting also removes those objects).
|
||||
let count = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter(|e| e.common().layer == name)
|
||||
.count();
|
||||
if count == 0 {
|
||||
self.push_undo_snapshot(i, "LAYER DELETE");
|
||||
self.tabs[i].scene.document.layers.remove(&name);
|
||||
self.tabs[i].dirty = true;
|
||||
let doc_layers = self.tabs[i].scene.document.layers.clone();
|
||||
let vp_info = self.tabs[i].scene.viewport_list();
|
||||
self.tabs[i]
|
||||
.layers
|
||||
.sync_with_viewports(&doc_layers, vp_info);
|
||||
self.tabs[i].layers.selected = None;
|
||||
self.sync_ribbon_layers();
|
||||
self.sync_layer_panel(i);
|
||||
} else {
|
||||
self.layer_delete_pending = Some((name, count));
|
||||
self.active_modal = Some(crate::app::ModalKind::LayerDeleteWarning);
|
||||
self.modal_offset = iced::Vector::ZERO;
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// User confirmed deleting a non-empty layer: erase every object on it,
|
||||
/// then remove the layer record.
|
||||
pub(super) fn on_layer_delete_confirm(&mut self) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
self.active_modal = None;
|
||||
self.modal_offset = iced::Vector::ZERO;
|
||||
let Some((name, _)) = self.layer_delete_pending.take() else {
|
||||
return Task::none();
|
||||
};
|
||||
self.push_undo_snapshot(i, "LAYER DELETE");
|
||||
let handles: Vec<acadrust::Handle> = self.tabs[i]
|
||||
.scene
|
||||
.document
|
||||
.entities()
|
||||
.filter(|e| e.common().layer == name)
|
||||
.map(|e| e.common().handle)
|
||||
.collect();
|
||||
// Remove the layer record first so the lock guard in `erase_entities`
|
||||
// (which looks the layer up by name) can't block the objects.
|
||||
self.tabs[i].scene.document.layers.remove(&name);
|
||||
self.tabs[i].scene.erase_entities(&handles);
|
||||
self.tabs[i].dirty = true;
|
||||
self.sync_layer_panel(i);
|
||||
self.refresh_properties();
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// Rebuild the Layer-manager panel + ribbon after a layer-table change.
|
||||
fn sync_layer_panel(&mut self, i: usize) {
|
||||
let doc_layers = self.tabs[i].scene.document.layers.clone();
|
||||
let vp_info = self.tabs[i].scene.viewport_list();
|
||||
self.tabs[i].layers.sync_with_viewports(&doc_layers, vp_info);
|
||||
self.tabs[i].layers.selected = None;
|
||||
self.sync_ribbon_layers();
|
||||
}
|
||||
|
||||
pub(super) fn on_layer_set_current(&mut self) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
if let Some(idx) = self.tabs[i].layers.selected {
|
||||
|
||||
Reference in new issue
Block a user