forked from 1b-agent/opencadstudio-upstream
feat(blocks): author Block Properties Table from the block editor
Add the BPROPERTIESTABLE command, the Block Authoring palette row and the grid dialog. The row needs an open block edit session with a Linear or Polar parameter column; OK parses cells to finite numbers, re-checks the table against the session it was opened from, writes it in one undo step, and no-op edits close without an undo.
This commit is contained in:
1 parent
2b0c1a6b58
commit
a1de7e4ecd
13 files changed
+865
-4
No files matched your search
@@ -0,0 +1,127 @@
|
||||
use super::{Message, ModalKind, OpenCADStudio};
|
||||
use crate::scene::block_properties_table as table;
|
||||
use crate::ui::window::block_properties_table::{Action, State};
|
||||
use iced::Task;
|
||||
|
||||
impl OpenCADStudio {
|
||||
pub(in crate::app) fn properties_table_error(&self) -> Option<&'static str> {
|
||||
let tab = &self.tabs[self.active_tab];
|
||||
let Some(session) = tab.active_block_edit_session() else {
|
||||
return Some("Open a block in the Block Editor before editing its Block Properties Table.");
|
||||
};
|
||||
let definition = session.br_handle;
|
||||
if table::columns(&tab.scene.document, definition).is_empty() {
|
||||
return Some(
|
||||
"Place a Linear or Polar parameter before editing the Block Properties Table.",
|
||||
);
|
||||
}
|
||||
table::table(&tab.scene.document, definition).err()
|
||||
}
|
||||
pub(in crate::app) fn open_block_properties_table(&mut self) -> Task<Message> {
|
||||
if let Some(error) = self.properties_table_error() {
|
||||
self.command_line.push_error(crate::t!(error).as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
let tab = &self.tabs[self.active_tab];
|
||||
let definition = tab.active_block_edit_session().unwrap().br_handle;
|
||||
let original = match table::table(&tab.scene.document, definition) {
|
||||
Ok(value) => value,
|
||||
Err(error) => {
|
||||
self.command_line.push_error(crate::t!(error).as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
};
|
||||
let (_, columns, rows) = match table::grid_table(&tab.scene.document, definition) {
|
||||
Ok(Some(value)) => value,
|
||||
Ok(None) => (codec::Handle::NULL, vec![], vec![]),
|
||||
Err(error) => {
|
||||
self.command_line.push_error(crate::t!(error).as_ref());
|
||||
return Task::none();
|
||||
}
|
||||
};
|
||||
self.block_properties_table = Some(State {
|
||||
document: tab.id,
|
||||
definition,
|
||||
epoch: tab.scene.geometry_epoch,
|
||||
original,
|
||||
columns,
|
||||
properties: table::columns(&tab.scene.document, definition),
|
||||
selected_property: None,
|
||||
rows,
|
||||
error: None,
|
||||
notice: None,
|
||||
});
|
||||
self.active_modal = Some(ModalKind::BlockPropertiesTable);
|
||||
Task::none()
|
||||
}
|
||||
pub(super) fn handle_block_properties_table(&mut self, action: Action) -> Task<Message> {
|
||||
if !matches!(action, Action::Apply) {
|
||||
if let Some(state) = self.block_properties_table.as_mut() {
|
||||
state.edit(action);
|
||||
}
|
||||
return Task::none();
|
||||
}
|
||||
let Some(state) = self.block_properties_table.as_ref() else {
|
||||
return Task::none();
|
||||
};
|
||||
let mut cells = Vec::with_capacity(state.rows.len());
|
||||
for row in &state.rows {
|
||||
let mut parsed = Vec::with_capacity(row.len());
|
||||
for value in row {
|
||||
match value.trim().parse::<f64>() {
|
||||
Ok(value) if value.is_finite() => parsed.push(value),
|
||||
_ => {
|
||||
self.block_properties_table.as_mut().unwrap().error = Some(
|
||||
crate::t!("Each cell must be a finite number.").into_owned(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
}
|
||||
}
|
||||
cells.push(parsed);
|
||||
}
|
||||
let i = self.active_tab;
|
||||
let tab = &self.tabs[i];
|
||||
let stale = tab.id != state.document
|
||||
|| tab.active_block_edit_session().map(|s| s.br_handle) != Some(state.definition)
|
||||
|| tab.scene.geometry_epoch != state.epoch
|
||||
|| table::table(&tab.scene.document, state.definition)
|
||||
.ok()
|
||||
.flatten()
|
||||
.as_ref()
|
||||
!= state.original.as_ref();
|
||||
if stale {
|
||||
self.block_properties_table.as_mut().unwrap().error = Some(
|
||||
crate::t!("The block changed. Cancel and reopen the Block Properties Table.")
|
||||
.into_owned(),
|
||||
);
|
||||
return Task::none();
|
||||
}
|
||||
let unchanged = state.original.as_ref().is_some_and(|view| {
|
||||
view.columns == state.columns
|
||||
&& view.rows.iter().eq(cells.iter())
|
||||
});
|
||||
if unchanged {
|
||||
self.block_properties_table = None;
|
||||
self.active_modal = None;
|
||||
return Task::none();
|
||||
}
|
||||
let mut working = tab.scene.document.clone();
|
||||
if let Err(error) =
|
||||
table::apply(&mut working, state.definition, &state.columns, &cells)
|
||||
{
|
||||
self.block_properties_table.as_mut().unwrap().error =
|
||||
Some(crate::t!(error).into_owned());
|
||||
return Task::none();
|
||||
}
|
||||
self.push_undo_snapshot(i, "Block Properties Table");
|
||||
self.tabs[i].scene.document = working;
|
||||
self.tabs[i].scene.bump_geometry();
|
||||
self.tabs[i].dirty = true;
|
||||
self.finish_pending_history(i);
|
||||
self.refresh_properties();
|
||||
self.block_properties_table = None;
|
||||
self.active_modal = None;
|
||||
Task::none()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user