diff --git a/src/app/commands/blocks.rs b/src/app/commands/blocks.rs index ee29c26b0..8e782c066 100644 --- a/src/app/commands/blocks.rs +++ b/src/app/commands/blocks.rs @@ -515,11 +515,13 @@ impl OpenCADStudio { self.open_attedit_dialog(); } + "PDFATTACH" => { + return Some(Task::done(Message::PdfAttachPick)); + } "XATTACH" => { // Launch the file picker; XAttachPickResult will start the command. return Some(Task::done(Message::XAttachPick)); } - cmd if cmd == "WBLOCK" || cmd == "WB" || cmd.starts_with("WBLOCK ") => { let arg = cmd.splitn(2, ' ').nth(1).unwrap_or("").trim(); if arg.is_empty() { diff --git a/src/app/mod.rs b/src/app/mod.rs index a4547f0dd..ad85aaffc 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -3189,6 +3189,12 @@ pub enum Message { ImagePick, /// Result of the image file picker + pixel dimension decode. ImagePickResult(Result<(std::path::PathBuf, u32, u32), String>), + // ── PDF Underlay ────────────────────────────────────────────────────── + /// Open file-picker dialog for PDFATTACH command (async). + PdfAttachPick, + /// Result of the PDFATTACH file picker. + PdfAttachPickResult(Result), + // ── XREF ────────────────────────────────────────────────────────────── /// Open file-picker dialog for XATTACH command (async). XAttachPick, diff --git a/src/app/update/mod.rs b/src/app/update/mod.rs index 5e04de369..fbe07234d 100644 --- a/src/app/update/mod.rs +++ b/src/app/update/mod.rs @@ -902,7 +902,60 @@ impl OpenCADStudio { } Task::none() } + Message::PdfAttachPick => Task::perform( + async { + let handle = crate::sys::file_dialog() + .set_title("Select PDF Underlay") + .add_filter("PDF Files", &["pdf", "PDF"]) + .pick_file() + .await; + match handle { + Some(h) => Ok(crate::sys::handle_path(&h)), + None => Err("Cancelled".to_string()), + } + }, + Message::PdfAttachPickResult, + ), + + Message::PdfAttachPickResult(Ok(path)) => { + use acadrust::objects::{ObjectType, UnderlayDefinition}; + use crate::command::CadCommand; + use crate::modules::insert::pdf_attach::PdfAttachCommand; + + let i = self.active_tab; + let path_str = path.to_string_lossy().into_owned(); + + let definition_handle = self.tabs[i].scene.document.allocate_handle(); + + let mut definition = UnderlayDefinition::pdf(&path_str, "1"); + definition.handle = definition_handle; + + self.tabs[i] + .scene + .document + .objects + .insert( + definition_handle, + ObjectType::UnderlayDefinition(definition), + ); + + let cmd = PdfAttachCommand::new(definition_handle); + + self.command_line.push_info(&cmd.prompt()); + self.tabs[i].active_cmd = Some(Box::new(cmd)); + + Task::none() + } + + Message::PdfAttachPickResult(Err(e)) => { + if e != "Cancelled" { + self.command_line + .push_error(crate::tf!("PDFATTACH: {e}").as_ref()); + } + + Task::none() + } Message::XAttachPick => Task::perform( async { let handle = crate::sys::file_dialog() diff --git a/src/modules/insert/mod.rs b/src/modules/insert/mod.rs index d3b01ff9f..0311f3eca 100644 --- a/src/modules/insert/mod.rs +++ b/src/modules/insert/mod.rs @@ -16,6 +16,7 @@ pub mod minsert; mod mview_block; mod open_obj; mod pc_attach; +pub(crate) mod pdf_attach; mod snap_underlays; pub(crate) mod solid3d_cmds; mod underlay_layers; @@ -45,6 +46,7 @@ impl CadModule for InsertModule { title: "Reference", tools: vec![ RibbonItem::LargeTool(xattach::tool()), + RibbonItem::LargeTool(pdf_attach::tool()), RibbonItem::LargeTool(xclip::tool()), RibbonItem::LargeTool(xadjust::tool()), RibbonItem::Tool(underlay_layers::tool()), diff --git a/src/modules/insert/pdf_attach.rs b/src/modules/insert/pdf_attach.rs new file mode 100644 index 000000000..3652e6c8d --- /dev/null +++ b/src/modules/insert/pdf_attach.rs @@ -0,0 +1,69 @@ +use acadrust::entities::Underlay; +use acadrust::types::{Handle, Vector3}; +use acadrust::EntityType; +use glam::DVec3; + +use crate::command::{CadCommand, CmdResult, WorkingPlane}; +use crate::modules::{IconKind, ModuleEvent, ToolDef}; +use crate::t; + +pub const ICON: IconKind = + IconKind::Svg(include_bytes!("../../../assets/icons/underlay_layers.svg")); + +pub fn tool() -> ToolDef { + ToolDef { + id: "PDFATTACH", + label: "Attach PDF", + icon: ICON, + event: ModuleEvent::Command("PDFATTACH".to_string()), + } +} + +pub struct PdfAttachCommand { + definition_handle: Handle, + plane: WorkingPlane, +} + +impl PdfAttachCommand { + pub fn new(definition_handle: Handle) -> Self { + Self { + definition_handle, + plane: WorkingPlane::default(), + } + } +} + +impl CadCommand for PdfAttachCommand { + fn set_working_plane(&mut self, plane: WorkingPlane) { + self.plane = plane; + } + + fn name(&self) -> &'static str { + "PDFATTACH" + } + + fn prompt(&self) -> String { + t!("PDFATTACH Specify insertion point:").into_owned() + } + + fn on_point(&mut self, pt: DVec3) -> CmdResult { + let point = self.plane.to_local(pt); + + let mut underlay = Underlay::pdf(); + underlay.definition_handle = self.definition_handle; + underlay.insertion_point = Vector3::new(point.x, point.y, point.z); + + CmdResult::CommitAndExit( + self.plane + .place_entity(EntityType::Underlay(underlay)), + ) + } + + fn on_enter(&mut self) -> CmdResult { + CmdResult::Cancel + } +} + +inventory::submit!(crate::command::CommandRegistration { + names: &["PDFATTACH"] +});