use super::{Message, ModalKind, OpenCADStudio}; use crate::scene::{block_basepoint, block_lookup as lookup}; use crate::ui::window::block_lookup::{Action, Draft, State}; use iced::Task; impl OpenCADStudio { pub(in crate::app) fn handle_place_block_single_parameter( &mut self, result: crate::command::CmdResult, ) -> Task { use crate::modules::block_editor::single_parameter::Kind; let crate::command::CmdResult::PlaceBlockSingleParameter { document_id, definition, kind, location, } = result else { unreachable!() }; let i = self.active_tab; if self.tabs[i].id != document_id || self.tabs[i] .active_block_edit_session() .map(|s| s.br_handle) != Some(definition) { self.command_line.push_error( crate::t!("The parameter's Block Editor session is no longer active.").as_ref(), ); return Task::none(); } if let Some(error) = self.parameter_placement_error(kind.name()) { self.command_line.push_error(crate::t!(error).as_ref()); return Task::none(); } if !location.is_finite() { self.command_line .push_error(crate::t!("Specify a finite parameter location.").as_ref()); return Task::none(); } let location = codec::types::Vector3::new(location.x, location.y, location.z); if kind == Kind::Basepoint && block_basepoint::parameter(&self.tabs[i].scene.document, definition).is_some_and( |(_, p)| { p.point == location && p.base_point == location && p.parameter.definition_point == location }, ) { self.tabs[i].active_cmd = None; self.tabs[i].scene.clear_preview_wire(); return Task::none(); } self.push_undo_snapshot(i, &format!("{} parameter", kind.name())); match kind { Kind::Basepoint => { block_basepoint::create(&mut self.tabs[i].scene.document, definition, location) } Kind::Lookup => lookup::create(&mut self.tabs[i].scene.document, definition, location), } .expect("validated parameter placement"); self.tabs[i].scene.bump_geometry(); self.tabs[i].dirty = true; self.tabs[i].active_cmd = None; self.tabs[i].scene.clear_preview_wire(); self.finish_pending_history(i); self.refresh_properties(); self.command_line.push_output( crate::t!(if kind == Kind::Lookup { "Lookup parameter placed. Use BLOOKUPTABLE to edit its property table." } else { "Basepoint parameter placed." }) .as_ref(), ); Task::none() } pub(in crate::app) fn lookup_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 a Lookup table."); }; if lookup::parameters(&tab.scene.document, session.br_handle).is_empty() { Some("Place a Lookup parameter before editing its property table.") } else { lookup::placement_error(&tab.scene.document, session.br_handle) } } pub(in crate::app) fn open_lookup_table(&mut self, name: &str) -> Task { if let Some(error) = self.lookup_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 mut drafts = vec![]; for (handle, parameter) in lookup::parameters(&tab.scene.document, definition) { let original = match lookup::table(&tab.scene.document, definition, handle) { Ok(value) => value, Err(error) => { self.command_line.push_error(crate::t!(error).as_ref()); return Task::none(); } }; let edited = original .as_ref() .map(|(_, t)| t.clone()) .unwrap_or_else(|| lookup::empty_table(parameter)); if edited.row_count < 0 || edited.row_count > 1000 || edited.columns.is_empty() || edited.columns.len() > 64 || edited.column_count != edited.columns.len() as i32 || edited.expressions.len() != edited.columns.len() * edited.row_count as usize { self.command_line.push_error( crate::t!("This Lookup table has inconsistent or unsupported dimensions.") .as_ref(), ); return Task::none(); } drafts.push(Draft { parameter: handle, original_parameter: parameter.clone(), original, edited, }); } let selected = if name.is_empty() { 0 } else { let Some(index) = drafts .iter() .position(|d| d.original_parameter.lookup_name.eq_ignore_ascii_case(name)) else { self.command_line .push_error(crate::t!("No Lookup parameter has that name.").as_ref()); return Task::none(); }; index }; self.block_lookup = Some(State { document: tab.id, definition, epoch: tab.scene.geometry_epoch, drafts, selected, properties: lookup::properties(&tab.scene.document, definition), selected_property: None, error: None, notice: None, }); self.active_modal = Some(ModalKind::BlockLookup); Task::none() } pub(super) fn handle_block_lookup(&mut self, action: Action) -> Task { if !matches!(action, Action::Apply) { if let Some(state) = self.block_lookup.as_mut() { state.edit(action); } return Task::none(); } let Some(state) = self.block_lookup.as_ref() else { return Task::none(); }; 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 || state.drafts.iter().any(|d| { lookup::parameters(&tab.scene.document, state.definition) .into_iter() .find(|(h, _)| *h == d.parameter) .map(|(_, p)| p) != Some(&d.original_parameter) || lookup::table(&tab.scene.document, state.definition, d.parameter) .ok() .as_ref() != Some(&d.original) }); if stale { self.block_lookup.as_mut().unwrap().error = Some( crate::t!("The block changed. Cancel and reopen the Lookup table.").into_owned(), ); return Task::none(); } let mut working = tab.scene.document.clone(); let mut changed = false; for draft in &state.drafts { if draft.changed() { if let Err(error) = lookup::apply_table( &mut working, state.definition, draft.parameter, draft.edited.clone(), ) { self.block_lookup.as_mut().unwrap().error = Some(crate::t!(error.as_str()).into_owned()); return Task::none(); } changed = true; } } if changed { self.push_undo_snapshot(i, "Lookup tables"); 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_lookup = None; self.active_modal = None; Task::none() } }