Files
OneCAD-Draft/src/ui/ribbon/underlay_tab.rs
T

231 lines
7.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! The contextual "PDF Underlay" tab: shown and brought forward while only
//! PDF underlays are selected, gone when the selection changes.
use iced::widget::{column, row, slider, text, text_input};
use iced::{Element, Length};
use super::*;
use crate::modules::{ModuleEvent, ToolDef};
/// What the tab shows for the selected underlay (the first one).
#[derive(Clone, Debug, PartialEq)]
pub struct UnderlayContext {
pub contrast: u8,
pub fade: u8,
pub monochrome: bool,
pub shown: bool,
pub snap: bool,
pub contrast_text: String,
pub fade_text: String,
}
impl UnderlayContext {
pub fn new(contrast: u8, fade: u8, monochrome: bool, shown: bool, snap: bool) -> Self {
Self {
contrast,
fade,
monochrome,
shown,
snap,
contrast_text: contrast.to_string(),
fade_text: fade.to_string(),
}
}
}
/// The tab's edits; the host writes them to the selected underlays.
#[derive(Clone, Debug)]
pub enum UnderlayTabMsg {
Contrast(u8),
Fade(u8),
ContrastText(String),
FadeText(String),
/// Slider released or value submitted: write contrast and fade.
Commit,
}
pub(super) const ADJUST: &str = "Adjust";
fn tool(id: &'static str, label: &'static str, icon: &'static [u8]) -> ToolDef {
ToolDef {
id,
label,
icon: IconKind::Svg(icon),
event: ModuleEvent::Command(id.to_string()),
}
}
fn groups() -> &'static [RibbonGroup] {
static GROUPS: std::sync::OnceLock<Vec<RibbonGroup>> = std::sync::OnceLock::new();
const FRAMES: &[u8] = include_bytes!("../../../assets/icons/underlay_frames.svg");
const CLIP: &[u8] = include_bytes!("../../../assets/icons/xclip.svg");
GROUPS.get_or_init(|| {
vec![
RibbonGroup {
title: ADJUST,
tools: vec![RibbonItem::LargeTool(tool(
"_PDFULMONO",
"Display in\nMonochrome",
FRAMES,
))],
},
RibbonGroup {
title: "Clipping",
tools: vec![
RibbonItem::LargeTool(tool("_PDFULCLIP", "Create Clipping\nBoundary", CLIP)),
RibbonItem::LargeTool(tool("_PDFULUNCLIP", "Remove\nClipping", CLIP)),
],
},
RibbonGroup {
title: "Options",
tools: vec![
RibbonItem::LargeTool(tool("_PDFULSHOW", "Show\nUnderlay", FRAMES)),
RibbonItem::LargeTool(tool(
"_PDFULSNAP",
"Enable\nSnap",
include_bytes!("../../../assets/icons/snap_underlays.svg"),
)),
RibbonItem::LargeTool(tool(
"EXTERNALREFERENCES",
"External\nReferences",
crate::ui::icons::FOLDER_OPEN,
)),
],
},
RibbonGroup {
title: "PDF Layers",
tools: vec![RibbonItem::LargeTool(tool(
"ULAYERS",
"Edit\nLayers",
include_bytes!("../../../assets/icons/underlay_layers.svg"),
))],
},
RibbonGroup {
title: "PDF Import",
tools: vec![RibbonItem::LargeTool(tool(
"_PDFULIMPORT",
"Import As\nObjects",
include_bytes!("../../../assets/icons/cui_import.svg"),
))],
},
]
})
}
/// Contrast / Fade: a label, a 0–100 slider and the value box.
fn adjust_rows<'a>(ctx: &'a UnderlayContext) -> Element<'a, Message> {
let line = |label: &'static str,
value: u8,
buffer: &'a str,
on_slide: fn(u8) -> UnderlayTabMsg,
on_text: fn(String) -> UnderlayTabMsg| {
row![
text(t!(label)).size(11).width(Length::Fixed(64.0)),
slider(0..=100u8, value, move |v| Message::UnderlayTab(on_slide(v)))
.on_release(Message::UnderlayTab(UnderlayTabMsg::Commit))
.width(Length::Fixed(110.0)),
text_input("", buffer)
.on_input(move |s| Message::UnderlayTab(on_text(s)))
.on_submit(Message::UnderlayTab(UnderlayTabMsg::Commit))
.size(11)
.padding([2, 4])
.width(Length::Fixed(44.0)),
]
.spacing(6)
.align_y(iced::Center)
};
column![
line(
"Contrast",
ctx.contrast,
&ctx.contrast_text,
UnderlayTabMsg::Contrast,
UnderlayTabMsg::ContrastText,
),
line(
"Fade",
ctx.fade,
&ctx.fade_text,
UnderlayTabMsg::Fade,
UnderlayTabMsg::FadeText,
),
]
.spacing(8)
.padding([6, 4])
.into()
}
impl Ribbon {
/// Show the tab for the selected underlay, or drop it (`None`). A tab
/// that appears comes to the front; one that goes returns the tab that
/// was active before it.
pub fn set_underlay_context(&mut self, context: Option<UnderlayContext>) {
match (&self.underlay_ctx, &context) {
(None, Some(_)) => self.underlay_tab_active = true,
(Some(_), None) => self.underlay_tab_active = false,
_ => {}
}
// A value being typed survives a refresh that leaves the value as is.
let mut context = context;
if let (Some(old), Some(new)) = (&self.underlay_ctx, &mut context) {
if (old.contrast, old.fade) == (new.contrast, new.fade) {
new.contrast_text = old.contrast_text.clone();
new.fade_text = old.fade_text.clone();
}
}
self.underlay_ctx = context;
}
pub fn underlay_context_mut(&mut self) -> Option<&mut UnderlayContext> {
self.underlay_ctx.as_mut()
}
/// Select a tab by its module id; "pdf_underlay" is the contextual tab.
pub fn select_by_id(&mut self, id: &str) -> bool {
if id == "pdf_underlay" {
self.select_underlay_tab();
return self.underlay_tab_active;
}
match self.modules.iter().position(|m| m.id() == id) {
Some(index) => {
self.select(index);
true
}
None => false,
}
}
pub fn select_underlay_tab(&mut self) {
if self.underlay_ctx.is_some() {
self.underlay_tab_active = true;
}
}
pub(super) fn underlay_tab_shown(&self) -> bool {
self.underlay_tab_active && self.underlay_ctx.is_some()
}
pub(super) fn underlay_panels<'a>(
&'a self,
ts: widgets::ToggleState,
style_ctx: &StyleContext<'_>,
) -> Vec<Panel<'a>> {
let Some(ctx) = self.underlay_ctx.as_ref() else {
return Vec::new();
};
groups()
.iter()
.map(|g| {
let lead = |_: bool| -> Vec<Element<'a, Message>> {
if g.title == ADJUST {
vec![adjust_rows(ctx)]
} else {
Vec::new()
}
};
self.panel(g, ts, style_ctx, &lead)
})
.collect()
}
}