forked from 1b-agent/opencadstudio-upstream
Merge pull request #1486 from ramox81/codex/xref-context-tab
Selection tools on the right-edge toolbar for PDF underlays and xrefs
This commit is contained in:
commit
82fe204a70
34 files changed
+387
-353
No files matched your search
@@ -1,9 +1,9 @@
|
||||
//! The contextual PDF Underlay ribbon tab: its values come from the selected
|
||||
//! underlays, its buttons edit them.
|
||||
//! The right-edge PDF underlay and xref tools: their state comes from the
|
||||
//! selection, their buttons edit it.
|
||||
|
||||
use super::*;
|
||||
use codec::entities::{Underlay, UnderlayDisplayFlags, UnderlayType};
|
||||
use crate::ui::ribbon::{UnderlayContext, UnderlayTabMsg};
|
||||
use crate::ui::ribbon::UnderlayContext;
|
||||
|
||||
impl OpenCADStudio {
|
||||
/// The selected PDF underlays, or nothing when anything else is selected.
|
||||
@@ -20,23 +20,44 @@ impl OpenCADStudio {
|
||||
out
|
||||
}
|
||||
|
||||
/// Show the tab while only PDF underlays are selected.
|
||||
/// 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::new(
|
||||
u.contrast,
|
||||
u.fade,
|
||||
u.flags.contains(UnderlayDisplayFlags::MONOCHROME),
|
||||
u.flags.contains(UnderlayDisplayFlags::ON),
|
||||
crate::scene::model::pdf_vector::pdf_osnap(),
|
||||
)
|
||||
UnderlayContext {
|
||||
monochrome: u.flags.contains(UnderlayDisplayFlags::MONOCHROME),
|
||||
shown: u.flags.contains(UnderlayDisplayFlags::ON),
|
||||
snap: crate::scene::model::pdf_vector::pdf_osnap(),
|
||||
}
|
||||
})
|
||||
};
|
||||
self.ribbon.set_underlay_context(context);
|
||||
self.ribbon.set_underlay_context(context, xref);
|
||||
}
|
||||
|
||||
/// One undo step that edits every selected PDF underlay.
|
||||
@@ -68,43 +89,7 @@ impl OpenCADStudio {
|
||||
self.refresh_properties();
|
||||
}
|
||||
|
||||
pub(in crate::app) fn update_underlay_tab(&mut self, message: UnderlayTabMsg) -> Task<Message> {
|
||||
let i = self.active_tab;
|
||||
let Some(ctx) = self.ribbon.underlay_context_mut() else {
|
||||
return Task::none();
|
||||
};
|
||||
match message {
|
||||
UnderlayTabMsg::Contrast(v) => {
|
||||
ctx.contrast = v;
|
||||
ctx.contrast_text = v.to_string();
|
||||
}
|
||||
UnderlayTabMsg::Fade(v) => {
|
||||
ctx.fade = v;
|
||||
ctx.fade_text = v.to_string();
|
||||
}
|
||||
UnderlayTabMsg::ContrastText(s) => ctx.contrast_text = s,
|
||||
UnderlayTabMsg::FadeText(s) => ctx.fade_text = s,
|
||||
UnderlayTabMsg::Commit => {
|
||||
// A typed value outside 0–100 or not a number keeps the value.
|
||||
let parse = |s: &str, old: u8| {
|
||||
s.trim().parse::<u8>().ok().filter(|v| *v <= 100).unwrap_or(old)
|
||||
};
|
||||
let (contrast, fade) = (
|
||||
parse(&ctx.contrast_text, ctx.contrast),
|
||||
parse(&ctx.fade_text, ctx.fade),
|
||||
);
|
||||
ctx.contrast_text = contrast.to_string();
|
||||
ctx.fade_text = fade.to_string();
|
||||
self.edit_selected_underlays(i, "PDFADJUST", |u| {
|
||||
u.set_contrast(contrast);
|
||||
u.set_fade(fade);
|
||||
});
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
||||
/// The tab's buttons (host-only command names).
|
||||
/// 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" => {
|
||||
@@ -127,6 +112,41 @@ impl OpenCADStudio {
|
||||
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();
|
||||
|
||||
@@ -700,8 +700,7 @@ impl OpenCADStudio {
|
||||
state.selected = pages;
|
||||
return Ok(Task::none());
|
||||
}
|
||||
// Bring a ribbon tab forward by module id ("pdf_underlay" for the
|
||||
// contextual underlay tab).
|
||||
// Bring a ribbon tab forward by module id.
|
||||
"ribbon_tab" => {
|
||||
let id = string(req, "value")?;
|
||||
if !self.ribbon.select_by_id(id) {
|
||||
|
||||
@@ -3838,11 +3838,8 @@ pub enum Message {
|
||||
PdfAttachPick,
|
||||
/// PDFIMPORT File: pick the PDF to import.
|
||||
PdfImportPick,
|
||||
/// The contextual PDF Underlay tab.
|
||||
RibbonSelectUnderlayTab,
|
||||
/// An edit in one of the PDF dialogs.
|
||||
PdfDialog(crate::ui::window::pdf_dialogs::PdfDialogMsg),
|
||||
UnderlayTab(crate::ui::ribbon::UnderlayTabMsg),
|
||||
PdfImportPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
||||
/// Result of the PDFATTACH file picker.
|
||||
PdfAttachPickResult(Result<(std::path::PathBuf, std::sync::Arc<Vec<u8>>), String>),
|
||||
|
||||
+12
-5
@@ -318,6 +318,18 @@ impl OpenCADStudio {
|
||||
}
|
||||
|
||||
pub fn update(&mut self, msg: Message) -> Task<Message> {
|
||||
// Whatever makes another document active (switching, opening,
|
||||
// creating or closing a tab), the contextual ribbon tab follows that
|
||||
// document's selection.
|
||||
let before = self.tabs.get(self.active_tab).map(|tab| tab.id);
|
||||
let task = self.update_message(msg);
|
||||
if self.tabs.get(self.active_tab).map(|tab| tab.id) != before {
|
||||
self.sync_underlay_tab();
|
||||
}
|
||||
task
|
||||
}
|
||||
|
||||
fn update_message(&mut self, msg: Message) -> Task<Message> {
|
||||
if let Some(tab) = self.tabs.get(self.active_tab) {
|
||||
crate::entities::common::set_unit_context(
|
||||
crate::entities::common::UnitContext::from_header(&tab.scene.document.header),
|
||||
@@ -1772,11 +1784,6 @@ impl OpenCADStudio {
|
||||
Task::none()
|
||||
}
|
||||
|
||||
Message::RibbonSelectUnderlayTab => {
|
||||
self.ribbon.select_underlay_tab();
|
||||
Task::none()
|
||||
}
|
||||
Message::UnderlayTab(message) => self.update_underlay_tab(message),
|
||||
Message::PdfDialog(message) => self.update_pdf_dialog(message),
|
||||
Message::RibbonSelectTab(idx) => {
|
||||
self.ribbon.select(idx);
|
||||
|
||||
+26
-3
@@ -1601,9 +1601,32 @@ bg={bg_ms:.1}ms n={view_count}"
|
||||
}
|
||||
}
|
||||
|
||||
// Paper-space context actions: a right-edge vertical toolbar
|
||||
// (viewport / page setup / plot) instead of a contextual ribbon tab.
|
||||
if is_paper && !tab.is_start {
|
||||
// Selection actions: with only PDF underlays or only xrefs
|
||||
// selected, their tools take the right edge (over the paper-space
|
||||
// tools, which come back when the selection changes).
|
||||
// ponytail: one right-edge toolbar at a time; stack them if both
|
||||
// are ever needed together.
|
||||
let selection_tools = if tab.is_start {
|
||||
None
|
||||
} else if self.ribbon.xref_context() {
|
||||
crate::ui::side_toolbar::view(&crate::ui::ribbon::xref_tools())
|
||||
} else if let Some(ctx) = self.ribbon.underlay_context() {
|
||||
let ctx = ctx.clone();
|
||||
crate::ui::side_toolbar::view_with_active(
|
||||
&crate::ui::ribbon::pdf_underlay_tools(),
|
||||
&move |id| match id {
|
||||
"_PDFULMONO" => ctx.monochrome,
|
||||
"_PDFULSHOW" => ctx.shown,
|
||||
"_PDFULSNAP" => ctx.snap,
|
||||
_ => false,
|
||||
},
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(tb) = selection_tools {
|
||||
viewport_stack = viewport_stack.push(tb);
|
||||
} else if is_paper && !tab.is_start {
|
||||
if let Some(tb) =
|
||||
crate::ui::side_toolbar::view(&crate::modules::layout::paper_space_tools())
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user