forked from 1b-agent/opencadstudio-upstream
- PDFOSNAP, DWFOSNAP and DGNOSNAP are separate; UOSNAP sets them all and reads 2 while they differ. Enable Snap switches the selected kind's. - DWFCLIP / DGNCLIP clip their own kind with the kind's prompts and message; CLIP routes DWF and DGN underlays there; Enter at a corner asks again. - The right-edge underlay tools show for DWF and DGN too (import is PDF only). - Underlay layers: DGN levels (V8 level table, "Default" listed as "0"; V7 used levels named from the level name table), hidden through the layer override data with the DGN placeholder string first; a plotted DWF has none. - V8 colour read from its colour word (by level, index, or a true colour drawn as its nearest index); 3D V8 elements drawn in plan (lines, line strings, ellipses and arcs by their orientation, text); a model's extent includes its text. - DWF 6 TrueType text (font, height, rotation from the font opcode) and DWFx glyph runs are drawn; the default W2D colour map is the measured one. - Contrast pulls lightness towards a per-kind pivot as measured (PDF 0.5, DWF 0.243, DGN 0.65). - Zoom extents includes raster images and underlays whose frames are hidden. Co-Authored-By: Claude Opus 5.5 <[email protected]>
196 lines
8.2 KiB
Rust
196 lines
8.2 KiB
Rust
//! The right-edge underlay and xref tools: their state comes from the
|
|
//! selection, their buttons edit it.
|
|
|
|
use super::*;
|
|
use codec::entities::{Underlay, UnderlayDisplayFlags};
|
|
use crate::ui::ribbon::UnderlayContext;
|
|
|
|
impl OpenCADStudio {
|
|
/// The selected underlays when they are all of one kind, or nothing
|
|
/// when anything else (or another kind) is selected.
|
|
fn selected_pdf_underlays(&self, i: usize) -> Vec<(codec::Handle, Underlay)> {
|
|
let mut out: Vec<(codec::Handle, Underlay)> = Vec::new();
|
|
for (handle, entity) in self.tabs[i].scene.selected_entities() {
|
|
match entity {
|
|
codec::EntityType::Underlay(u)
|
|
if out.first().is_none_or(|(_, first)| first.underlay_type == u.underlay_type) =>
|
|
{
|
|
out.push((handle, u.clone()));
|
|
}
|
|
_ => return Vec::new(),
|
|
}
|
|
}
|
|
out
|
|
}
|
|
|
|
/// The selected xrefs, or nothing when anything else is selected.
|
|
pub(in crate::app) fn selected_xrefs(&self, i: usize) -> Vec<codec::Handle> {
|
|
let doc = &self.tabs[i].scene.document;
|
|
let mut out = Vec::new();
|
|
for (handle, entity) in self.tabs[i].scene.selected_entities() {
|
|
let xref = match entity {
|
|
codec::EntityType::Insert(ins) => doc
|
|
.block_records
|
|
.get(&ins.block_name)
|
|
.is_some_and(|br| br.flags.is_xref || br.flags.is_xref_overlay),
|
|
_ => false,
|
|
};
|
|
if !xref {
|
|
return Vec::new();
|
|
}
|
|
out.push(handle);
|
|
}
|
|
out
|
|
}
|
|
|
|
/// The selection context for the right-edge tools: the selected PDF
|
|
/// underlay's switches while only underlays are selected, or that only
|
|
/// xrefs are.
|
|
pub(in crate::app) fn sync_underlay_tab(&mut self) {
|
|
let i = self.active_tab;
|
|
let xref = !self.tabs[i].is_start && !self.selected_xrefs(i).is_empty();
|
|
let context = if self.tabs[i].is_start {
|
|
None
|
|
} else {
|
|
self.selected_pdf_underlays(i).first().map(|(_, u)| {
|
|
UnderlayContext {
|
|
kind: u.underlay_type,
|
|
monochrome: u.flags.contains(UnderlayDisplayFlags::MONOCHROME),
|
|
shown: u.flags.contains(UnderlayDisplayFlags::ON),
|
|
snap: crate::scene::model::pdf_vector::underlay_osnap(u.underlay_type),
|
|
}
|
|
})
|
|
};
|
|
self.ribbon.set_underlay_context(context, xref);
|
|
}
|
|
|
|
/// One undo step that edits every selected PDF underlay.
|
|
fn edit_selected_underlays(&mut self, i: usize, label: &str, edit: impl Fn(&mut Underlay)) {
|
|
let handles: Vec<_> = self
|
|
.selected_pdf_underlays(i)
|
|
.into_iter()
|
|
.map(|(h, _)| h)
|
|
.filter(|h| !self.tabs[i].scene.is_layer_locked(*h))
|
|
.collect();
|
|
if handles.is_empty() {
|
|
return;
|
|
}
|
|
self.push_undo_snapshot(i, label);
|
|
for handle in &handles {
|
|
if let Some(codec::EntityType::Underlay(u)) =
|
|
self.tabs[i].scene.document.get_entity_mut(*handle)
|
|
{
|
|
edit(u);
|
|
}
|
|
self.tabs[i].scene.reseed_derived_caches(*handle);
|
|
}
|
|
let changes: Vec<_> = handles
|
|
.iter()
|
|
.map(|h| (*h, crate::scene::ChangeKind::Modified))
|
|
.collect();
|
|
self.tabs[i].scene.bump_entities(&changes);
|
|
self.tabs[i].dirty = true;
|
|
self.refresh_properties();
|
|
}
|
|
|
|
/// The right-edge tools (host-only command names).
|
|
pub(super) fn dispatch_pdf_underlay(&mut self, cmd: &str, i: usize) -> Option<Task<Message>> {
|
|
match cmd {
|
|
"_PDFULMONO" => {
|
|
let on = !self
|
|
.selected_pdf_underlays(i)
|
|
.first()
|
|
.is_some_and(|(_, u)| u.flags.contains(UnderlayDisplayFlags::MONOCHROME));
|
|
self.edit_selected_underlays(i, "PDFADJUST", |u| u.set_monochrome(on));
|
|
}
|
|
"_PDFULSHOW" => {
|
|
let on = !self
|
|
.selected_pdf_underlays(i)
|
|
.first()
|
|
.is_some_and(|(_, u)| u.flags.contains(UnderlayDisplayFlags::ON));
|
|
self.edit_selected_underlays(i, "PDFUNDERLAY", |u| u.set_on(on));
|
|
}
|
|
// Enable Snap switches PDFOSNAP, DWFOSNAP or DGNOSNAP.
|
|
"_PDFULSNAP" => {
|
|
let Some((_, underlay)) = self.selected_pdf_underlays(i).into_iter().next() else {
|
|
return Some(Task::none());
|
|
};
|
|
let kind = underlay.underlay_type;
|
|
let on = !crate::scene::model::pdf_vector::underlay_osnap(kind);
|
|
crate::scene::model::pdf_vector::set_underlay_osnap(kind, on);
|
|
self.tabs[i].scene.reseed_underlays();
|
|
self.sync_underlay_tab();
|
|
}
|
|
// External Reference tab: the selected xrefs.
|
|
"_XREFEDIT" | "_XREFOPEN" => {
|
|
let Some(&handle) = self.selected_xrefs(i).first() else {
|
|
return Some(Task::none());
|
|
};
|
|
self.tabs[i].scene.deselect_all();
|
|
self.tabs[i].scene.select_entity(handle, true);
|
|
let command = if cmd == "_XREFEDIT" { "REFEDIT" } else { "XOPEN" };
|
|
return Some(self.dispatch_command(command));
|
|
}
|
|
"_XREFCLIP" => {
|
|
use crate::command::CadCommand;
|
|
let inserts = self.selected_xrefs(i);
|
|
if inserts.is_empty() {
|
|
return Some(Task::none());
|
|
}
|
|
let clipped = inserts.iter().any(|h| {
|
|
crate::scene::pick::xclip::filter_handle(&self.tabs[i].scene.document, *h).is_some()
|
|
});
|
|
let (command, first) =
|
|
crate::modules::insert::xclip::XclipCommand::start_new_boundary(inserts, clipped);
|
|
if let crate::command::CmdResult::ReportMeasurement(text) = first {
|
|
for line in text.lines() {
|
|
self.command_line.push_output(line);
|
|
}
|
|
}
|
|
self.command_line.push_info(&command.prompt());
|
|
self.tabs[i].active_cmd = Some(Box::new(command));
|
|
}
|
|
"_XREFUNCLIP" => {
|
|
let inserts = self.selected_xrefs(i);
|
|
if !inserts.is_empty() {
|
|
self.apply_xclip(i, inserts, crate::modules::insert::xclip::XclipAction::Delete);
|
|
}
|
|
}
|
|
"_PDFULUNCLIP" => {
|
|
self.edit_selected_underlays(i, "PDFCLIP", |u| {
|
|
u.clip_boundary_vertices.clear();
|
|
u.clip_inverted = false;
|
|
u.flags -= UnderlayDisplayFlags::CLIPPING;
|
|
});
|
|
}
|
|
"_PDFULCLIP" => {
|
|
use crate::command::CadCommand;
|
|
let Some((handle, underlay)) = self.selected_pdf_underlays(i).into_iter().next()
|
|
else {
|
|
return Some(Task::none());
|
|
};
|
|
let (command, first) =
|
|
crate::modules::insert::pdf_clip::PdfClipCommand::new_boundary(handle, underlay);
|
|
if let crate::command::CmdResult::ReportMeasurement(text) = first {
|
|
for line in text.lines() {
|
|
self.command_line.push_output(line);
|
|
}
|
|
}
|
|
self.command_line.push_info(&command.prompt());
|
|
self.tabs[i].active_cmd = Some(Box::new(command));
|
|
}
|
|
"_PDFULIMPORT" => {
|
|
use crate::command::CadCommand;
|
|
let Some((handle, _)) = self.selected_pdf_underlays(i).into_iter().next() else {
|
|
return Some(Task::none());
|
|
};
|
|
let command = crate::modules::insert::pdf_import::PdfImportCommand::for_underlay(handle);
|
|
self.command_line.push_info(&command.prompt());
|
|
self.tabs[i].active_cmd = Some(Box::new(command));
|
|
}
|
|
_ => return None,
|
|
}
|
|
Some(self.finish_dispatch(cmd))
|
|
}
|
|
}
|