forked from 1b-agent/opencadstudio-upstream
style: rustfmt the tree so the CI lint check passes
This commit is contained in:
1 parent
1317a7b4fd
commit
6025d8be44
128 files changed
+4097
-1676
No files matched your search
@@ -553,7 +553,11 @@ impl CommandLine {
|
||||
let fade = self.fade_secs();
|
||||
let now = Instant::now();
|
||||
for entry in &mut self.history {
|
||||
if since.saturating_duration_since(entry.created_at).as_secs_f32() < fade {
|
||||
if since
|
||||
.saturating_duration_since(entry.created_at)
|
||||
.as_secs_f32()
|
||||
< fade
|
||||
{
|
||||
entry.created_at = now;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-1
@@ -156,7 +156,9 @@ impl PanelId {
|
||||
pub fn icon(self) -> &'static [u8] {
|
||||
match self {
|
||||
PanelId::Properties => include_bytes!("../../assets/icons/properties.svg"),
|
||||
PanelId::GraphicAttributes => include_bytes!("../../assets/icons/graphic_attributes.svg"),
|
||||
PanelId::GraphicAttributes => {
|
||||
include_bytes!("../../assets/icons/graphic_attributes.svg")
|
||||
}
|
||||
PanelId::BlockPalette => include_bytes!("../../assets/icons/blocks/insert.svg"),
|
||||
PanelId::ExternalReferences => crate::ui::icons::FOLDER_OPEN,
|
||||
PanelId::Browser => include_bytes!("../../assets/icons/content_browser.svg"),
|
||||
|
||||
+1
-1
@@ -6,9 +6,9 @@ pub mod color_select;
|
||||
pub mod command_line;
|
||||
pub mod dock;
|
||||
pub mod icons;
|
||||
pub mod menu_bar;
|
||||
pub mod modal;
|
||||
pub mod node_graph;
|
||||
pub mod menu_bar;
|
||||
pub mod overlay;
|
||||
pub mod popup;
|
||||
pub mod properties;
|
||||
|
||||
+2
-1
@@ -1798,7 +1798,8 @@ impl canvas::Program<Message> for SelectionCanvas {
|
||||
},
|
||||
..canvas::Stroke::default().with_color(marker).with_width(1.0)
|
||||
};
|
||||
let tip = Point::new(sp.x + dx / len * 18.0, sp.y + dy / len * 18.0);
|
||||
let tip =
|
||||
Point::new(sp.x + dx / len * 18.0, sp.y + dy / len * 18.0);
|
||||
// `base` is a projected entity endpoint: clip before
|
||||
// stroking (see `clip_seg`).
|
||||
if let Some((a, b)) = clip_seg(base, tip, bounds) {
|
||||
|
||||
@@ -270,7 +270,11 @@ pub(super) fn keytip_items(id: &str) -> Vec<(&'static str, &'static str, bool)>
|
||||
.tools
|
||||
.iter()
|
||||
.filter(|tool| tool.command == id)
|
||||
.flat_map(|tool| tool.options.iter().map(|(cmd, _)| (tool.command, *cmd, false)))
|
||||
.flat_map(|tool| {
|
||||
tool.options
|
||||
.iter()
|
||||
.map(|(cmd, _)| (tool.command, *cmd, false))
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
|
||||
+67
-13
@@ -29,7 +29,9 @@ pub enum KeyTipInput {
|
||||
AltTap,
|
||||
/// Show the tab level; `refocus` returns the focus to the command line
|
||||
/// when the tips close.
|
||||
Open { refocus: bool },
|
||||
Open {
|
||||
refocus: bool,
|
||||
},
|
||||
/// One typed character (upper case).
|
||||
Char(String),
|
||||
/// Settle a code that also starts a longer one.
|
||||
@@ -104,7 +106,10 @@ pub fn active() -> bool {
|
||||
}
|
||||
|
||||
/// Record `el`'s bounds under `id` while key tips show.
|
||||
pub(super) fn anchor<'a>(id: &'static str, el: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
||||
pub(super) fn anchor<'a>(
|
||||
id: &'static str,
|
||||
el: impl Into<Element<'a, Message>>,
|
||||
) -> Element<'a, Message> {
|
||||
if active() {
|
||||
PosReport::new(id, el).into()
|
||||
} else {
|
||||
@@ -166,7 +171,11 @@ pub(super) fn collapsed_anchor(title: &str) -> String {
|
||||
|
||||
/// Keyboard listener hook for a key press. `Some` while key tips own the
|
||||
/// keyboard (the inner value is the message to send, if any).
|
||||
pub fn key_pressed(key: &Key, modifiers: keyboard::Modifiers, repeat: bool) -> Option<Option<Message>> {
|
||||
pub fn key_pressed(
|
||||
key: &Key,
|
||||
modifiers: keyboard::Modifiers,
|
||||
repeat: bool,
|
||||
) -> Option<Option<Message>> {
|
||||
let alt = matches!(key, Key::Named(Named::Alt));
|
||||
if !repeat {
|
||||
ALT_ALONE.store(
|
||||
@@ -607,7 +616,8 @@ impl Ribbon {
|
||||
.filter(|tip| tip.code.as_deref() == Some(typed))
|
||||
.min_by_key(|tip| rank(tip))
|
||||
};
|
||||
let longer = |typed: &str| codes().any(|code| code.len() > typed.len() && code.starts_with(typed));
|
||||
let longer =
|
||||
|typed: &str| codes().any(|code| code.len() > typed.len() && code.starts_with(typed));
|
||||
let typed = ch.map(|ch| format!("{}{ch}", state.typed));
|
||||
let chosen = match typed {
|
||||
Some(typed) if codes().any(|code| code.starts_with(&typed)) => {
|
||||
@@ -791,7 +801,9 @@ impl Ribbon {
|
||||
.map(|(position, (dropdown_id, cmd, submenu))| Tip {
|
||||
anchor: item_anchor(dropdown_id, cmd),
|
||||
target: cmd,
|
||||
code: Some(code(open, cmd).map_or_else(|| (position + 1).to_string(), str::to_string)),
|
||||
code: Some(
|
||||
code(open, cmd).map_or_else(|| (position + 1).to_string(), str::to_string),
|
||||
),
|
||||
action: if submenu {
|
||||
Action::Open(Message::ToggleRibbonDropdown(cmd.to_string()))
|
||||
} else {
|
||||
@@ -1103,7 +1115,9 @@ impl overlay::Overlay<Message, Theme, Renderer> for KeyTipOverlay<'_> {
|
||||
let free = |rect: &Rectangle, blocked: &[Rectangle]| {
|
||||
// Touching edges do not count.
|
||||
!blocked.iter().any(|other| {
|
||||
other.intersection(rect).is_some_and(|r| r.width > 1.0 && r.height > 1.0)
|
||||
other
|
||||
.intersection(rect)
|
||||
.is_some_and(|r| r.width > 1.0 && r.height > 1.0)
|
||||
})
|
||||
};
|
||||
let mut pills: Vec<(&String, &String, Rectangle)> = Vec::new();
|
||||
@@ -1125,7 +1139,11 @@ impl overlay::Overlay<Message, Theme, Renderer> for KeyTipOverlay<'_> {
|
||||
let Some(&first) = rects.first() else {
|
||||
continue;
|
||||
};
|
||||
let pill = rects.iter().copied().find(|rect| free(rect, &blocked)).unwrap_or(first);
|
||||
let pill = rects
|
||||
.iter()
|
||||
.copied()
|
||||
.find(|rect| free(rect, &blocked))
|
||||
.unwrap_or(first);
|
||||
blocked.push(pill);
|
||||
pills.push((anchor, code, pill));
|
||||
}
|
||||
@@ -1161,9 +1179,27 @@ impl overlay::Overlay<Message, Theme, Renderer> for KeyTipOverlay<'_> {
|
||||
..pill
|
||||
};
|
||||
if !done.is_empty() {
|
||||
draw_text(renderer, done, Point::new(left, pill.center_y() - 0.5), PILL_TEXT, font, ink.scale_alpha(0.45), false, clip);
|
||||
draw_text(
|
||||
renderer,
|
||||
done,
|
||||
Point::new(left, pill.center_y() - 0.5),
|
||||
PILL_TEXT,
|
||||
font,
|
||||
ink.scale_alpha(0.45),
|
||||
false,
|
||||
clip,
|
||||
);
|
||||
}
|
||||
draw_text(renderer, rest, Point::new(left + done_w, pill.center_y() - 0.5), PILL_TEXT, font, ink, false, clip);
|
||||
draw_text(
|
||||
renderer,
|
||||
rest,
|
||||
Point::new(left + done_w, pill.center_y() - 0.5),
|
||||
PILL_TEXT,
|
||||
font,
|
||||
ink,
|
||||
false,
|
||||
clip,
|
||||
);
|
||||
shown.push(anchor.clone());
|
||||
}
|
||||
});
|
||||
@@ -1210,7 +1246,16 @@ fn draw_bar(renderer: &mut Renderer, theme: &Theme, parts: &[String; 3]) {
|
||||
1 => (regular, ink),
|
||||
_ => (regular, ink.scale_alpha(0.6)),
|
||||
};
|
||||
draw_text(renderer, part, Point::new(x, y), BAR_TEXT, part_font, color, false, bar);
|
||||
draw_text(
|
||||
renderer,
|
||||
part,
|
||||
Point::new(x, y),
|
||||
BAR_TEXT,
|
||||
part_font,
|
||||
color,
|
||||
false,
|
||||
bar,
|
||||
);
|
||||
x += text_width(part, BAR_TEXT, part_font) + 18.0;
|
||||
}
|
||||
}
|
||||
@@ -1239,13 +1284,19 @@ fn pill_candidates(
|
||||
let left_corner = |x: f32, y: f32| Point::new(x - width / 2.0, y + PILL_H / 2.0);
|
||||
let label = fresh_bounds(&label_id(anchor));
|
||||
if anchor.starts_with("tab:") {
|
||||
return vec![Point::new(b.center_x(), b.y + b.height - 4.0 + PILL_H / 2.0)];
|
||||
return vec![Point::new(
|
||||
b.center_x(),
|
||||
b.y + b.height - 4.0 + PILL_H / 2.0,
|
||||
)];
|
||||
}
|
||||
if anchor.starts_with("qa:")
|
||||
|| anchor == super::widgets::UNDO_HISTORY_ID
|
||||
|| anchor == super::widgets::REDO_HISTORY_ID
|
||||
{
|
||||
return vec![corner(b.x + 14.0, b.center_y()), Point::new(b.x + 14.0, b.center_y())];
|
||||
return vec![
|
||||
corner(b.x + 14.0, b.center_y()),
|
||||
Point::new(b.x + 14.0, b.center_y()),
|
||||
];
|
||||
}
|
||||
if anchor == XREF_LAUNCHER {
|
||||
return vec![
|
||||
@@ -1291,7 +1342,10 @@ fn pill_candidates(
|
||||
let mut spots = Vec::new();
|
||||
if dropdown {
|
||||
// The arrow strip lies under the label.
|
||||
spots.push(Point::new(b.center_x(), b.y + b.height - super::widgets::LARGE_ARR / 2.0));
|
||||
spots.push(Point::new(
|
||||
b.center_x(),
|
||||
b.y + b.height - super::widgets::LARGE_ARR / 2.0,
|
||||
));
|
||||
}
|
||||
spots.extend([
|
||||
Point::new(b.center_x(), label.y + label.height + 1.0 + PILL_H / 2.0),
|
||||
|
||||
@@ -30,17 +30,17 @@ pub use context_tools::{pdf_underlay_tools, point_cloud_tools, xref_tools, Under
|
||||
use widgets::{StyleContext, *};
|
||||
pub(crate) use widgets::{REDO_HISTORY_ID, UNDO_HISTORY_ID};
|
||||
mod collapse;
|
||||
mod locate;
|
||||
mod keytips;
|
||||
mod locate;
|
||||
use crate::t;
|
||||
use crate::ui::wrap_bar::{PosReport, WrapBar, WrapFlow};
|
||||
pub use collapse::CollapseMode;
|
||||
use collapse::{CollapsePanels, Panel};
|
||||
pub use keytips::{
|
||||
active as keytips_active, key_pressed as keytip_key_pressed,
|
||||
key_released as keytip_key_released, pointer_pressed as keytip_pointer_pressed, KeyTipInput,
|
||||
KeyTipStep,
|
||||
};
|
||||
use crate::t;
|
||||
use crate::ui::wrap_bar::{PosReport, WrapBar, WrapFlow};
|
||||
pub use collapse::CollapseMode;
|
||||
use collapse::{CollapsePanels, Panel};
|
||||
|
||||
pub(crate) fn tooltip_content(text: String) -> Element<'static, Message> {
|
||||
widgets::make_tip(text)
|
||||
@@ -337,7 +337,10 @@ impl Ribbon {
|
||||
/// Show the Block Editor tab while a block is being edited, and return to
|
||||
/// the previous tab when the editing ends.
|
||||
pub fn set_block_editing(&mut self, editing: bool) {
|
||||
let Some(index) = self.modules.iter().position(|module| module.id() == "block_editor")
|
||||
let Some(index) = self
|
||||
.modules
|
||||
.iter()
|
||||
.position(|module| module.id() == "block_editor")
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
@@ -1103,8 +1103,8 @@ pub(super) fn render_large<'a>(item: &RibbonItem, ctx: &RenderCtx<'_>) -> Elemen
|
||||
|| super::keytips::label_id(LAYER_COMBO_ID),
|
||||
text(active_layer_label).size(11),
|
||||
))
|
||||
.width(name_w)
|
||||
.clip(true),
|
||||
.width(name_w)
|
||||
.clip(true),
|
||||
icons::themed_arrow_down(9.0),
|
||||
]
|
||||
.spacing(4)
|
||||
@@ -1253,8 +1253,8 @@ pub(super) fn render_large<'a>(item: &RibbonItem, ctx: &RenderCtx<'_>) -> Elemen
|
||||
|| super::keytips::label_id(combo_id),
|
||||
text(active.clone()).size(11),
|
||||
))
|
||||
.width(Fill)
|
||||
.clip(true),
|
||||
.width(Fill)
|
||||
.clip(true),
|
||||
if is_open {
|
||||
icons::themed_arrow_up(9.0)
|
||||
} else {
|
||||
|
||||
+35
-20
@@ -72,24 +72,30 @@ pub fn refedit_header(name: &str, count: usize) -> Element<'static, Message> {
|
||||
..Default::default()
|
||||
}
|
||||
});
|
||||
let tip = container(text(name.to_string()).size(11)).padding([2, 6]).style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.strong.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
text_color: Some(palette.background.strong.text),
|
||||
..Default::default()
|
||||
}
|
||||
});
|
||||
let tip = container(text(name.to_string()).size(11))
|
||||
.padding([2, 6])
|
||||
.style(|theme: &Theme| {
|
||||
let palette = theme.palette();
|
||||
container::Style {
|
||||
background: Some(Background::Color(palette.background.strong.color)),
|
||||
border: Border {
|
||||
color: palette.background.neutral.color,
|
||||
width: 1.0,
|
||||
radius: 3.0.into(),
|
||||
},
|
||||
text_color: Some(palette.background.strong.text),
|
||||
..Default::default()
|
||||
}
|
||||
});
|
||||
column![
|
||||
tooltip(pill, tip, tooltip::Position::Left).gap(6),
|
||||
text(format!("{} {}", count, crate::tr!("ref-edit", "toolbar-objects")))
|
||||
.size(9)
|
||||
.style(crate::ui::style::common::muted_style),
|
||||
text(format!(
|
||||
"{} {}",
|
||||
count,
|
||||
crate::tr!("ref-edit", "toolbar-objects")
|
||||
))
|
||||
.size(9)
|
||||
.style(crate::ui::style::common::muted_style),
|
||||
]
|
||||
.spacing(2)
|
||||
.align_x(iced::Center)
|
||||
@@ -126,9 +132,16 @@ pub fn view_sections(
|
||||
if let Some(header) = header {
|
||||
col = col.push(header);
|
||||
}
|
||||
let tools = groups.iter().filter(|g| !g.is_empty()).enumerate().flat_map(|(group, tools)| {
|
||||
tools.iter().enumerate().map(move |(k, t)| (group > 0 && k == 0, t))
|
||||
});
|
||||
let tools = groups
|
||||
.iter()
|
||||
.filter(|g| !g.is_empty())
|
||||
.enumerate()
|
||||
.flat_map(|(group, tools)| {
|
||||
tools
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(move |(k, t)| (group > 0 && k == 0, t))
|
||||
});
|
||||
for (starts_group, t) in tools {
|
||||
if starts_group {
|
||||
col = col.push(
|
||||
@@ -136,7 +149,9 @@ pub fn view_sections(
|
||||
.width(Length::Fixed(BTN_SIZE - 4.0))
|
||||
.height(Length::Fixed(1.0))
|
||||
.style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.neutral.color)),
|
||||
background: Some(Background::Color(
|
||||
theme.palette().background.neutral.color,
|
||||
)),
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -221,10 +221,14 @@ pub fn view_window<'a>(
|
||||
.style(button::secondary)]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![add, button(text(t!("Import PGP…")).size(12))
|
||||
.on_press_maybe((!pending_add).then_some(Message::AliasEditorImport))
|
||||
.padding([4, 10])
|
||||
.style(button::secondary)].spacing(8),
|
||||
row![
|
||||
add,
|
||||
button(text(t!("Import PGP…")).size(12))
|
||||
.on_press_maybe((!pending_add).then_some(Message::AliasEditorImport))
|
||||
.padding([4, 10])
|
||||
.style(button::secondary)
|
||||
]
|
||||
.spacing(8),
|
||||
)
|
||||
};
|
||||
// Apply — primary action; commits the rows to ocad.pgp and stays open.
|
||||
@@ -276,12 +280,7 @@ pub fn view_window<'a>(
|
||||
Space::new().height(6),
|
||||
scrollable(conflict_banner).height(Length::Fit.max(72)),
|
||||
Space::new().height(4),
|
||||
row![
|
||||
add_area,
|
||||
reset_area,
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
row![add_area, reset_area,].spacing(8).align_y(iced::Center),
|
||||
row![stats, Space::new().width(Length::Fill), apply_area]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
|
||||
@@ -904,9 +904,14 @@ pub fn data_extraction_view(
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
let add_folder = iced::widget::tooltip(
|
||||
add_folder,
|
||||
container(text(t!("Adding folders is not available in the browser. Add drawing files instead.")).size(11))
|
||||
.padding([3, 6])
|
||||
.style(container::bordered_box),
|
||||
container(
|
||||
text(t!(
|
||||
"Adding folders is not available in the browser. Add drawing files instead."
|
||||
))
|
||||
.size(11),
|
||||
)
|
||||
.padding([3, 6])
|
||||
.style(container::bordered_box),
|
||||
iced::widget::tooltip::Position::Top,
|
||||
)
|
||||
.gap(4);
|
||||
|
||||
@@ -664,7 +664,11 @@ pub fn view_window<'a>(
|
||||
.align_x(iced::alignment::Horizontal::Center),
|
||||
Space::new().height(16),
|
||||
row![
|
||||
dialog_button(t!("Redefine"), Message::BlockDefConfirmRedefine(true), false),
|
||||
dialog_button(
|
||||
t!("Redefine"),
|
||||
Message::BlockDefConfirmRedefine(true),
|
||||
false
|
||||
),
|
||||
// Default / safe focus on "No"
|
||||
dialog_button(t!("No"), Message::BlockDefConfirmRedefine(false), true),
|
||||
]
|
||||
|
||||
@@ -31,7 +31,16 @@ pub struct GradientEditorState {
|
||||
preview: std::cell::RefCell<Option<(PreviewKey, Option<iced::widget::image::Handle>)>>,
|
||||
}
|
||||
|
||||
type PreviewKey = (GradientColorMode, AcadColor, AcadColor, u32, GradientKind, bool, String, bool);
|
||||
type PreviewKey = (
|
||||
GradientColorMode,
|
||||
AcadColor,
|
||||
AcadColor,
|
||||
u32,
|
||||
GradientKind,
|
||||
bool,
|
||||
String,
|
||||
bool,
|
||||
);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum GradientMsg {
|
||||
@@ -54,10 +63,7 @@ fn msg(message: GradientMsg) -> Message {
|
||||
}
|
||||
|
||||
impl GradientEditorState {
|
||||
pub fn from_hatch(
|
||||
handles: Vec<codec::Handle>,
|
||||
hatch: &codec::entities::Hatch,
|
||||
) -> Self {
|
||||
pub fn from_hatch(handles: Vec<codec::Handle>, hatch: &codec::entities::Hatch) -> Self {
|
||||
let color = |index: usize| {
|
||||
hatch
|
||||
.gradient_color
|
||||
@@ -124,9 +130,7 @@ impl GradientEditorState {
|
||||
fn rgb(color: &AcadColor) -> Color {
|
||||
let (r, g, b) = match color {
|
||||
AcadColor::Rgb { r, g, b } => (*r, *g, *b),
|
||||
AcadColor::Index(i) => {
|
||||
codec::types::aci_table::aci_to_rgb(*i).unwrap_or((128, 128, 128))
|
||||
}
|
||||
AcadColor::Index(i) => codec::types::aci_table::aci_to_rgb(*i).unwrap_or((128, 128, 128)),
|
||||
_ => (255, 255, 255),
|
||||
};
|
||||
Color::from_rgb8(r, g, b)
|
||||
@@ -192,7 +196,9 @@ fn preview_sized(
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(iced::widget::image::Handle::from_rgba(width, height, pixels))
|
||||
Some(iced::widget::image::Handle::from_rgba(
|
||||
width, height, pixels,
|
||||
))
|
||||
}
|
||||
|
||||
fn preview(state: &GradientEditorState) -> Element<'static, Message> {
|
||||
@@ -219,7 +225,9 @@ pub(crate) fn compact_preview(
|
||||
preview_sized(&GradientEditorState::from_hatch(Vec::new(), hatch), 256, 26)
|
||||
}
|
||||
|
||||
pub(crate) fn preview_image(handle: Option<iced::widget::image::Handle>) -> Element<'static, Message> {
|
||||
pub(crate) fn preview_image(
|
||||
handle: Option<iced::widget::image::Handle>,
|
||||
) -> Element<'static, Message> {
|
||||
match handle {
|
||||
Some(handle) => image(handle)
|
||||
.width(Fill)
|
||||
@@ -357,20 +365,20 @@ pub fn view(state: &GradientEditorState, dimmed: bool) -> Element<'_, Message> {
|
||||
));
|
||||
}
|
||||
let invert_selected = state.inverted && state.kind != GradientKind::Linear;
|
||||
let invert = button(column![text("⇄").size(18), text(crate::t!("Invert")).size(9)].align_x(iced::Center))
|
||||
.on_press_maybe(
|
||||
(state.kind != GradientKind::Linear).then(|| msg(GradientMsg::InvertToggled)),
|
||||
)
|
||||
.width(42)
|
||||
.height(48)
|
||||
.style(move |theme: &Theme, status| {
|
||||
let mut style = button::secondary(theme, status);
|
||||
if invert_selected {
|
||||
style.border.color = theme.palette().primary.base.color;
|
||||
style.border.width = 1.0;
|
||||
}
|
||||
style
|
||||
});
|
||||
let invert = button(
|
||||
column![text("⇄").size(18), text(crate::t!("Invert")).size(9)].align_x(iced::Center),
|
||||
)
|
||||
.on_press_maybe((state.kind != GradientKind::Linear).then(|| msg(GradientMsg::InvertToggled)))
|
||||
.width(42)
|
||||
.height(48)
|
||||
.style(move |theme: &Theme, status| {
|
||||
let mut style = button::secondary(theme, status);
|
||||
if invert_selected {
|
||||
style.border.color = theme.palette().primary.base.color;
|
||||
style.border.width = 1.0;
|
||||
}
|
||||
style
|
||||
});
|
||||
let separator =
|
||||
container(Space::new().width(1).height(36)).style(|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().background.neutral.color)),
|
||||
|
||||
@@ -10,8 +10,7 @@ use codec::entities::{EntityCommon, Hatch, HatchGradientPattern};
|
||||
use codec::types::{Color as AcadColor, LineWeight, Transparency};
|
||||
use codec::{CadDocument, EntityType, Handle};
|
||||
use iced::widget::{
|
||||
button, image, column, combo_box, container, row, slider, text, text_input, tooltip,
|
||||
Space,
|
||||
button, column, combo_box, container, image, row, slider, text, text_input, tooltip, Space,
|
||||
};
|
||||
use iced::{Background, Border, Element, Fill, Length, Padding, Theme};
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
@@ -171,7 +170,11 @@ impl GraphicAttributesState {
|
||||
|
||||
/// The fills and fill kind of `selected`, from an index of associative
|
||||
/// fills that is rebuilt only when the scene's geometry changes.
|
||||
pub(crate) fn fills(&self, scene: &Scene, selected: &[Handle]) -> (Vec<Handle>, GraphicAttribute) {
|
||||
pub(crate) fn fills(
|
||||
&self,
|
||||
scene: &Scene,
|
||||
selected: &[Handle],
|
||||
) -> (Vec<Handle>, GraphicAttribute) {
|
||||
// Geometry epochs are unique across all scenes, so the epoch alone
|
||||
// identifies both the tab and the state of its drawing.
|
||||
let key = scene.geometry_epoch;
|
||||
@@ -213,13 +216,25 @@ impl GraphicAttributesState {
|
||||
let same = |handles: &[Handle]| {
|
||||
handles.len() == selected.len() && selected.iter().all(|h| handles.contains(h))
|
||||
};
|
||||
if self.gradient_editor.as_ref().is_some_and(|e| !same(&e.handles)) {
|
||||
if self
|
||||
.gradient_editor
|
||||
.as_ref()
|
||||
.is_some_and(|e| !same(&e.handles))
|
||||
{
|
||||
self.gradient_editor = None;
|
||||
}
|
||||
if self.hatch_editor.as_ref().is_some_and(|e| !same(&e.handles)) {
|
||||
if self
|
||||
.hatch_editor
|
||||
.as_ref()
|
||||
.is_some_and(|e| !same(&e.handles))
|
||||
{
|
||||
self.hatch_editor = None;
|
||||
}
|
||||
if self.pending_fill_close.as_ref().is_some_and(|p| !same(&p.selection)) {
|
||||
if self
|
||||
.pending_fill_close
|
||||
.as_ref()
|
||||
.is_some_and(|p| !same(&p.selection))
|
||||
{
|
||||
self.pending_fill_close = None;
|
||||
}
|
||||
}
|
||||
@@ -227,8 +242,10 @@ impl GraphicAttributesState {
|
||||
pub fn fill_close_prompt(&self) -> Option<String> {
|
||||
let count = self.pending_fill_close.as_ref()?.open_boundaries.len();
|
||||
Some(if count == 1 {
|
||||
crate::t!("The selected object is open. Close it before creating the fill? [Yes / No] <Yes>")
|
||||
.into_owned()
|
||||
crate::t!(
|
||||
"The selected object is open. Close it before creating the fill? [Yes / No] <Yes>"
|
||||
)
|
||||
.into_owned()
|
||||
} else {
|
||||
crate::tf!(
|
||||
"{count} selected objects are open. Close them before creating the fill? [Yes / No] <Yes>"
|
||||
@@ -367,7 +384,9 @@ fn shared_value<'a, T: PartialEq>(
|
||||
draw_depth: &DrawDepth,
|
||||
value: impl Fn(&EntityCommon) -> T,
|
||||
) -> Option<(&'a EntityCommon, T, bool)> {
|
||||
let top = document.get_entity(top_handle(handles, draw_depth)?)?.common();
|
||||
let top = document
|
||||
.get_entity(top_handle(handles, draw_depth)?)?
|
||||
.common();
|
||||
let shared = value(top);
|
||||
let varies = handles.iter().any(|handle| {
|
||||
document
|
||||
@@ -469,10 +488,13 @@ impl TransparencyInfo {
|
||||
}
|
||||
}
|
||||
|
||||
fn color_info(document: &CadDocument, handles: &[Handle], draw_depth: &DrawDepth) -> Option<ColorInfo> {
|
||||
fn color_info(
|
||||
document: &CadDocument,
|
||||
handles: &[Handle],
|
||||
draw_depth: &DrawDepth,
|
||||
) -> Option<ColorInfo> {
|
||||
let (top, color, varies) = shared_value(document, handles, draw_depth, |c| c.color)?;
|
||||
let rgba =
|
||||
crate::scene::view::render::render_style_for_common_viewport(document, top, None).0;
|
||||
let rgba = crate::scene::view::render::render_style_for_common_viewport(document, top, None).0;
|
||||
Some(ColorInfo {
|
||||
color,
|
||||
display: iced::Color::from_rgba(rgba[0], rgba[1], rgba[2], 1.0),
|
||||
@@ -576,8 +598,8 @@ fn linetype_preview_art(document: &CadDocument, linetype: &str) -> String {
|
||||
.filter(|character| matches!(character, '_' | '.' | '-' | '/' | '\\' | '|' | '~'))
|
||||
.count()
|
||||
>= 2)
|
||||
.then(|| art.find(part))
|
||||
.flatten()
|
||||
.then(|| art.find(part))
|
||||
.flatten()
|
||||
});
|
||||
let pattern = pattern_start.map_or(art.as_str(), |start| &art[start..]);
|
||||
if pattern.is_empty() {
|
||||
@@ -746,7 +768,10 @@ fn color_control<'a>(
|
||||
vec![
|
||||
("ByLayer".into(), msg(changed(AcadColor::ByLayer))),
|
||||
("ByBlock".into(), msg(changed(AcadColor::ByBlock))),
|
||||
(crate::t!("Custom…"), Message::OpenColorWindow(target, info.color)),
|
||||
(
|
||||
crate::t!("Custom…"),
|
||||
Message::OpenColorWindow(target, info.color),
|
||||
),
|
||||
],
|
||||
);
|
||||
row![swatch, more].spacing(4).align_y(iced::Center).into()
|
||||
@@ -791,7 +816,11 @@ fn transparency_control<'a>(
|
||||
row![control, more].spacing(4).align_y(iced::Center).into()
|
||||
}
|
||||
|
||||
fn header_menu(side: DockSide, open: bool, fill_on_current_layer: bool) -> Element<'static, Message> {
|
||||
fn header_menu(
|
||||
side: DockSide,
|
||||
open: bool,
|
||||
fill_on_current_layer: bool,
|
||||
) -> Element<'static, Message> {
|
||||
let menu_button = button(crate::ui::icons::themed_secondary(
|
||||
crate::ui::icons::MENU,
|
||||
12.0,
|
||||
@@ -817,10 +846,22 @@ fn header_menu(side: DockSide, open: bool, fill_on_current_layer: bool) -> Eleme
|
||||
.padding([4, 8])
|
||||
};
|
||||
let popup = container(column![
|
||||
item("Set all attributes ByLayer", GraphicAttributesMsg::SetAllByLayer),
|
||||
item("Set all attributes ByBlock", GraphicAttributesMsg::SetAllByBlock),
|
||||
item("Remove ByLayer / ByBlock", GraphicAttributesMsg::RemoveReferences),
|
||||
item("Create Layer with active settings", GraphicAttributesMsg::CreateLayer),
|
||||
item(
|
||||
"Set all attributes ByLayer",
|
||||
GraphicAttributesMsg::SetAllByLayer
|
||||
),
|
||||
item(
|
||||
"Set all attributes ByBlock",
|
||||
GraphicAttributesMsg::SetAllByBlock
|
||||
),
|
||||
item(
|
||||
"Remove ByLayer / ByBlock",
|
||||
GraphicAttributesMsg::RemoveReferences
|
||||
),
|
||||
item(
|
||||
"Create Layer with active settings",
|
||||
GraphicAttributesMsg::CreateLayer
|
||||
),
|
||||
container(iced::widget::rule::horizontal(1)).padding([2, 4]),
|
||||
container(
|
||||
iced::widget::checkbox(fill_on_current_layer)
|
||||
@@ -944,7 +985,11 @@ fn preview_row<'a>(
|
||||
})
|
||||
.height(COMPACT_BUTTON_HEIGHT)
|
||||
.padding([1, 3]);
|
||||
let edit = tooltip(edit, text(tooltip_label).size(10), tooltip::Position::Bottom);
|
||||
let edit = tooltip(
|
||||
edit,
|
||||
text(tooltip_label).size(10),
|
||||
tooltip::Position::Bottom,
|
||||
);
|
||||
let edit: Element<'a, Message> = match flyout {
|
||||
Some((popup, width, height)) => {
|
||||
let mut drop_down = iced_aw::DropDown::new(edit, popup, true)
|
||||
@@ -1011,8 +1056,14 @@ fn linetype_row<'a>(
|
||||
open_menu,
|
||||
COMPACT_BUTTON_HEIGHT,
|
||||
vec![
|
||||
("ByLayer".into(), msg(GraphicAttributesMsg::LineLinetype("ByLayer".into()))),
|
||||
("ByBlock".into(), msg(GraphicAttributesMsg::LineLinetype("ByBlock".into()))),
|
||||
(
|
||||
"ByLayer".into(),
|
||||
msg(GraphicAttributesMsg::LineLinetype("ByLayer".into())),
|
||||
),
|
||||
(
|
||||
"ByBlock".into(),
|
||||
msg(GraphicAttributesMsg::LineLinetype("ByBlock".into())),
|
||||
),
|
||||
],
|
||||
);
|
||||
row![crate::ui::wide_menu::wide_menu(combo, 220.0), more]
|
||||
@@ -1108,8 +1159,14 @@ fn lineweight_field<'a>(
|
||||
open_menu,
|
||||
COMPACT_BUTTON_HEIGHT,
|
||||
vec![
|
||||
("ByLayer".into(), msg(GraphicAttributesMsg::LineLineweight(LineWeight::ByLayer))),
|
||||
("ByBlock".into(), msg(GraphicAttributesMsg::LineLineweight(LineWeight::ByBlock))),
|
||||
(
|
||||
"ByLayer".into(),
|
||||
msg(GraphicAttributesMsg::LineLineweight(LineWeight::ByLayer)),
|
||||
),
|
||||
(
|
||||
"ByBlock".into(),
|
||||
msg(GraphicAttributesMsg::LineLineweight(LineWeight::ByBlock)),
|
||||
),
|
||||
],
|
||||
);
|
||||
row![
|
||||
@@ -1142,15 +1199,22 @@ fn line_section<'a>(
|
||||
}
|
||||
let layer = top_layer(document, handles, draw_depth).to_string();
|
||||
let header = &document.header;
|
||||
let (linetype, linetype_varies) = shared_value(document, handles, draw_depth, |c| {
|
||||
c.linetype.clone()
|
||||
})
|
||||
.map_or_else(
|
||||
|| (current_linetype_name(document).to_string(), false),
|
||||
|(_, value, varies)| (if value.is_empty() { "ByLayer".into() } else { value }, varies),
|
||||
);
|
||||
let (scale, scale_varies) =
|
||||
shared_value(document, handles, draw_depth, |c| c.linetype_scale).map_or(
|
||||
let (linetype, linetype_varies) =
|
||||
shared_value(document, handles, draw_depth, |c| c.linetype.clone()).map_or_else(
|
||||
|| (current_linetype_name(document).to_string(), false),
|
||||
|(_, value, varies)| {
|
||||
(
|
||||
if value.is_empty() {
|
||||
"ByLayer".into()
|
||||
} else {
|
||||
value
|
||||
},
|
||||
varies,
|
||||
)
|
||||
},
|
||||
);
|
||||
let (scale, scale_varies) = shared_value(document, handles, draw_depth, |c| c.linetype_scale)
|
||||
.map_or(
|
||||
(header.current_entity_linetype_scale, false),
|
||||
|(_, value, varies)| (value, varies),
|
||||
);
|
||||
@@ -1159,10 +1223,15 @@ fn line_section<'a>(
|
||||
(LineWeight::from_value(header.current_line_weight), false),
|
||||
|(_, value, varies)| (value, varies),
|
||||
);
|
||||
let color = color_info(document, handles, draw_depth)
|
||||
.unwrap_or_else(|| current_color_info(document));
|
||||
let color =
|
||||
color_info(document, handles, draw_depth).unwrap_or_else(|| current_color_info(document));
|
||||
let transparency = transparency_info(document, handles, draw_depth).unwrap_or_else(|| {
|
||||
TransparencyInfo::new(document, &layer, document.current_entity_transparency(), false)
|
||||
TransparencyInfo::new(
|
||||
document,
|
||||
&layer,
|
||||
document.current_entity_transparency(),
|
||||
false,
|
||||
)
|
||||
});
|
||||
let continuous =
|
||||
!linetype_varies && is_continuous(effective_linetype(document, &layer, &linetype));
|
||||
|
||||
@@ -737,7 +737,6 @@ fn name_tip<'a>(name: &'a str) -> Element<'a, Message> {
|
||||
.into()
|
||||
}
|
||||
|
||||
|
||||
/// The layer-name field while a rename is open; a click that moves focus
|
||||
/// elsewhere commits the typed name.
|
||||
pub fn rename_input_id() -> iced::widget::Id {
|
||||
|
||||
@@ -6,8 +6,8 @@ pub mod attribute_editor;
|
||||
pub mod auto_constrain_settings;
|
||||
pub mod block_definition;
|
||||
pub mod block_edit_picker;
|
||||
pub mod block_save_as;
|
||||
pub mod block_palette;
|
||||
pub mod block_save_as;
|
||||
pub mod browser;
|
||||
pub mod count_palette;
|
||||
pub mod drafting_settings;
|
||||
|
||||
@@ -1810,11 +1810,9 @@ pub fn view_window<'a>(
|
||||
let files = column![
|
||||
text(crate::t!("Files")).size(15),
|
||||
Space::new().height(6),
|
||||
text(crate::t!(
|
||||
"Where the application keeps its own files."
|
||||
))
|
||||
.size(11)
|
||||
.width(sizing.width),
|
||||
text(crate::t!("Where the application keeps its own files."))
|
||||
.size(11)
|
||||
.width(sizing.width),
|
||||
Space::new().height(16),
|
||||
folder_row(crate::t!("Configuration"), folders.config.clone(), None),
|
||||
Space::new().height(12),
|
||||
|
||||
+212
-87
@@ -312,7 +312,10 @@ pub(crate) fn well_style(theme: &Theme) -> container::Style {
|
||||
}
|
||||
|
||||
/// A titled card.
|
||||
pub(crate) fn card<'a>(title: String, content: impl Into<Element<'a, Message>>) -> Element<'a, Message> {
|
||||
pub(crate) fn card<'a>(
|
||||
title: String,
|
||||
content: impl Into<Element<'a, Message>>,
|
||||
) -> Element<'a, Message> {
|
||||
container(
|
||||
column![
|
||||
text(title.to_uppercase()).size(10).style(accent_text),
|
||||
@@ -2212,12 +2215,27 @@ pub struct BgPreview {
|
||||
pub fn bg_preview(path: &str) -> Option<BgPreview> {
|
||||
let decoded = ::image::open(path.trim()).ok()?;
|
||||
let size = [decoded.width(), decoded.height()];
|
||||
let small = if size[0].max(size[1]) > 512 { decoded.thumbnail(512, 512) } else { decoded }.to_rgba8();
|
||||
let handle = |pixels: ::image::RgbaImage| image::Handle::from_rgba(pixels.width(), pixels.height(), pixels.into_raw());
|
||||
let small = if size[0].max(size[1]) > 512 {
|
||||
decoded.thumbnail(512, 512)
|
||||
} else {
|
||||
decoded
|
||||
}
|
||||
.to_rgba8();
|
||||
let handle = |pixels: ::image::RgbaImage| {
|
||||
image::Handle::from_rgba(pixels.width(), pixels.height(), pixels.into_raw())
|
||||
};
|
||||
let flip_x = ::image::imageops::flip_horizontal(&small);
|
||||
let flip_y = ::image::imageops::flip_vertical(&small);
|
||||
let flip_xy = ::image::imageops::flip_vertical(&flip_x);
|
||||
Some(BgPreview { size, handles: [handle(small), handle(flip_x), handle(flip_y), handle(flip_xy)] })
|
||||
Some(BgPreview {
|
||||
size,
|
||||
handles: [
|
||||
handle(small),
|
||||
handle(flip_x),
|
||||
handle(flip_y),
|
||||
handle(flip_xy),
|
||||
],
|
||||
})
|
||||
}
|
||||
|
||||
/// Image offsets the reference's Adjust Image sliders reach, in pixels.
|
||||
@@ -2325,7 +2343,11 @@ pub fn view_background<'a>(
|
||||
if state.position == BgPosition::Stretch {
|
||||
// Filling the view; keeping the aspect ratio it covers
|
||||
// the view and the overflow is cut off.
|
||||
let fit = if state.aspect { iced::ContentFit::Cover } else { iced::ContentFit::Fill };
|
||||
let fit = if state.aspect {
|
||||
iced::ContentFit::Cover
|
||||
} else {
|
||||
iced::ContentFit::Fill
|
||||
};
|
||||
let handle = state.preview.as_ref().map_or_else(
|
||||
|| image::Handle::from_path(state.image.trim()),
|
||||
|preview| preview.handles[0].clone(),
|
||||
@@ -2338,13 +2360,26 @@ pub fn view_background<'a>(
|
||||
} else {
|
||||
// The image at its size times its scale, moved by its
|
||||
// offset, in a view of the drawing viewport's shape.
|
||||
let parse = |v: &String, d: f64| v.trim().parse::<f64>().ok().filter(|v| v.is_finite()).unwrap_or(d);
|
||||
let parse = |v: &String, d: f64| {
|
||||
v.trim()
|
||||
.parse::<f64>()
|
||||
.ok()
|
||||
.filter(|v| v.is_finite())
|
||||
.unwrap_or(d)
|
||||
};
|
||||
let offset = [parse(&state.offset[0], 0.0), parse(&state.offset[1], 0.0)];
|
||||
let scale = [0, 1].map(|k| {
|
||||
let v = parse(&state.scale[k], state.scale_base[k]);
|
||||
if v.abs() < 1e-9 { state.scale_base[k] } else { v }
|
||||
if v.abs() < 1e-9 {
|
||||
state.scale_base[k]
|
||||
} else {
|
||||
v
|
||||
}
|
||||
});
|
||||
let [iw, ih] = state.preview.as_ref().map_or([1, 1], |preview| preview.size);
|
||||
let [iw, ih] = state
|
||||
.preview
|
||||
.as_ref()
|
||||
.map_or([1, 1], |preview| preview.size);
|
||||
// A negative scale mirrors the image.
|
||||
let mirrored = usize::from(scale[0] < 0.0) | usize::from(scale[1] < 0.0) << 1;
|
||||
let handle = state.preview.as_ref().map_or_else(
|
||||
@@ -2363,7 +2398,10 @@ pub fn view_background<'a>(
|
||||
let x = size.width / 2.0 + offset[0] as f32 * factor - w / 2.0;
|
||||
let y = size.height / 2.0 + offset[1] as f32 * factor - h / 2.0;
|
||||
let (cols, rows) = if tile {
|
||||
((size.width / w).ceil() as i32 + 1, (size.height / h).ceil() as i32 + 1)
|
||||
(
|
||||
(size.width / w).ceil() as i32 + 1,
|
||||
(size.height / h).ceil() as i32 + 1,
|
||||
)
|
||||
} else {
|
||||
(0, 0)
|
||||
};
|
||||
@@ -2375,7 +2413,10 @@ pub fn view_background<'a>(
|
||||
let y0 = if tile { y - (y / h).ceil() * h } else { y };
|
||||
for row_index in 0..=rows.min(60) {
|
||||
for col_index in 0..=cols.min(60) {
|
||||
let at = iced::Point::new(x0 + col_index as f32 * w, y0 + row_index as f32 * h);
|
||||
let at = iced::Point::new(
|
||||
x0 + col_index as f32 * w,
|
||||
y0 + row_index as f32 * h,
|
||||
);
|
||||
layers = layers.push(
|
||||
iced::widget::pin(
|
||||
image(handle.clone())
|
||||
@@ -2398,7 +2439,9 @@ pub fn view_background<'a>(
|
||||
} else {
|
||||
container(note(crate::tr!("view-background", "no-image")))
|
||||
.center(Fill)
|
||||
.style(fill(Background::Color(iced::Color::from_rgb8(0x1a, 0x1a, 0x1a))))
|
||||
.style(fill(Background::Color(iced::Color::from_rgb8(
|
||||
0x1a, 0x1a, 0x1a,
|
||||
))))
|
||||
.into()
|
||||
}
|
||||
}
|
||||
@@ -2421,26 +2464,27 @@ pub fn view_background<'a>(
|
||||
})
|
||||
.width(Length::Fixed(92.0))
|
||||
};
|
||||
let color_row = |name: String, slot: u8, color: [u8; 3], enabled: bool| -> Element<'a, Message> {
|
||||
let mut pick = button(text(crate::tr!("view-background", "select")).size(11))
|
||||
.style(button_style(false))
|
||||
.padding([4, 10]);
|
||||
if enabled {
|
||||
pick = pick.on_press(msg(PdfDialogMsg::BgPickColor(slot)));
|
||||
}
|
||||
row![
|
||||
label(name, enabled),
|
||||
swatch(color, 64.0, 22.0),
|
||||
text(format!("{},{},{}", color[0], color[1], color[2]))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
Space::new().width(Fill),
|
||||
pick,
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
};
|
||||
let color_row =
|
||||
|name: String, slot: u8, color: [u8; 3], enabled: bool| -> Element<'a, Message> {
|
||||
let mut pick = button(text(crate::tr!("view-background", "select")).size(11))
|
||||
.style(button_style(false))
|
||||
.padding([4, 10]);
|
||||
if enabled {
|
||||
pick = pick.on_press(msg(PdfDialogMsg::BgPickColor(slot)));
|
||||
}
|
||||
row![
|
||||
label(name, enabled),
|
||||
swatch(color, 64.0, 22.0),
|
||||
text(format!("{},{},{}", color[0], color[1], color[2]))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
Space::new().width(Fill),
|
||||
pick,
|
||||
]
|
||||
.spacing(8)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
};
|
||||
let options: Element<'a, Message> = match state.kind {
|
||||
BgKind::None => card(
|
||||
crate::tr!("view-background", "info"),
|
||||
@@ -2473,8 +2517,18 @@ pub fn view_background<'a>(
|
||||
.spacing(8)
|
||||
.align_y(iced::Center),
|
||||
color_row(crate::tr!("view-background", "top"), 1, state.top, true),
|
||||
color_row(crate::tr!("view-background", "middle"), 2, state.middle, state.three),
|
||||
color_row(crate::tr!("view-background", "bottom"), 3, state.bottom, true),
|
||||
color_row(
|
||||
crate::tr!("view-background", "middle"),
|
||||
2,
|
||||
state.middle,
|
||||
state.three
|
||||
),
|
||||
color_row(
|
||||
crate::tr!("view-background", "bottom"),
|
||||
3,
|
||||
state.bottom,
|
||||
true
|
||||
),
|
||||
]
|
||||
.spacing(8),
|
||||
),
|
||||
@@ -2497,9 +2551,11 @@ pub fn view_background<'a>(
|
||||
.align_y(iced::Center),
|
||||
row![
|
||||
label(crate::tr!("view-background", "position"), true),
|
||||
pick_list(Some(state.position), BgPosition::ALL.to_vec(), |p: &BgPosition| {
|
||||
p.to_string()
|
||||
})
|
||||
pick_list(
|
||||
Some(state.position),
|
||||
BgPosition::ALL.to_vec(),
|
||||
|p: &BgPosition| { p.to_string() }
|
||||
)
|
||||
.on_select(|p| msg(PdfDialogMsg::BgPosition(p)))
|
||||
.text_size(12)
|
||||
.padding([4, 8])
|
||||
@@ -2545,11 +2601,17 @@ fn adjust_card<'a>(state: &'a ViewBackgroundState) -> Element<'a, Message> {
|
||||
if enabled {
|
||||
muted_style(theme)
|
||||
} else {
|
||||
text::Style { color: Some(theme.palette().background.strong.color) }
|
||||
text::Style {
|
||||
color: Some(theme.palette().background.strong.color),
|
||||
}
|
||||
}
|
||||
};
|
||||
let spin = |field: u8| -> Element<'a, Message> {
|
||||
let value = if field < 2 { &state.offset[field as usize] } else { &state.scale[field as usize - 2] };
|
||||
let value = if field < 2 {
|
||||
&state.offset[field as usize]
|
||||
} else {
|
||||
&state.scale[field as usize - 2]
|
||||
};
|
||||
let mut input = text_input("", value)
|
||||
.size(11)
|
||||
.padding([4, 8])
|
||||
@@ -2583,7 +2645,10 @@ fn adjust_card<'a>(state: &'a ViewBackgroundState) -> Element<'a, Message> {
|
||||
}
|
||||
b
|
||||
};
|
||||
row![input, column![arrow(true), arrow(false)].spacing(1)].spacing(2).align_y(iced::Center).into()
|
||||
row![input, column![arrow(true), arrow(false)].spacing(1)]
|
||||
.spacing(2)
|
||||
.align_y(iced::Center)
|
||||
.into()
|
||||
};
|
||||
let axis = |name: &'static str| text(name).size(11).style(dim);
|
||||
let line = |label: String, a: u8, b: u8, unit: bool| -> Element<'a, Message> {
|
||||
@@ -2597,7 +2662,11 @@ fn adjust_card<'a>(state: &'a ViewBackgroundState) -> Element<'a, Message> {
|
||||
.spacing(6)
|
||||
.align_y(iced::Center);
|
||||
if unit {
|
||||
r = r.push(text(crate::tr!("view-background", "pixels")).size(11).style(dim));
|
||||
r = r.push(
|
||||
text(crate::tr!("view-background", "pixels"))
|
||||
.size(11)
|
||||
.style(dim),
|
||||
);
|
||||
}
|
||||
r.into()
|
||||
};
|
||||
@@ -2610,9 +2679,15 @@ fn adjust_card<'a>(state: &'a ViewBackgroundState) -> Element<'a, Message> {
|
||||
container(
|
||||
column![
|
||||
row![
|
||||
text(crate::tr!("view-background", "adjust").to_uppercase()).size(10).style(move |theme: &Theme| {
|
||||
if enabled { accent_text(theme) } else { dim(theme) }
|
||||
}),
|
||||
text(crate::tr!("view-background", "adjust").to_uppercase())
|
||||
.size(10)
|
||||
.style(move |theme: &Theme| {
|
||||
if enabled {
|
||||
accent_text(theme)
|
||||
} else {
|
||||
dim(theme)
|
||||
}
|
||||
}),
|
||||
Space::new().width(Fill),
|
||||
reset,
|
||||
]
|
||||
@@ -2653,11 +2728,16 @@ pub const IMAGE_ADJUST_DEFAULTS: [u8; 3] = [50, 50, 0];
|
||||
|
||||
impl ImageAdjustState {
|
||||
pub fn new(handles: Vec<codec::Handle>, path: &str, values: [u8; 3], canvas: [u8; 3]) -> Self {
|
||||
let name = std::path::Path::new(path.trim())
|
||||
.file_name()
|
||||
.map_or_else(|| path.trim().to_string(), |n| n.to_string_lossy().into_owned());
|
||||
let name = std::path::Path::new(path.trim()).file_name().map_or_else(
|
||||
|| path.trim().to_string(),
|
||||
|n| n.to_string_lossy().into_owned(),
|
||||
);
|
||||
let source = crate::scene::model::image_model::resolve_image(path).and_then(|decoded| {
|
||||
let full = ::image::RgbaImage::from_raw(decoded.width, decoded.height, decoded.pixels.to_vec())?;
|
||||
let full = ::image::RgbaImage::from_raw(
|
||||
decoded.width,
|
||||
decoded.height,
|
||||
decoded.pixels.to_vec(),
|
||||
)?;
|
||||
let small = if full.width().max(full.height()) > 512 {
|
||||
::image::imageops::thumbnail(&full, 512, 512)
|
||||
} else {
|
||||
@@ -2690,7 +2770,10 @@ impl ImageAdjustState {
|
||||
self.preview = self.source.as_ref().map(|(w, h, pixels)| {
|
||||
// As the drawing shows it: brightness then contrast per channel,
|
||||
// fade as the image's opacity over the drawing area.
|
||||
let lut = crate::scene::model::image_model::brightness_contrast_lut(self.values[0], self.values[1]);
|
||||
let lut = crate::scene::model::image_model::brightness_contrast_lut(
|
||||
self.values[0],
|
||||
self.values[1],
|
||||
);
|
||||
let opacity = 1.0 - f32::from(self.values[2]) / 100.0;
|
||||
let mut out = Vec::with_capacity(pixels.len());
|
||||
for (k, px) in pixels.chunks_exact(4).enumerate() {
|
||||
@@ -2741,16 +2824,20 @@ pub fn image_adjust<'a>(
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.into(),
|
||||
None => container(text(crate::tr!("image-adjust", "no-preview")).size(11).style(muted_style))
|
||||
.center(Fill)
|
||||
.into(),
|
||||
None => container(
|
||||
text(crate::tr!("image-adjust", "no-preview"))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
)
|
||||
.center(Fill)
|
||||
.into(),
|
||||
};
|
||||
let divider = container(
|
||||
container(Space::new().width(2.0).height(Fill)).style(|theme: &Theme| container::Style {
|
||||
let divider = container(container(Space::new().width(2.0).height(Fill)).style(
|
||||
|theme: &Theme| container::Style {
|
||||
background: Some(Background::Color(theme.palette().primary.base.color)),
|
||||
..Default::default()
|
||||
}),
|
||||
)
|
||||
},
|
||||
))
|
||||
.center_x(Fill)
|
||||
.height(Fill);
|
||||
let badges = row![
|
||||
@@ -2759,7 +2846,10 @@ pub fn image_adjust<'a>(
|
||||
badge(crate::tr!("image-adjust", "after"), true),
|
||||
]
|
||||
.padding(8);
|
||||
let mut layers = iced::widget::Stack::new().width(Fill).height(Fill).push(picture);
|
||||
let mut layers = iced::widget::Stack::new()
|
||||
.width(Fill)
|
||||
.height(Fill)
|
||||
.push(picture);
|
||||
if state.preview.is_some() {
|
||||
layers = layers.push(divider).push(badges);
|
||||
}
|
||||
@@ -2771,15 +2861,24 @@ pub fn image_adjust<'a>(
|
||||
.clip(true)
|
||||
.style(move |_: &Theme| container::Style {
|
||||
background: Some(Background::Color(canvas)),
|
||||
border: Border { radius: 5.0.into(), ..Default::default() },
|
||||
border: Border {
|
||||
radius: 5.0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}),
|
||||
);
|
||||
let ramp = |row: u8| -> Background {
|
||||
let (from, to) = match row {
|
||||
// Contrast: grey, the flat image its zero gives.
|
||||
1 => (iced::Color::from_rgb8(0x77, 0x77, 0x77), iced::Color::from_rgb8(0x77, 0x77, 0x77)),
|
||||
_ => (iced::Color::from_rgb8(0x22, 0x22, 0x22), iced::Color::from_rgb8(0xee, 0xee, 0xee)),
|
||||
1 => (
|
||||
iced::Color::from_rgb8(0x77, 0x77, 0x77),
|
||||
iced::Color::from_rgb8(0x77, 0x77, 0x77),
|
||||
),
|
||||
_ => (
|
||||
iced::Color::from_rgb8(0x22, 0x22, 0x22),
|
||||
iced::Color::from_rgb8(0xee, 0xee, 0xee),
|
||||
),
|
||||
};
|
||||
Background::Gradient(iced::Gradient::Linear(
|
||||
iced::gradient::Linear::new(iced::Radians(std::f32::consts::FRAC_PI_2))
|
||||
@@ -2789,30 +2888,41 @@ pub fn image_adjust<'a>(
|
||||
};
|
||||
let line = |row: u8, label: String| -> Element<'a, Message> {
|
||||
let k = row as usize;
|
||||
let bar = container(container(Space::new()).width(Fill).height(8.0).style(move |_: &Theme| {
|
||||
container::Style {
|
||||
let bar = container(container(Space::new()).width(Fill).height(8.0).style(
|
||||
move |_: &Theme| container::Style {
|
||||
background: Some(ramp(row)),
|
||||
border: Border { radius: 4.0.into(), ..Default::default() },
|
||||
border: Border {
|
||||
radius: 4.0.into(),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
}))
|
||||
},
|
||||
))
|
||||
.center_y(16.0)
|
||||
.width(Fill);
|
||||
let slide = iced::widget::slider(0..=100u8, state.values[k], move |v| msg(PdfDialogMsg::IaValue(row, v)))
|
||||
.height(16.0)
|
||||
.style(|theme: &Theme, _| iced::widget::slider::Style {
|
||||
rail: iced::widget::slider::Rail {
|
||||
backgrounds: (iced::Color::TRANSPARENT.into(), iced::Color::TRANSPARENT.into()),
|
||||
width: 0.0,
|
||||
border: Border::default(),
|
||||
let slide = iced::widget::slider(0..=100u8, state.values[k], move |v| {
|
||||
msg(PdfDialogMsg::IaValue(row, v))
|
||||
})
|
||||
.height(16.0)
|
||||
.style(|theme: &Theme, _| iced::widget::slider::Style {
|
||||
rail: iced::widget::slider::Rail {
|
||||
backgrounds: (
|
||||
iced::Color::TRANSPARENT.into(),
|
||||
iced::Color::TRANSPARENT.into(),
|
||||
),
|
||||
width: 0.0,
|
||||
border: Border::default(),
|
||||
},
|
||||
handle: iced::widget::slider::Handle {
|
||||
shape: iced::widget::slider::HandleShape::Rectangle {
|
||||
width: 4,
|
||||
border_radius: 2.0.into(),
|
||||
},
|
||||
handle: iced::widget::slider::Handle {
|
||||
shape: iced::widget::slider::HandleShape::Rectangle { width: 4, border_radius: 2.0.into() },
|
||||
background: theme.palette().primary.base.color.into(),
|
||||
border_width: 0.0,
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
},
|
||||
});
|
||||
background: theme.palette().primary.base.color.into(),
|
||||
border_width: 0.0,
|
||||
border_color: iced::Color::TRANSPARENT,
|
||||
},
|
||||
});
|
||||
let step = |dir: i8| {
|
||||
button(text(if dir < 0 { "\u{2212}" } else { "+" }).size(12))
|
||||
.on_press(msg(PdfDialogMsg::IaStep(row, dir)))
|
||||
@@ -2829,9 +2939,13 @@ pub fn image_adjust<'a>(
|
||||
row![
|
||||
text(label).size(11).width(Length::Fixed(72.0)),
|
||||
iced::widget::Stack::new().width(Fill).push(bar).push(slide),
|
||||
container(row![step(-1), value, step(1)].spacing(2).align_y(iced::Center))
|
||||
.padding(2)
|
||||
.style(well_style),
|
||||
container(
|
||||
row![step(-1), value, step(1)]
|
||||
.spacing(2)
|
||||
.align_y(iced::Center)
|
||||
)
|
||||
.padding(2)
|
||||
.style(well_style),
|
||||
]
|
||||
.spacing(10)
|
||||
.align_y(iced::Center)
|
||||
@@ -2843,18 +2957,29 @@ pub fn image_adjust<'a>(
|
||||
line(0, crate::tr!("image-adjust", "brightness")),
|
||||
line(1, crate::tr!("image-adjust", "contrast")),
|
||||
line(2, crate::tr!("image-adjust", "fade")),
|
||||
text(crate::tr!("image-adjust", "hint")).size(10).style(muted_style),
|
||||
text(crate::tr!("image-adjust", "hint"))
|
||||
.size(10)
|
||||
.style(muted_style),
|
||||
]
|
||||
.spacing(10),
|
||||
);
|
||||
let reset = button(text(crate::tr!("image-adjust", "reset")).size(11).style(accent_text))
|
||||
.on_press(msg(PdfDialogMsg::IaReset))
|
||||
.style(button::text)
|
||||
.padding([5, 4]);
|
||||
let reset = button(
|
||||
text(crate::tr!("image-adjust", "reset"))
|
||||
.size(11)
|
||||
.style(accent_text),
|
||||
)
|
||||
.on_press(msg(PdfDialogMsg::IaReset))
|
||||
.style(button::text)
|
||||
.padding([5, 4]);
|
||||
column![
|
||||
preview,
|
||||
adjust,
|
||||
footer("imageadjust", Some(reset.into()), crate::tr!("image-adjust", "apply").into(), PdfDialogMsg::IaApply),
|
||||
footer(
|
||||
"imageadjust",
|
||||
Some(reset.into()),
|
||||
crate::tr!("image-adjust", "apply").into(),
|
||||
PdfDialogMsg::IaApply
|
||||
),
|
||||
]
|
||||
.spacing(10)
|
||||
.padding([10, 12])
|
||||
|
||||
@@ -153,25 +153,37 @@ pub fn view<'a>(
|
||||
const MAX: usize = 64;
|
||||
let count = path.chars().count();
|
||||
let shown = if count > MAX {
|
||||
format!("…{}", path.chars().skip(count - (MAX - 1)).collect::<String>())
|
||||
format!(
|
||||
"…{}",
|
||||
path.chars().skip(count - (MAX - 1)).collect::<String>()
|
||||
)
|
||||
} else {
|
||||
path.to_string()
|
||||
};
|
||||
about = about.push(
|
||||
text(format!("{} {}", crate::tr!("ref-edit", "dialog-path"), shown))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
text(format!(
|
||||
"{} {}",
|
||||
crate::tr!("ref-edit", "dialog-path"),
|
||||
shown
|
||||
))
|
||||
.size(11)
|
||||
.style(muted_style),
|
||||
);
|
||||
}
|
||||
let reference = card(
|
||||
crate::tr!("ref-edit", "dialog-reference"),
|
||||
row![
|
||||
preview,
|
||||
column![iced::widget::scrollable(chips).direction(
|
||||
iced::widget::scrollable::Direction::Horizontal(
|
||||
iced::widget::scrollable::Scrollbar::new().width(4).scroller_width(4)
|
||||
)
|
||||
), about]
|
||||
column![
|
||||
iced::widget::scrollable(chips).direction(
|
||||
iced::widget::scrollable::Direction::Horizontal(
|
||||
iced::widget::scrollable::Scrollbar::new()
|
||||
.width(4)
|
||||
.scroller_width(4)
|
||||
)
|
||||
),
|
||||
about
|
||||
]
|
||||
.spacing(8)
|
||||
.width(Fill)
|
||||
]
|
||||
|
||||
+4
-2
@@ -1070,8 +1070,10 @@ impl<'a> Widget<Message, Theme, Renderer> for PosReport<'a> {
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
DD_BOUNDS.with(|m| {
|
||||
m.borrow_mut()
|
||||
.insert(self.id.to_string(), (layout.bounds(), FRAME.with(Cell::get)));
|
||||
m.borrow_mut().insert(
|
||||
self.id.to_string(),
|
||||
(layout.bounds(), FRAME.with(Cell::get)),
|
||||
);
|
||||
});
|
||||
self.child.as_widget().draw(
|
||||
&tree.children[0],
|
||||
|
||||
Reference in new issue
Block a user