forked from 1b-agent/opencadstudio-upstream
feat(bedit): add Edit Block Definition dialog and new block authoring
Open the Edit Block Definition dialog when BEDIT is invoked without an active selection, listing candidate block definitions alphabetically while filtering layout records, anonymous blocks, and external references. Allow typing a new valid block name to start an empty block editor session that only commits to the drawing block table upon explicit save.
This commit is contained in:
1 parent
9632979d09
commit
abad1b9045
10 files changed
+719
-86
No files matched your search
@@ -56,7 +56,9 @@ fn main_depth(app: &OpenCADStudio) -> usize {
|
||||
|
||||
#[test]
|
||||
fn local_undo_preserves_imported_native_networks_when_constraints_are_unchanged() {
|
||||
use codec::objects::{AssocNetwork, AssociativeData, AssociativeObject, Dictionary, ObjectType};
|
||||
use codec::objects::{
|
||||
AssocNetwork, AssociativeData, AssociativeObject, Dictionary, ObjectType,
|
||||
};
|
||||
let (mut app, reference) = fixture();
|
||||
let i = app.active_tab;
|
||||
let doc = &mut app.tabs[i].scene.document;
|
||||
@@ -919,3 +921,234 @@ fn close_all_resumes_after_discarding_a_dirty_block() {
|
||||
.any(|tab| tab.id == first || tab.id == second));
|
||||
assert!(app.pending_tab_closes.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_no_selection_opens_edit_block_definition_dialog() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab].scene.define_block_raw(
|
||||
"DOOR",
|
||||
Vector3::ZERO,
|
||||
vec![EntityType::Line(Line::from_points(
|
||||
Vector3::ZERO,
|
||||
Vector3::UNIT_X,
|
||||
))],
|
||||
);
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get_mut("DOOR")
|
||||
.unwrap()
|
||||
.description = "Wooden door".to_string();
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.define_block_raw("WINDOW", Vector3::ZERO, vec![]);
|
||||
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
|
||||
assert_eq!(app.active_modal, Some(ModalKind::BlockEditPicker));
|
||||
let picker = app.block_edit_picker.as_ref().expect("picker state set");
|
||||
assert_eq!(picker.existing_names, vec!["DOOR", "WINDOW"]);
|
||||
assert_eq!(picker.name, "DOOR");
|
||||
assert_eq!(picker.description, "Wooden door");
|
||||
assert!(picker.error.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_dialog_select_existing_block() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.define_block_raw("DOOR", Vector3::ZERO, vec![]);
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.define_block_raw("WINDOW", Vector3::ZERO, vec![]);
|
||||
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerSelect("WINDOW".to_string()));
|
||||
let picker = app.block_edit_picker.as_ref().unwrap();
|
||||
assert_eq!(picker.name, "WINDOW");
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerApply);
|
||||
assert_eq!(app.active_modal, None);
|
||||
assert!(app.tabs[tab].active_block_edit.is_some());
|
||||
assert_eq!(
|
||||
app.tabs[tab]
|
||||
.active_block_edit_session()
|
||||
.unwrap()
|
||||
.block_name,
|
||||
"WINDOW"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_dialog_name_validation() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerName("INVALID/NAME".to_string()));
|
||||
assert!(app.block_edit_picker.as_ref().unwrap().error.is_some());
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerApply);
|
||||
assert_eq!(app.active_modal, Some(ModalKind::BlockEditPicker));
|
||||
assert!(app.tabs[tab].active_block_edit.is_none());
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerName("".to_string()));
|
||||
let _ = app.update(Message::BlockEditPickerApply);
|
||||
assert_eq!(app.active_modal, Some(ModalKind::BlockEditPicker));
|
||||
assert!(app.tabs[tab].active_block_edit.is_none());
|
||||
|
||||
let _ = app.update(Message::CloseModal);
|
||||
assert_eq!(app.active_modal, None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_dialog_create_new_block_closes_without_saving() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerName("BRAND_NEW".to_string()));
|
||||
assert!(app.block_edit_picker.as_ref().unwrap().error.is_none());
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerApply);
|
||||
assert_eq!(app.active_modal, None);
|
||||
assert!(app.tabs[tab].active_block_edit.is_some());
|
||||
assert_eq!(
|
||||
app.tabs[tab]
|
||||
.active_block_edit_session()
|
||||
.unwrap()
|
||||
.block_name,
|
||||
"BRAND_NEW"
|
||||
);
|
||||
assert!(!app.block_edit_dirty(tab));
|
||||
|
||||
let _ = app.dispatch_command("BCLOSE");
|
||||
assert_eq!(app.active_modal, None);
|
||||
assert!(app.tabs[tab].active_block_edit.is_none());
|
||||
assert!(
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get("BRAND_NEW")
|
||||
.is_none(),
|
||||
"unpersisted new block closed without saving must not remain in drawing"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_dialog_create_new_block_discard_dirty_leaves_no_block() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerName("BRAND_NEW2".to_string()));
|
||||
let _ = app.update(Message::BlockEditPickerApply);
|
||||
assert!(app.tabs[tab].active_block_edit.is_some());
|
||||
|
||||
circle(&mut app, 1.0);
|
||||
assert!(app.block_edit_dirty(tab));
|
||||
|
||||
let _ = app.dispatch_command("BCLOSE");
|
||||
assert_eq!(app.active_modal, Some(ModalKind::BlockEditChanges));
|
||||
|
||||
let _ = app.update(Message::BlockEditChangesDiscard);
|
||||
assert_eq!(app.active_modal, None);
|
||||
assert!(app.tabs[tab].active_block_edit.is_none());
|
||||
assert!(
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get("BRAND_NEW2")
|
||||
.is_none(),
|
||||
"discarded new block must not remain in drawing"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_dialog_create_new_block_save_persists_and_undoes() {
|
||||
let mut app = OpenCADStudio::new_for_test();
|
||||
app.automation_op(r#"{"op":"new"}"#);
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
|
||||
let _ = app.update(Message::BlockEditPickerName("SAVED_BLOCK".to_string()));
|
||||
let _ = app.update(Message::BlockEditPickerApply);
|
||||
assert!(app.tabs[tab].active_block_edit.is_some());
|
||||
|
||||
circle(&mut app, 1.0);
|
||||
let _ = app.dispatch_command("BSAVE");
|
||||
assert!(app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get("SAVED_BLOCK")
|
||||
.is_some());
|
||||
|
||||
let _ = app.dispatch_command("BCLOSE");
|
||||
assert!(app.tabs[tab].active_block_edit.is_none());
|
||||
assert!(app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get("SAVED_BLOCK")
|
||||
.is_some());
|
||||
|
||||
app.undo_steps(1);
|
||||
assert!(
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get("SAVED_BLOCK")
|
||||
.is_none(),
|
||||
"undo must revert block creation"
|
||||
);
|
||||
|
||||
app.redo_steps(1);
|
||||
assert!(
|
||||
app.tabs[tab]
|
||||
.scene
|
||||
.document
|
||||
.block_records
|
||||
.get("SAVED_BLOCK")
|
||||
.is_some(),
|
||||
"redo must restore block creation"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn locked_bedit_with_preselected_insert_opens_directly() {
|
||||
let (mut app, reference) = fixture();
|
||||
let tab = app.active_tab;
|
||||
app.tabs[tab].scene.deselect_all();
|
||||
app.tabs[tab].scene.select_entity(reference, true);
|
||||
|
||||
let _ = app.dispatch_command("BEDIT");
|
||||
assert_eq!(app.active_modal, None);
|
||||
assert!(app.tabs[tab].active_block_edit.is_some());
|
||||
assert_eq!(
|
||||
app.tabs[tab]
|
||||
.active_block_edit_session()
|
||||
.unwrap()
|
||||
.block_name,
|
||||
"EDIT_ME"
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user