mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-24 10:08:00 -06:00
UI: checkboxes and radio buttons are outlined, and checkboxes are square (#1173)
* UI: checkboxes draw a rounded-square box, not a radio-button circle
The theme's 6 px widget radius made egui's 14 px checkbox box a circle,
so View > Page display's Continuous scrolling and Show cover page looked
like two more layout radio buttons. On the light theme an unticked box
had no outline and the panel's fill, so it couldn't be seen.
Every checkbox (81 call sites) now goes through widgets::checkbox /
checkbox_without_text, which draws the box with a 3 px radius and a 1 px
border outline, keeping an outline the caller already set (dialogs).
clippy.toml disallows egui::Ui::checkbox and egui::Checkbox::{new,
without_text} so new checkboxes use the helper too.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* UI: radio buttons are outlined and filled, so they show on the light theme
On the light theme the field colour is the panels' white and widgets
have no outline, so an unselected radio button (and an unticked
checkbox) was invisible outside dialogs: View > Page display showed only
the selected dot.
Every radio button (79 call sites) now goes through widgets::radio /
radio_value, sharing the checkbox helper's styling: a 1 px border
outline, text_muted on hover, and on the light theme the plain fill
(hover) as dialogs already had, keeping what the caller set. The dark
theme keeps its fills. clippy.toml also disallows egui::Ui::radio,
egui::Ui::radio_value and egui::RadioButton::new.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
Brandon Thomas
parent
38df401687
commit
0ef75d294c
@@ -140,6 +140,7 @@ Ranked; detail and the full milestone table in [docs/roadmap.md](docs/roadmap.md
|
|||||||
|
|
||||||
Newest first: the date, what moved, and the new overall estimate.
|
Newest first: the date, what moved, and the new overall estimate.
|
||||||
|
|
||||||
|
- **2026-10-11 (UI, checkboxes and radio buttons):** Checkboxes no longer look like radio buttons, and on the light theme neither is invisible. With the theme's 6 px widget radius egui drew a checkbox's 14 px box as a circle, so View ▸ Page display's Continuous scrolling and Show cover page read as two more layout choices; on the light theme an unticked box or an unselected radio button was white on white with no edge. Every checkbox (81) and radio button (79) now goes through `widgets::checkbox` / `widgets::radio` / `widgets::radio_value`: a 1 px outline (darker on hover), the plain fill on the light theme as dialogs already had, and for checkboxes a rounded-square box (3 px), keeping what the caller set (dialogs, the search panel). `clippy.toml` disallows `ui.checkbox`, `ui.radio`, `ui.radio_value`, `egui::Checkbox` and `egui::RadioButton` so new ones use them too. Estimates unchanged.
|
||||||
- **2026-10-11 (M13, #971):** `text_find` now bounds each page's temporary match-range list to the remaining result limit plus one, which is enough to detect and return a continuation without allocating every match on a dense page. Added limited-search coverage; the existing end-to-end continuation test passes. The text cache byte budget and per-page extraction warnings remain open.
|
- **2026-10-11 (M13, #971):** `text_find` now bounds each page's temporary match-range list to the remaining result limit plus one, which is enough to detect and return a continuation without allocating every match on a dense page. Added limited-search coverage; the existing end-to-end continuation test passes. The text cache byte budget and per-page extraction warnings remain open.
|
||||||
- **2026-10-11 (M7, #594):** Ordinary paragraph body drags now use the existing source-preserving object translation path; edge drags still rewrap. An in-progress drag is discarded if the document generation changes, and added-content gestures keep their own edit handler. Added real-shell source regressions for overlapping and identical labels, history, save/reopen, partial overlap, kerning, rotation/crop/UserUnit and atomic refusal. Linux validation passed: focused UI/engine regressions, exact-base source-loss failures, native drag/undo/redo/save/reopen with independent Poppler renders, and all eight CI gates. Text replacement and resizing over unrelated overlapping content remain outside this repair. Estimates unchanged.
|
- **2026-10-11 (M7, #594):** Ordinary paragraph body drags now use the existing source-preserving object translation path; edge drags still rewrap. An in-progress drag is discarded if the document generation changes, and added-content gestures keep their own edit handler. Added real-shell source regressions for overlapping and identical labels, history, save/reopen, partial overlap, kerning, rotation/crop/UserUnit and atomic refusal. Linux validation passed: focused UI/engine regressions, exact-base source-loss failures, native drag/undo/redo/save/reopen with independent Poppler renders, and all eight CI gates. Text replacement and resizing over unrelated overlapping content remain outside this repair. Estimates unchanged.
|
||||||
|
|
||||||
|
|||||||
+11
@@ -2,3 +2,14 @@ allow-unwrap-in-tests = true
|
|||||||
allow-expect-in-tests = true
|
allow-expect-in-tests = true
|
||||||
allow-panic-in-tests = true
|
allow-panic-in-tests = true
|
||||||
allow-indexing-slicing-in-tests = true
|
allow-indexing-slicing-in-tests = true
|
||||||
|
|
||||||
|
# With the theme's widget visuals egui draws a checkbox's box as a circle, like a radio button,
|
||||||
|
# and on the light theme neither has an outline or a fill that shows on a white panel.
|
||||||
|
disallowed-methods = [
|
||||||
|
{ path = "egui::Ui::checkbox", reason = "use checkbox from crates/ui-egui/src/widgets.rs, whose box is a square" },
|
||||||
|
{ path = "egui::Checkbox::new", reason = "use checkbox from crates/ui-egui/src/widgets.rs, whose box is a square" },
|
||||||
|
{ path = "egui::Checkbox::without_text", reason = "use checkbox_without_text from crates/ui-egui/src/widgets.rs, whose box is a square" },
|
||||||
|
{ path = "egui::Ui::radio", reason = "use radio from crates/ui-egui/src/widgets.rs, which is outlined and filled" },
|
||||||
|
{ path = "egui::Ui::radio_value", reason = "use radio_value from crates/ui-egui/src/widgets.rs, which is outlined and filled" },
|
||||||
|
{ path = "egui::RadioButton::new", reason = "use radio from crates/ui-egui/src/widgets.rs, which is outlined and filled" },
|
||||||
|
]
|
||||||
|
|||||||
@@ -83,12 +83,12 @@ pub(crate) fn options_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens)
|
|||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
};
|
};
|
||||||
group(ui, "Report Options", &mut |ui| {
|
group(ui, "Report Options", &mut |ui| {
|
||||||
ui.checkbox(&mut o.create_report, tl!("Create accessibility report"));
|
ui.add(widgets::checkbox(&mut o.create_report, tl!("Create accessibility report")));
|
||||||
});
|
});
|
||||||
group(ui, "Page Range", &mut |ui| {
|
group(ui, "Page Range", &mut |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut o.all_pages, true, tl!("All pages in document"));
|
widgets::radio_value(ui, &mut o.all_pages, true, tl!("All pages in document"));
|
||||||
ui.radio_value(&mut o.all_pages, false, tl!("Pages from"));
|
widgets::radio_value(ui, &mut o.all_pages, false, tl!("Pages from"));
|
||||||
ui.add_enabled(!o.all_pages, egui::DragValue::new(&mut o.from).range(1..=pages));
|
ui.add_enabled(!o.all_pages, egui::DragValue::new(&mut o.from).range(1..=pages));
|
||||||
ui.label(tl!("to"));
|
ui.label(tl!("to"));
|
||||||
ui.add_enabled(!o.all_pages, egui::DragValue::new(&mut o.to).range(1..=pages));
|
ui.add_enabled(!o.all_pages, egui::DragValue::new(&mut o.to).range(1..=pages));
|
||||||
@@ -107,7 +107,7 @@ pub(crate) fn options_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens)
|
|||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
for r in Rule::ALL.into_iter().filter(|r| r.category() == o.category) {
|
for r in Rule::ALL.into_iter().filter(|r| r.category() == o.category) {
|
||||||
let mut checked = o.rules.contains(&r);
|
let mut checked = o.rules.contains(&r);
|
||||||
if ui.checkbox(&mut checked, tl!(r.description())).changed() {
|
if ui.add(widgets::checkbox(&mut checked, tl!(r.description()))).changed() {
|
||||||
if checked {
|
if checked {
|
||||||
o.rules.insert(r);
|
o.rules.insert(r);
|
||||||
} else {
|
} else {
|
||||||
@@ -126,7 +126,7 @@ pub(crate) fn options_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens)
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.checkbox(&mut o.show_dialog, tl!("Show this dialog when the Checker starts"));
|
ui.add(widgets::checkbox(&mut o.show_dialog, tl!("Show this dialog when the Checker starts")));
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
let (mut start, mut cancel) = (false, false);
|
let (mut start, mut cancel) = (false, false);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -507,7 +507,7 @@ pub(crate) fn alt_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) ->
|
|||||||
let l = ui.label(tl!("Alternate text"));
|
let l = ui.label(tl!("Alternate text"));
|
||||||
ui.add(crate::widgets::block(&mut d.texts[i]).desired_rows(3).desired_width(380.0).hint_text(tl!("Describe the figure"))).labelled_by(l.id);
|
ui.add(crate::widgets::block(&mut d.texts[i]).desired_rows(3).desired_width(380.0).hint_text(tl!("Describe the figure"))).labelled_by(l.id);
|
||||||
});
|
});
|
||||||
ui.checkbox(&mut d.decorative[i], tl!("Decorative figure"));
|
ui.add(widgets::checkbox(&mut d.decorative[i], tl!("Decorative figure")));
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
let (mut save, mut cancel) = (false, false);
|
let (mut save, mut cancel) = (false, false);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ use egui::{Rect, RichText, pos2, vec2};
|
|||||||
|
|
||||||
use crate::PdfCraftApp;
|
use crate::PdfCraftApp;
|
||||||
use crate::theme::{self, ThemeKind, ThemePreference, Tokens};
|
use crate::theme::{self, ThemeKind, ThemePreference, Tokens};
|
||||||
|
use crate::widgets;
|
||||||
|
|
||||||
/// The themes each card offers. One of each today; a new theme is one more row here.
|
/// The themes each card offers. One of each today; a new theme is one more row here.
|
||||||
pub const LIGHT_THEMES: &[(ThemeKind, &str)] = &[(ThemeKind::Light, "Light gray")];
|
pub const LIGHT_THEMES: &[(ThemeKind, &str)] = &[(ThemeKind::Light, "Light gray")];
|
||||||
@@ -103,7 +104,7 @@ fn card(ui: &mut egui::Ui, t: &Tokens, title: &str, active: bool, themes: &[(The
|
|||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
for (kind, label) in themes {
|
for (kind, label) in themes {
|
||||||
// One theme per card today, so it is always the chosen one.
|
// One theme per card today, so it is always the chosen one.
|
||||||
let _ = ui.radio(*kind == shown, tl!(label));
|
let _ = ui.add(widgets::radio(*kind == shown, tl!(label)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,7 +203,7 @@ impl Draft {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn row<T>(ui: &mut egui::Ui, label: &str, c: &mut Change<T>, edit: impl FnOnce(&mut egui::Ui, &mut T)) {
|
fn row<T>(ui: &mut egui::Ui, label: &str, c: &mut Change<T>, edit: impl FnOnce(&mut egui::Ui, &mut T)) {
|
||||||
ui.checkbox(&mut c.apply, tl!(label));
|
ui.add(widgets::checkbox(&mut c.apply, tl!(label)));
|
||||||
// Colour swatches contain their own grid; give every property's editor a separate
|
// Colour swatches contain their own grid; give every property's editor a separate
|
||||||
// namespace so border and fill controls do not share widget identities.
|
// namespace so border and fill controls do not share widget identities.
|
||||||
ui.push_id(label, |ui| ui.add_enabled_ui(c.apply, |ui| edit(ui, &mut c.value)));
|
ui.push_id(label, |ui| ui.add_enabled_ui(c.apply, |ui| edit(ui, &mut c.value)));
|
||||||
@@ -248,7 +248,7 @@ fn bool_row(ui: &mut egui::Ui, label: &str, c: &mut Change<bool>) {
|
|||||||
fn color(ui: &mut egui::Ui, value: &mut Option<[f64; 3]>) {
|
fn color(ui: &mut egui::Ui, value: &mut Option<[f64; 3]>) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let mut none = value.is_none();
|
let mut none = value.is_none();
|
||||||
if ui.checkbox(&mut none, tl!("No color")).changed() {
|
if ui.add(widgets::checkbox(&mut none, tl!("No color"))).changed() {
|
||||||
*value = if none { None } else { Some([0.0; 3]) };
|
*value = if none { None } else { Some([0.0; 3]) };
|
||||||
}
|
}
|
||||||
if let Some(c) = crate::comments::swatch_grid(ui, *value) {
|
if let Some(c) = crate::comments::swatch_grid(ui, *value) {
|
||||||
|
|||||||
@@ -3423,8 +3423,8 @@ fn find_bar(view: &mut DocView, pages: usize, area: Rect, ui: &mut egui::Ui, t:
|
|||||||
}
|
}
|
||||||
let opts = icons::button(ui, "settings-2", 26.0, find.case_sensitive || find.whole_words, tl!("Find options"));
|
let opts = icons::button(ui, "settings-2", 26.0, find.case_sensitive || find.whole_words, tl!("Find options"));
|
||||||
egui::Popup::menu(&opts).show(|ui| {
|
egui::Popup::menu(&opts).show(|ui| {
|
||||||
let a = ui.checkbox(&mut find.whole_words, tl!("Whole words only")).changed();
|
let a = ui.add(widgets::checkbox(&mut find.whole_words, tl!("Whole words only"))).changed();
|
||||||
let b = ui.checkbox(&mut find.case_sensitive, tl!("Case-sensitive")).changed();
|
let b = ui.add(widgets::checkbox(&mut find.case_sensitive, tl!("Case-sensitive"))).changed();
|
||||||
if a || b {
|
if a || b {
|
||||||
// Search again with the new options.
|
// Search again with the new options.
|
||||||
find.case_query.clear();
|
find.case_query.clear();
|
||||||
@@ -4050,7 +4050,7 @@ fn organize_grid(view: &mut DocView, info: &DocInfo, editable: bool, dirty: bool
|
|||||||
// outside the page image, including on very tall or wide pages.
|
// outside the page image, including on very tall or wide pages.
|
||||||
let mut checked = view.selected.contains(&i);
|
let mut checked = view.selected.contains(&i);
|
||||||
let check_rect = Rect::from_min_size(c.min + vec2(8.0, 8.0), Vec2::splat(20.0));
|
let check_rect = Rect::from_min_size(c.min + vec2(8.0, 8.0), Vec2::splat(20.0));
|
||||||
let check = ui.push_id(("org-check", i), |ui| ui.put(check_rect, egui::Checkbox::without_text(&mut checked))).inner;
|
let check = ui.push_id(("org-check", i), |ui| ui.put(check_rect, widgets::checkbox_without_text(&mut checked))).inner;
|
||||||
let label = crate::i18n::fmt(tl!("Select page {label}"), &[("label", &p.label)]);
|
let label = crate::i18n::fmt(tl!("Select page {label}"), &[("label", &p.label)]);
|
||||||
check.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Checkbox, true, checked, label.clone()));
|
check.widget_info(|| egui::WidgetInfo::selected(egui::WidgetType::Checkbox, true, checked, label.clone()));
|
||||||
if check.on_hover_text(label).changed() {
|
if check.on_hover_text(label).changed() {
|
||||||
|
|||||||
@@ -206,7 +206,7 @@ fn theme_menu(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
|||||||
(ThemePreference::Light, "view.theme.light", "Light gray"),
|
(ThemePreference::Light, "view.theme.light", "Light gray"),
|
||||||
(ThemePreference::Dark, "view.theme.dark", "Dark gray"),
|
(ThemePreference::Dark, "view.theme.dark", "Dark gray"),
|
||||||
] {
|
] {
|
||||||
if ui.radio(app.theme_preference == preference, tl!(label)).clicked() {
|
if ui.add(widgets::radio(app.theme_preference == preference, tl!(label))).clicked() {
|
||||||
app.execute(command);
|
app.execute(command);
|
||||||
ui.close();
|
ui.close();
|
||||||
}
|
}
|
||||||
@@ -467,7 +467,7 @@ fn main_menu(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
|||||||
(RightPanel::Search, tl!("Search")),
|
(RightPanel::Search, tl!("Search")),
|
||||||
(RightPanel::Compare, tl!("Compare")),
|
(RightPanel::Compare, tl!("Compare")),
|
||||||
] {
|
] {
|
||||||
if ui.radio(app.right == Some(p), tl!(label)).clicked() {
|
if ui.add(widgets::radio(app.right == Some(p), tl!(label))).clicked() {
|
||||||
app.choose_right_panel(Some(p));
|
app.choose_right_panel(Some(p));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -595,18 +595,18 @@ fn page_display_menu(ui: &mut egui::Ui, view: &DocView) -> Option<&'static str>
|
|||||||
ui.label(egui::RichText::new(tl!("Page display")).color(t.text_faint).small());
|
ui.label(egui::RichText::new(tl!("Page display")).color(t.text_faint).small());
|
||||||
let mut picked = None;
|
let mut picked = None;
|
||||||
for l in PageLayout::ORDER {
|
for l in PageLayout::ORDER {
|
||||||
if ui.radio(view.layout == l, tl!(l.label())).clicked() {
|
if ui.add(widgets::radio(view.layout == l, tl!(l.label()))).clicked() {
|
||||||
picked = Some(l.command());
|
picked = Some(l.command());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.separator();
|
ui.separator();
|
||||||
// Continuous scrolling is an independent checkbox over the base layout, as in Acrobat (#752).
|
// Continuous scrolling is an independent checkbox over the base layout, as in Acrobat (#752).
|
||||||
let mut continuous = view.continuous();
|
let mut continuous = view.continuous();
|
||||||
if ui.add(egui::Checkbox::new(&mut continuous, tl!("Continuous scrolling"))).changed() {
|
if ui.add(widgets::checkbox(&mut continuous, tl!("Continuous scrolling"))).changed() {
|
||||||
picked = Some("view.continuous");
|
picked = Some("view.continuous");
|
||||||
}
|
}
|
||||||
let mut cover = view.cover;
|
let mut cover = view.cover;
|
||||||
if ui.add_enabled(view.cover_applies(), egui::Checkbox::new(&mut cover, tl!("Show cover page in two-page view"))).changed() {
|
if ui.add_enabled(view.cover_applies(), widgets::checkbox(&mut cover, tl!("Show cover page in two-page view"))).changed() {
|
||||||
picked = Some("view.layout.cover");
|
picked = Some("view.layout.cover");
|
||||||
}
|
}
|
||||||
if picked.is_some() {
|
if picked.is_some() {
|
||||||
|
|||||||
@@ -260,7 +260,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
ui.checkbox(&mut d.edited.locked, tl!("Locked"));
|
ui.add(widgets::checkbox(&mut d.edited.locked, tl!("Locked")));
|
||||||
let (mut apply, mut cancel) = (false, false);
|
let (mut apply, mut cancel) = (false, false);
|
||||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
if widgets::pill_button(ui, tl!("OK"), true).clicked() {
|
if widgets::pill_button(ui, tl!("OK"), true).clicked() {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use crate::comments::{CommentPrefs, CommentTool, SortBy, color32, status_badge};
|
|||||||
use crate::icons;
|
use crate::icons;
|
||||||
use crate::panels::Nav;
|
use crate::panels::Nav;
|
||||||
use crate::theme::{self, Tokens};
|
use crate::theme::{self, Tokens};
|
||||||
|
use crate::widgets;
|
||||||
|
|
||||||
/// Accent bar of the selected card (11-visual-spec §3: `#0165DD`).
|
/// Accent bar of the selected card (11-visual-spec §3: `#0165DD`).
|
||||||
const CARD_ACCENT: Color32 = Color32::from_rgb(0x01, 0x65, 0xDD);
|
const CARD_ACCENT: Color32 = Color32::from_rgb(0x01, 0x65, 0xDD);
|
||||||
@@ -510,7 +511,7 @@ pub(crate) fn header_controls(ui: &mut egui::Ui, info: &DocInfo, view: &mut DocV
|
|||||||
(SortBy::Type, tl!("Type")),
|
(SortBy::Type, tl!("Type")),
|
||||||
(SortBy::Color, tl!("Colour")),
|
(SortBy::Color, tl!("Colour")),
|
||||||
] {
|
] {
|
||||||
if ui.radio(cv.sort == s, label).clicked() {
|
if ui.add(widgets::radio(cv.sort == s, label)).clicked() {
|
||||||
cv.sort = s;
|
cv.sort = s;
|
||||||
ui.close();
|
ui.close();
|
||||||
}
|
}
|
||||||
@@ -533,7 +534,7 @@ pub(crate) fn header_controls(ui: &mut egui::Ui, info: &DocInfo, view: &mut DocV
|
|||||||
ui.set_min_width(200.0);
|
ui.set_min_width(200.0);
|
||||||
let toggle = |ui: &mut egui::Ui, list: &mut Vec<String>, value: &str, label: &str| {
|
let toggle = |ui: &mut egui::Ui, list: &mut Vec<String>, value: &str, label: &str| {
|
||||||
let mut shown = !list.iter().any(|x| x == value);
|
let mut shown = !list.iter().any(|x| x == value);
|
||||||
if ui.checkbox(&mut shown, label).changed() {
|
if ui.add(widgets::checkbox(&mut shown, label)).changed() {
|
||||||
if shown {
|
if shown {
|
||||||
list.retain(|x| x != value);
|
list.retain(|x| x != value);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::PdfCraftApp;
|
use crate::PdfCraftApp;
|
||||||
|
use crate::widgets;
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
pub enum ResolutionChoice {
|
pub enum ResolutionChoice {
|
||||||
@@ -24,10 +25,10 @@ pub(crate) fn image_import_body(ui: &mut egui::Ui, app: &mut PdfCraftApp) -> (bo
|
|||||||
let Some(draft) = app.image_import.as_mut() else { return (false, true) };
|
let Some(draft) = app.image_import.as_mut() else { return (false, true) };
|
||||||
ui.label(crate::i18n::fmt(tl!("Selected image files: {count}"), &[("count", &draft.images.len().to_string())]));
|
ui.label(crate::i18n::fmt(tl!("Selected image files: {count}"), &[("count", &draft.images.len().to_string())]));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.radio_value(&mut draft.choice, ResolutionChoice::Image, tl!("Use image resolution"));
|
widgets::radio_value(ui, &mut draft.choice, ResolutionChoice::Image, tl!("Use image resolution"));
|
||||||
ui.label(tl!("Use each image's embedded DPI; use 72 DPI when it is absent."));
|
ui.label(tl!("Use each image's embedded DPI; use 72 DPI when it is absent."));
|
||||||
ui.radio_value(&mut draft.choice, ResolutionChoice::Default72, tl!("Use 72 DPI (one point per pixel)"));
|
widgets::radio_value(ui, &mut draft.choice, ResolutionChoice::Default72, tl!("Use 72 DPI (one point per pixel)"));
|
||||||
ui.radio_value(&mut draft.choice, ResolutionChoice::Custom, tl!("Use custom DPI"));
|
widgets::radio_value(ui, &mut draft.choice, ResolutionChoice::Custom, tl!("Use custom DPI"));
|
||||||
ui.add_enabled(draft.choice == ResolutionChoice::Custom, egui::DragValue::new(&mut draft.dpi).range(1.0..=1200.0).suffix(" DPI"));
|
ui.add_enabled(draft.choice == ResolutionChoice::Custom, egui::DragValue::new(&mut draft.dpi).range(1.0..=1200.0).suffix(" DPI"));
|
||||||
ui.label(tl!("DPI sets the printed page size without resampling the image."));
|
ui.label(tl!("DPI sets the printed page size without resampling the image."));
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|||||||
@@ -223,13 +223,13 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.label("");
|
ui.label("");
|
||||||
ui.add_enabled_ui(editable, |ui| {
|
ui.add_enabled_ui(editable, |ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.checkbox(&mut v.fit_window, tl!("Resize window to initial page"));
|
ui.add(widgets::checkbox(&mut v.fit_window, tl!("Resize window to initial page")));
|
||||||
ui.checkbox(&mut v.center_window, tl!("Center window on screen"));
|
ui.add(widgets::checkbox(&mut v.center_window, tl!("Center window on screen")));
|
||||||
ui.checkbox(&mut v.full_screen, tl!("Open in Full Screen mode"));
|
ui.add(widgets::checkbox(&mut v.full_screen, tl!("Open in Full Screen mode")));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Show:"));
|
ui.label(tl!("Show:"));
|
||||||
ui.radio_value(&mut v.display_title, false, tl!("File Name"));
|
widgets::radio_value(ui, &mut v.display_title, false, tl!("File Name"));
|
||||||
ui.radio_value(&mut v.display_title, true, tl!("Document Title"));
|
widgets::radio_value(ui, &mut v.display_title, true, tl!("Document Title"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -239,9 +239,9 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.label("");
|
ui.label("");
|
||||||
ui.add_enabled_ui(editable, |ui| {
|
ui.add_enabled_ui(editable, |ui| {
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.checkbox(&mut v.hide_menubar, tl!("Hide menu bar"));
|
ui.add(widgets::checkbox(&mut v.hide_menubar, tl!("Hide menu bar")));
|
||||||
ui.checkbox(&mut v.hide_toolbar, tl!("Hide toolbars"));
|
ui.add(widgets::checkbox(&mut v.hide_toolbar, tl!("Hide toolbars")));
|
||||||
ui.checkbox(&mut v.hide_window_ui, tl!("Hide window controls"));
|
ui.add(widgets::checkbox(&mut v.hide_window_ui, tl!("Hide window controls")));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -359,8 +359,8 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.label(egui::RichText::new(tl!("Binding")).color(t.text_muted));
|
ui.label(egui::RichText::new(tl!("Binding")).color(t.text_muted));
|
||||||
ui.add_enabled_ui(editable, |ui| {
|
ui.add_enabled_ui(editable, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut v.right_to_left, false, tl!("Left Edge"));
|
widgets::radio_value(ui, &mut v.right_to_left, false, tl!("Left Edge"));
|
||||||
ui.radio_value(&mut v.right_to_left, true, tl!("Right Edge"));
|
widgets::radio_value(ui, &mut v.right_to_left, true, tl!("Right Edge"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -403,7 +403,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
if selected.is_empty() && draft.mode == M::Selection {
|
if selected.is_empty() && draft.mode == M::Selection {
|
||||||
draft.mode = M::Pages;
|
draft.mode = M::Pages;
|
||||||
}
|
}
|
||||||
ui.radio_value(&mut draft.mode, M::Pages, tl!("Number of pages"));
|
widgets::radio_value(ui, &mut draft.mode, M::Pages, tl!("Number of pages"));
|
||||||
ui.add_enabled_ui(draft.mode == M::Pages, |ui| {
|
ui.add_enabled_ui(draft.mode == M::Pages, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_space(24.0);
|
ui.add_space(24.0);
|
||||||
@@ -411,7 +411,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.add(egui::DragValue::new(&mut draft.every).range(1..=n.max(1)));
|
ui.add(egui::DragValue::new(&mut draft.every).range(1..=n.max(1)));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.radio_value(&mut draft.mode, M::Size, tl!("File size"));
|
widgets::radio_value(ui, &mut draft.mode, M::Size, tl!("File size"));
|
||||||
ui.add_enabled_ui(draft.mode == M::Size, |ui| {
|
ui.add_enabled_ui(draft.mode == M::Size, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_space(24.0);
|
ui.add_space(24.0);
|
||||||
@@ -420,14 +420,15 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.add_enabled_ui(!marks.is_empty(), |ui| {
|
ui.add_enabled_ui(!marks.is_empty(), |ui| {
|
||||||
ui.radio_value(
|
widgets::radio_value(
|
||||||
|
ui,
|
||||||
&mut draft.mode,
|
&mut draft.mode,
|
||||||
M::Bookmarks,
|
M::Bookmarks,
|
||||||
crate::i18n::fmt(tl!("Top-level bookmarks ({n})"), &[("n", &marks.len().to_string())]),
|
crate::i18n::fmt(tl!("Top-level bookmarks ({n})"), &[("n", &marks.len().to_string())]),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
ui.add_enabled_ui(!selected.is_empty(), |ui| {
|
ui.add_enabled_ui(!selected.is_empty(), |ui| {
|
||||||
ui.radio_value(&mut draft.mode, M::Selection, tl!("Before each selected page (select pages in Organize)"))
|
widgets::radio_value(ui, &mut draft.mode, M::Selection, tl!("Before each selected page (select pages in Organize)"))
|
||||||
});
|
});
|
||||||
let plan = match draft.mode {
|
let plan = match draft.mode {
|
||||||
M::Pages => crate::SplitPlan::By(pdfcraft_engine::SplitBy::PageCount(draft.every)),
|
M::Pages => crate::SplitPlan::By(pdfcraft_engine::SplitBy::PageCount(draft.every)),
|
||||||
@@ -570,8 +571,8 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
let l = ui.label(tl!("File name"));
|
let l = ui.label(tl!("File name"));
|
||||||
ui.add(egui::TextEdit::singleline(&mut app.extract_draft.name).desired_width(240.0)).labelled_by(l.id);
|
ui.add(egui::TextEdit::singleline(&mut app.extract_draft.name).desired_width(240.0)).labelled_by(l.id);
|
||||||
});
|
});
|
||||||
ui.checkbox(&mut app.extract_draft.delete, tl!("Delete pages after extracting"));
|
ui.add(widgets::checkbox(&mut app.extract_draft.delete, tl!("Delete pages after extracting")));
|
||||||
ui.checkbox(&mut app.extract_draft.separate, tl!("Extract pages as separate files"));
|
ui.add(widgets::checkbox(&mut app.extract_draft.separate, tl!("Extract pages as separate files")));
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
ui.with_layout(Layout::right_to_left(Align::Center), |ui| {
|
||||||
if widgets::pill_button(ui, tl!("Extract"), true).clicked() {
|
if widgets::pill_button(ui, tl!("Extract"), true).clicked() {
|
||||||
@@ -606,10 +607,10 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tl!("Pages:"));
|
ui.label(tl!("Pages:"));
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.radio_value(&mut d.which, 0, tl!("All"));
|
widgets::radio_value(ui, &mut d.which, 0, tl!("All"));
|
||||||
ui.radio_value(&mut d.which, 1, tl!("Selection"));
|
widgets::radio_value(ui, &mut d.which, 1, tl!("Selection"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.which, 2, tl!("From"));
|
widgets::radio_value(ui, &mut d.which, 2, tl!("From"));
|
||||||
ui.add_enabled(d.which == 2, egui::DragValue::new(&mut d.from).range(1..=n));
|
ui.add_enabled(d.which == 2, egui::DragValue::new(&mut d.from).range(1..=n));
|
||||||
ui.label(tl!("to"));
|
ui.label(tl!("to"));
|
||||||
ui.add_enabled(d.which == 2, egui::DragValue::new(&mut d.to).range(1..=n));
|
ui.add_enabled(d.which == 2, egui::DragValue::new(&mut d.to).range(1..=n));
|
||||||
@@ -666,9 +667,9 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.label(egui::RichText::new(tl!("Duplicate Field")).font(theme::semibold(18.0)));
|
ui.label(egui::RichText::new(tl!("Duplicate Field")).font(theme::semibold(18.0)));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.label(crate::i18n::fmt(tl!("Duplicate \"{name}\" onto:"), &[("name", &d.name)]));
|
ui.label(crate::i18n::fmt(tl!("Duplicate \"{name}\" onto:"), &[("name", &d.name)]));
|
||||||
ui.radio_value(&mut d.all, true, tl!("All pages"));
|
widgets::radio_value(ui, &mut d.all, true, tl!("All pages"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.all, false, tl!("From"));
|
widgets::radio_value(ui, &mut d.all, false, tl!("From"));
|
||||||
ui.add_enabled(!d.all, egui::DragValue::new(&mut d.from).range(1..=n));
|
ui.add_enabled(!d.all, egui::DragValue::new(&mut d.from).range(1..=n));
|
||||||
ui.label(tl!("to"));
|
ui.label(tl!("to"));
|
||||||
ui.add_enabled(!d.all, egui::DragValue::new(&mut d.to).range(1..=n));
|
ui.add_enabled(!d.all, egui::DragValue::new(&mut d.to).range(1..=n));
|
||||||
@@ -734,7 +735,7 @@ fn dialogs(app: &mut PdfCraftApp, ctx: &egui::Context) {
|
|||||||
ui.label(egui::RichText::new(tl!("Summarize Options")).font(theme::semibold(18.0)));
|
ui.label(egui::RichText::new(tl!("Summarize Options")).font(theme::semibold(18.0)));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.label(tl!("Choose a layout:"));
|
ui.label(tl!("Choose a layout:"));
|
||||||
let _ = ui.radio(true, tl!("Comments only"));
|
let _ = ui.add(widgets::radio(true, tl!("Comments only")));
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Sort comments by:"));
|
ui.label(tl!("Sort comments by:"));
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use pdfcraft_render::DocInfo;
|
|||||||
|
|
||||||
use crate::canvas::{DocView, PageXform};
|
use crate::canvas::{DocView, PageXform};
|
||||||
use crate::theme::Tokens;
|
use crate::theme::Tokens;
|
||||||
|
use crate::widgets;
|
||||||
|
|
||||||
const FOCUS_BLUE: Color32 = Color32::from_rgb(0x14, 0x73, 0xE6);
|
const FOCUS_BLUE: Color32 = Color32::from_rgb(0x14, 0x73, 0xE6);
|
||||||
/// Background of the in-place text field editor, the same in every theme.
|
/// Background of the in-place text field editor, the same in every theme.
|
||||||
@@ -425,7 +426,7 @@ pub(crate) fn overlay(ctx: &egui::Context, view: &mut DocView, info: &DocInfo, f
|
|||||||
let on = if multi { fx.picked.contains(export) } else { f.value.first() == Some(export) };
|
let on = if multi { fx.picked.contains(export) } else { f.value.first() == Some(export) };
|
||||||
if multi {
|
if multi {
|
||||||
let mut c = on;
|
let mut c = on;
|
||||||
if ui.checkbox(&mut c, display).changed() {
|
if ui.add(widgets::checkbox(&mut c, display)).changed() {
|
||||||
if c {
|
if c {
|
||||||
fx.picked.push(export.clone());
|
fx.picked.push(export.clone());
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
+12
-12
@@ -291,7 +291,7 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
|||||||
(crate::Mode::Convert, "Convert"),
|
(crate::Mode::Convert, "Convert"),
|
||||||
(crate::Mode::Sign, "E-Sign"),
|
(crate::Mode::Sign, "E-Sign"),
|
||||||
] {
|
] {
|
||||||
ui.radio_value(&mut app.default_mode, mode, tl!(label));
|
widgets::radio_value(ui, &mut app.default_mode, mode, tl!(label));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.label(
|
ui.label(
|
||||||
@@ -303,16 +303,16 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Default page display"));
|
ui.label(tl!("Default page display"));
|
||||||
for l in crate::canvas::PageLayout::ORDER {
|
for l in crate::canvas::PageLayout::ORDER {
|
||||||
ui.radio_value(&mut defaults.layout, l, tl!(l.label()));
|
widgets::radio_value(ui, &mut defaults.layout, l, tl!(l.label()));
|
||||||
}
|
}
|
||||||
ui.checkbox(&mut defaults.continuous, tl!("Continuous scrolling"));
|
ui.add(widgets::checkbox(&mut defaults.continuous, tl!("Continuous scrolling")));
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
use crate::canvas::Fit;
|
use crate::canvas::Fit;
|
||||||
ui.label(tl!("Default zoom"));
|
ui.label(tl!("Default zoom"));
|
||||||
ui.radio_value(&mut defaults.fit, Fit::Width, tl!("Fit to width"));
|
widgets::radio_value(ui, &mut defaults.fit, Fit::Width, tl!("Fit to width"));
|
||||||
ui.radio_value(&mut defaults.fit, Fit::Page, tl!("Zoom to page level"));
|
widgets::radio_value(ui, &mut defaults.fit, Fit::Page, tl!("Zoom to page level"));
|
||||||
ui.radio_value(&mut defaults.fit, Fit::None, tl!("Custom"));
|
widgets::radio_value(ui, &mut defaults.fit, Fit::None, tl!("Custom"));
|
||||||
// Editing the percentage selects Custom.
|
// Editing the percentage selects Custom.
|
||||||
let mut percent = defaults.zoom * 100.0;
|
let mut percent = defaults.zoom * 100.0;
|
||||||
if ui.add(egui::DragValue::new(&mut percent).range(8.0..=6400.0).max_decimals(0).suffix("%")).changed() {
|
if ui.add(egui::DragValue::new(&mut percent).range(8.0..=6400.0).max_decimals(0).suffix("%")).changed() {
|
||||||
@@ -327,20 +327,20 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Units"));
|
ui.label(tl!("Units"));
|
||||||
for u in crate::units::Unit::ALL {
|
for u in crate::units::Unit::ALL {
|
||||||
ui.radio_value(&mut app.units, u, u.label());
|
widgets::radio_value(ui, &mut app.units, u, u.label());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.label(egui::RichText::new(tl!("For page sizes and margins.")).small().color(t.text_muted));
|
ui.label(egui::RichText::new(tl!("For page sizes and margins.")).small().color(t.text_muted));
|
||||||
// The web build has no file paths to reopen.
|
// The web build has no file paths to reopen.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
ui.checkbox(&mut app.reopen_last_session, tl!("Reopen the files that were open when PdfCraft last closed"));
|
ui.add(widgets::checkbox(&mut app.reopen_last_session, tl!("Reopen the files that were open when PdfCraft last closed")));
|
||||||
// The web build has no files on disk to watch.
|
// The web build has no files on disk to watch.
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
ui.checkbox(&mut app.reload_changed_files, tl!("Reload documents when their files change on disk"));
|
ui.add(widgets::checkbox(&mut app.reload_changed_files, tl!("Reload documents when their files change on disk")));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Mouse wheel scroll speed"));
|
ui.label(tl!("Mouse wheel scroll speed"));
|
||||||
for speed in crate::scroll_speed::ScrollSpeed::ALL {
|
for speed in crate::scroll_speed::ScrollSpeed::ALL {
|
||||||
ui.radio_value(&mut app.scroll_speed, speed, tl_ctx!("scroll speed", speed.label()));
|
widgets::radio_value(ui, &mut app.scroll_speed, speed, tl_ctx!("scroll speed", speed.label()));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
@@ -352,7 +352,7 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
|||||||
});
|
});
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.label(egui::RichText::new(tl!("Fill & Sign")).font(theme::semibold(13.0)));
|
ui.label(egui::RichText::new(tl!("Fill & Sign")).font(theme::semibold(13.0)));
|
||||||
ui.checkbox(&mut app.flatten_fill_sign_on_save, tl!("Flatten Fill & Sign when saving"));
|
ui.add(widgets::checkbox(&mut app.flatten_fill_sign_on_save, tl!("Flatten Fill & Sign when saving")));
|
||||||
ui.label(
|
ui.label(
|
||||||
egui::RichText::new(tl!("Text, marks and signatures become part of the page. Other comments stay editable.")).small().color(t.text_muted),
|
egui::RichText::new(tl!("Text, marks and signatures become part of the page. Other comments stay editable.")).small().color(t.text_muted),
|
||||||
);
|
);
|
||||||
@@ -363,7 +363,7 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
|||||||
egui::Frame::new().fill(t.hover).corner_radius(egui::CornerRadius::same(6)).inner_margin(egui::Margin::same(10)).show(ui, |ui| {
|
egui::Frame::new().fill(t.hover).corner_radius(egui::CornerRadius::same(6)).inner_margin(egui::Margin::same(10)).show(ui, |ui| {
|
||||||
ui.set_width(ui.available_width());
|
ui.set_width(ui.available_width());
|
||||||
let mut on = app.session.javascript();
|
let mut on = app.session.javascript();
|
||||||
if ui.checkbox(&mut on, tl!("Enable Acrobat JavaScript")).changed() {
|
if ui.add(widgets::checkbox(&mut on, tl!("Enable Acrobat JavaScript"))).changed() {
|
||||||
app.session.set_javascript(on);
|
app.session.set_javascript(on);
|
||||||
}
|
}
|
||||||
ui.label(
|
ui.label(
|
||||||
|
|||||||
@@ -249,14 +249,14 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut LinkDraft, pages: usize, t: &Token
|
|||||||
});
|
});
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
widgets::section_title(ui, tl!("Link Action"));
|
widgets::section_title(ui, tl!("Link Action"));
|
||||||
ui.radio_value(&mut d.web, true, tl!("Open a web page"));
|
widgets::radio_value(ui, &mut d.web, true, tl!("Open a web page"));
|
||||||
ui.add_enabled_ui(d.web, |ui| {
|
ui.add_enabled_ui(d.web, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_space(24.0);
|
ui.add_space(24.0);
|
||||||
ui.add(crate::widgets::line(&mut d.url).desired_width(360.0).hint_text("https://example.org"));
|
ui.add(crate::widgets::line(&mut d.url).desired_width(360.0).hint_text("https://example.org"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.radio_value(&mut d.web, false, tl!("Go to a page view"));
|
widgets::radio_value(ui, &mut d.web, false, tl!("Go to a page view"));
|
||||||
ui.add_enabled_ui(!d.web, |ui| {
|
ui.add_enabled_ui(!d.web, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_space(24.0);
|
ui.add_space(24.0);
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ impl PageRange {
|
|||||||
|
|
||||||
pub(crate) fn ui(&mut self, ui: &mut egui::Ui, count: usize) {
|
pub(crate) fn ui(&mut self, ui: &mut egui::Ui, count: usize) {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut self.all, true, tl!("All pages"));
|
widgets::radio_value(ui, &mut self.all, true, tl!("All pages"));
|
||||||
ui.radio_value(&mut self.all, false, tl!("Pages from"));
|
widgets::radio_value(ui, &mut self.all, false, tl!("Pages from"));
|
||||||
ui.add_enabled(!self.all, egui::DragValue::new(&mut self.from).range(1..=count.max(1)));
|
ui.add_enabled(!self.all, egui::DragValue::new(&mut self.from).range(1..=count.max(1)));
|
||||||
ui.label(tl!("to"));
|
ui.label(tl!("to"));
|
||||||
if self.to == 1 && self.from == 1 {
|
if self.to == 1 && self.from == 1 {
|
||||||
@@ -164,7 +164,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
|||||||
ui.label(tl!("Font: Helvetica"));
|
ui.label(tl!("Font: Helvetica"));
|
||||||
ui.label(tl!("Size"));
|
ui.label(tl!("Size"));
|
||||||
ui.add(egui::DragValue::new(&mut d.hf.font_size).range(4.0..=72.0).speed(0.5));
|
ui.add(egui::DragValue::new(&mut d.hf.font_size).range(4.0..=72.0).speed(0.5));
|
||||||
ui.checkbox(&mut d.hf.underline, tl!("Underline"));
|
ui.add(widgets::checkbox(&mut d.hf.underline, tl!("Underline")));
|
||||||
color_button(ui, &mut d.hf.color);
|
color_button(ui, &mut d.hf.color);
|
||||||
ui.add_space(16.0);
|
ui.add_space(16.0);
|
||||||
ui.label(format!("{} ({})", tl!("Margins"), units.label()));
|
ui.label(format!("{} ({})", tl!("Margins"), units.label()));
|
||||||
@@ -228,8 +228,8 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
|||||||
MarkKind::Watermark => {
|
MarkKind::Watermark => {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Source"));
|
ui.label(tl!("Source"));
|
||||||
ui.radio_value(&mut d.use_file, false, tl!("Text"));
|
widgets::radio_value(ui, &mut d.use_file, false, tl!("Text"));
|
||||||
ui.radio_value(&mut d.use_file, true, tl!("File"));
|
widgets::radio_value(ui, &mut d.use_file, true, tl!("File"));
|
||||||
});
|
});
|
||||||
if d.use_file {
|
if d.use_file {
|
||||||
browse |= file_source(ui, d, MarkKind::Watermark);
|
browse |= file_source(ui, d, MarkKind::Watermark);
|
||||||
@@ -243,7 +243,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add_enabled_ui(!d.use_file, |ui| ui.checkbox(&mut d.fit, tl!("Fit to page")));
|
ui.add_enabled_ui(!d.use_file, |ui| ui.add(widgets::checkbox(&mut d.fit, tl!("Fit to page"))));
|
||||||
ui.add_enabled_ui(!d.fit, |ui| {
|
ui.add_enabled_ui(!d.fit, |ui| {
|
||||||
ui.label(tl!("Size"));
|
ui.label(tl!("Size"));
|
||||||
if d.wm.font_size == 0.0 {
|
if d.wm.font_size == 0.0 {
|
||||||
@@ -256,7 +256,7 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Rotation"));
|
ui.label(tl!("Rotation"));
|
||||||
for r in [-45.0, 0.0, 45.0] {
|
for r in [-45.0, 0.0, 45.0] {
|
||||||
ui.radio_value(&mut d.wm.rotation, r, if r == 0.0 { tl!("None").to_string() } else { format!("{r}°") });
|
widgets::radio_value(ui, &mut d.wm.rotation, r, if r == 0.0 { tl!("None").to_string() } else { format!("{r}°") });
|
||||||
}
|
}
|
||||||
ui.add(egui::DragValue::new(&mut d.wm.rotation).range(-180.0..=180.0).suffix("°"));
|
ui.add(egui::DragValue::new(&mut d.wm.rotation).range(-180.0..=180.0).suffix("°"));
|
||||||
});
|
});
|
||||||
@@ -269,8 +269,8 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
|||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Location"));
|
ui.label(tl!("Location"));
|
||||||
ui.radio_value(&mut d.wm.behind, true, tl!("Appear behind page"));
|
widgets::radio_value(ui, &mut d.wm.behind, true, tl!("Appear behind page"));
|
||||||
ui.radio_value(&mut d.wm.behind, false, tl!("Appear on top of page"));
|
widgets::radio_value(ui, &mut d.wm.behind, false, tl!("Appear on top of page"));
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Offset from centre"));
|
ui.label(tl!("Offset from centre"));
|
||||||
@@ -284,10 +284,10 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens, kind: M
|
|||||||
}
|
}
|
||||||
MarkKind::Background => {
|
MarkKind::Background => {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.use_file, false, tl!("From colour"));
|
widgets::radio_value(ui, &mut d.use_file, false, tl!("From colour"));
|
||||||
ui.add_enabled_ui(!d.use_file, |ui| color_button(ui, &mut d.bg.color));
|
ui.add_enabled_ui(!d.use_file, |ui| color_button(ui, &mut d.bg.color));
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
ui.radio_value(&mut d.use_file, true, tl!("File"));
|
widgets::radio_value(ui, &mut d.use_file, true, tl!("File"));
|
||||||
});
|
});
|
||||||
if d.use_file {
|
if d.use_file {
|
||||||
browse |= file_source(ui, d, MarkKind::Background);
|
browse |= file_source(ui, d, MarkKind::Background);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
//! Measure objects: page-space clicks, live readings and persistent drawing scales.
|
//! Measure objects: page-space clicks, live readings and persistent drawing scales.
|
||||||
use crate::{LeftPanel, PdfCraftApp, QuickTool, canvas::DocView, theme::Tokens};
|
use crate::{LeftPanel, PdfCraftApp, QuickTool, canvas::DocView, theme::Tokens, widgets};
|
||||||
use egui::{Color32, Pos2, Stroke};
|
use egui::{Color32, Pos2, Stroke};
|
||||||
use pdfcraft_engine::{
|
use pdfcraft_engine::{
|
||||||
Document, Edit, Style,
|
Document, Edit, Style,
|
||||||
@@ -338,15 +338,15 @@ fn panel_body(app: &mut PdfCraftApp, ui: &mut egui::Ui, _t: &Tokens) {
|
|||||||
ui.add(crate::widgets::line(&mut state.label).desired_width(130.0).char_limit(128));
|
ui.add(crate::widgets::line(&mut state.label).desired_width(130.0).char_limit(128));
|
||||||
});
|
});
|
||||||
ui.separator();
|
ui.separator();
|
||||||
ui.checkbox(&mut state.snap_enabled, tl!("Snap to drawing"));
|
ui.add(widgets::checkbox(&mut state.snap_enabled, tl!("Snap to drawing")));
|
||||||
if state.snap_enabled {
|
if state.snap_enabled {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(&mut state.snaps.endpoints, tl!("Endpoints"));
|
ui.add(widgets::checkbox(&mut state.snaps.endpoints, tl!("Endpoints")));
|
||||||
ui.checkbox(&mut state.snaps.midpoints, tl!("Midpoints"));
|
ui.add(widgets::checkbox(&mut state.snaps.midpoints, tl!("Midpoints")));
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(&mut state.snaps.intersections, tl!("Intersections"));
|
ui.add(widgets::checkbox(&mut state.snaps.intersections, tl!("Intersections")));
|
||||||
ui.checkbox(&mut state.snaps.paths, tl!("Paths"));
|
ui.add(widgets::checkbox(&mut state.snaps.paths, tl!("Paths")));
|
||||||
});
|
});
|
||||||
ui.add(egui::Slider::new(&mut state.sensitivity, 1.0..=20.0).text(tl!("Sensitivity")));
|
ui.add(egui::Slider::new(&mut state.sensitivity, 1.0..=20.0).text(tl!("Sensitivity")));
|
||||||
}
|
}
|
||||||
@@ -365,7 +365,7 @@ fn panel_body(app: &mut PdfCraftApp, ui: &mut egui::Ui, _t: &Tokens) {
|
|||||||
if ui.button(tl!("Calibrate from two points")).clicked() {
|
if ui.button(tl!("Calibrate from two points")).clicked() {
|
||||||
tool = Some(Tool::Calibrate);
|
tool = Some(Tool::Calibrate);
|
||||||
}
|
}
|
||||||
ui.checkbox(&mut state.whole_page, tl!("Entire page"));
|
ui.add(widgets::checkbox(&mut state.whole_page, tl!("Entire page")));
|
||||||
if !state.whole_page {
|
if !state.whole_page {
|
||||||
ui.label(tl!("Viewport rectangle in page points (x, y, width, height)"));
|
ui.label(tl!("Viewport rectangle in page points (x, y, width, height)"));
|
||||||
for (i, name) in ["X", "Y", "Width", "Height"].iter().enumerate() {
|
for (i, name) in ["X", "Y", "Width", "Height"].iter().enumerate() {
|
||||||
|
|||||||
@@ -75,10 +75,10 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
|||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
};
|
};
|
||||||
group(ui, "Pages", &mut |ui| {
|
group(ui, "Pages", &mut |ui| {
|
||||||
ui.radio_value(&mut d.pages, OcrPages::All, tl!("All pages"));
|
widgets::radio_value(ui, &mut d.pages, OcrPages::All, tl!("All pages"));
|
||||||
ui.radio_value(&mut d.pages, OcrPages::Current, tl!("Current page"));
|
widgets::radio_value(ui, &mut d.pages, OcrPages::Current, tl!("Current page"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.pages, OcrPages::Range, tl!("From"));
|
widgets::radio_value(ui, &mut d.pages, OcrPages::Range, tl!("From"));
|
||||||
let on = d.pages == OcrPages::Range;
|
let on = d.pages == OcrPages::Range;
|
||||||
ui.add_enabled(on, egui::DragValue::new(&mut d.from).range(1..=pages));
|
ui.add_enabled(on, egui::DragValue::new(&mut d.from).range(1..=pages));
|
||||||
ui.label(tl!("to"));
|
ui.label(tl!("to"));
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ fn summary(r: &OptimizeReport) -> String {
|
|||||||
fn image_row(ui: &mut egui::Ui, id: &str, title: &str, s: &mut ImageSettings) {
|
fn image_row(ui: &mut egui::Ui, id: &str, title: &str, s: &mut ImageSettings) {
|
||||||
ui.label(egui::RichText::new(tl!(title)).font(theme::semibold(13.0)));
|
ui.label(egui::RichText::new(tl!(title)).font(theme::semibold(13.0)));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(&mut s.downsample, tl!("Bicubic downsampling to"));
|
ui.add(widgets::checkbox(&mut s.downsample, tl!("Bicubic downsampling to")));
|
||||||
ui.add_enabled(s.downsample, egui::DragValue::new(&mut s.target_ppi).range(9.0..=2400.0).suffix(" ppi"));
|
ui.add_enabled(s.downsample, egui::DragValue::new(&mut s.target_ppi).range(9.0..=2400.0).suffix(" ppi"));
|
||||||
ui.label(tl!("for images above"));
|
ui.label(tl!("for images above"));
|
||||||
ui.add_enabled(s.downsample, egui::DragValue::new(&mut s.above_ppi).range(9.0..=2400.0).suffix(" ppi"));
|
ui.add_enabled(s.downsample, egui::DragValue::new(&mut s.above_ppi).range(9.0..=2400.0).suffix(" ppi"));
|
||||||
@@ -144,7 +144,7 @@ fn image_row(ui: &mut egui::Ui, id: &str, title: &str, s: &mut ImageSettings) {
|
|||||||
|
|
||||||
fn discard_box(ui: &mut egui::Ui, list: &mut Vec<Hidden>, h: Hidden, label: &str) {
|
fn discard_box(ui: &mut egui::Ui, list: &mut Vec<Hidden>, h: Hidden, label: &str) {
|
||||||
let mut on = list.contains(&h);
|
let mut on = list.contains(&h);
|
||||||
if ui.checkbox(&mut on, tl!(label)).changed() {
|
if ui.add(widgets::checkbox(&mut on, tl!(label))).changed() {
|
||||||
if on {
|
if on {
|
||||||
list.push(h);
|
list.push(h);
|
||||||
} else {
|
} else {
|
||||||
@@ -184,10 +184,10 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut OptimizeDraft, t: &Tokens) -> (boo
|
|||||||
}
|
}
|
||||||
OptimizeTab::DiscardObjects => {
|
OptimizeTab::DiscardObjects => {
|
||||||
discard_box(ui, &mut d.discard, Hidden::LinksActionsScripts, "Discard all links, actions and JavaScript");
|
discard_box(ui, &mut d.discard, Hidden::LinksActionsScripts, "Discard all links, actions and JavaScript");
|
||||||
ui.checkbox(&mut s.discard_alternate_images, tl!("Discard alternate images"));
|
ui.add(widgets::checkbox(&mut s.discard_alternate_images, tl!("Discard alternate images")));
|
||||||
ui.checkbox(&mut s.discard_thumbnails, tl!("Discard embedded page thumbnails"));
|
ui.add(widgets::checkbox(&mut s.discard_thumbnails, tl!("Discard embedded page thumbnails")));
|
||||||
ui.checkbox(&mut s.discard_tags, tl!("Discard document tags"));
|
ui.add(widgets::checkbox(&mut s.discard_tags, tl!("Discard document tags")));
|
||||||
ui.checkbox(&mut s.discard_print_settings, tl!("Discard embedded print settings"));
|
ui.add(widgets::checkbox(&mut s.discard_print_settings, tl!("Discard embedded print settings")));
|
||||||
discard_box(ui, &mut d.discard, Hidden::Bookmarks, "Discard bookmarks");
|
discard_box(ui, &mut d.discard, Hidden::Bookmarks, "Discard bookmarks");
|
||||||
discard_box(ui, &mut d.discard, Hidden::FormFields, "Flatten form fields");
|
discard_box(ui, &mut d.discard, Hidden::FormFields, "Flatten form fields");
|
||||||
discard_box(ui, &mut d.discard, Hidden::HiddenLayers, "Discard hidden layer content");
|
discard_box(ui, &mut d.discard, Hidden::HiddenLayers, "Discard hidden layer content");
|
||||||
@@ -200,11 +200,11 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut OptimizeDraft, t: &Tokens) -> (boo
|
|||||||
discard_box(ui, &mut d.discard, Hidden::HiddenText, "Discard hidden text");
|
discard_box(ui, &mut d.discard, Hidden::HiddenText, "Discard hidden text");
|
||||||
}
|
}
|
||||||
OptimizeTab::CleanUp => {
|
OptimizeTab::CleanUp => {
|
||||||
ui.checkbox(&mut s.flate_unencoded, tl!("Use Flate to encode streams that are not encoded"));
|
ui.add(widgets::checkbox(&mut s.flate_unencoded, tl!("Use Flate to encode streams that are not encoded")));
|
||||||
ui.checkbox(&mut s.remove_invalid_links, tl!("Remove invalid links and bookmarks"));
|
ui.add(widgets::checkbox(&mut s.remove_invalid_links, tl!("Remove invalid links and bookmarks")));
|
||||||
ui.checkbox(&mut s.remove_unreferenced_dests, tl!("Remove unreferenced named destinations"));
|
ui.add(widgets::checkbox(&mut s.remove_unreferenced_dests, tl!("Remove unreferenced named destinations")));
|
||||||
ui.add_enabled(false, egui::Checkbox::new(&mut true, tl!("Compress document structure (object streams)")));
|
ui.add_enabled(false, widgets::checkbox(&mut true, tl!("Compress document structure (object streams)")));
|
||||||
ui.add_enabled(false, egui::Checkbox::new(&mut true, tl!("Remove unused objects and merge identical ones")));
|
ui.add_enabled(false, widgets::checkbox(&mut true, tl!("Remove unused objects and merge identical ones")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|||||||
@@ -135,15 +135,20 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
|||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tl!("Pages"));
|
ui.label(tl!("Pages"));
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.radio_value(&mut d.range, Range::All, tl!("All"));
|
widgets::radio_value(ui, &mut d.range, Range::All, tl!("All"));
|
||||||
ui.radio_value(&mut d.range, Range::Current, crate::i18n::fmt(tl!("Current page ({n})"), &[("n", &(current + 1).to_string())]));
|
widgets::radio_value(
|
||||||
|
ui,
|
||||||
|
&mut d.range,
|
||||||
|
Range::Current,
|
||||||
|
crate::i18n::fmt(tl!("Current page ({n})"), &[("n", &(current + 1).to_string())]),
|
||||||
|
);
|
||||||
let (mut a, mut b) = match d.range {
|
let (mut a, mut b) = match d.range {
|
||||||
Range::Pages(a, b) => (a, b),
|
Range::Pages(a, b) => (a, b),
|
||||||
_ => (1, count),
|
_ => (1, count),
|
||||||
};
|
};
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let on = matches!(d.range, Range::Pages(..));
|
let on = matches!(d.range, Range::Pages(..));
|
||||||
if ui.radio(on, tl!("From")).clicked() {
|
if ui.add(widgets::radio(on, tl!("From"))).clicked() {
|
||||||
d.range = Range::Pages(a, b);
|
d.range = Range::Pages(a, b);
|
||||||
}
|
}
|
||||||
let ra = ui.add(egui::DragValue::new(&mut a).range(1..=count));
|
let ra = ui.add(egui::DragValue::new(&mut a).range(1..=count));
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use pdfcraft_render::DocInfo;
|
|||||||
|
|
||||||
use crate::canvas::{DocView, PageXform};
|
use crate::canvas::{DocView, PageXform};
|
||||||
use crate::theme;
|
use crate::theme;
|
||||||
|
use crate::widgets;
|
||||||
|
|
||||||
const SELECT_BLUE: Color32 = Color32::from_rgb(0x14, 0x73, 0xE6);
|
const SELECT_BLUE: Color32 = Color32::from_rgb(0x14, 0x73, 0xE6);
|
||||||
/// Acrobat's field fill in Prepare Form (light blue).
|
/// Acrobat's field fill in Prepare Form (light blue).
|
||||||
@@ -918,7 +919,7 @@ const OPTION_FLAGS: [u32; 10] = {
|
|||||||
/// A check box for a `/Ff` bit (`inverted`: checked when the bit is clear).
|
/// A check box for a `/Ff` bit (`inverted`: checked when the bit is clear).
|
||||||
fn flag_box(ui: &mut egui::Ui, flags: &mut u32, bit: u32, inverted: bool, label: &str) {
|
fn flag_box(ui: &mut egui::Ui, flags: &mut u32, bit: u32, inverted: bool, label: &str) {
|
||||||
let mut on = (*flags & bit != 0) != inverted;
|
let mut on = (*flags & bit != 0) != inverted;
|
||||||
if ui.checkbox(&mut on, label).changed() {
|
if ui.add(widgets::checkbox(&mut on, label)).changed() {
|
||||||
if on != inverted {
|
if on != inverted {
|
||||||
*flags |= bit;
|
*flags |= bit;
|
||||||
} else {
|
} else {
|
||||||
@@ -965,15 +966,15 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Toke
|
|||||||
});
|
});
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
widgets::section_title(ui, tl!("Common Properties"));
|
widgets::section_title(ui, tl!("Common Properties"));
|
||||||
ui.checkbox(&mut d.read_only, tl!("Read Only"));
|
ui.add(widgets::checkbox(&mut d.read_only, tl!("Read Only")));
|
||||||
ui.checkbox(&mut d.required, tl!("Required"));
|
ui.add(widgets::checkbox(&mut d.required, tl!("Required")));
|
||||||
}
|
}
|
||||||
FieldTab::Appearance => {
|
FieldTab::Appearance => {
|
||||||
egui::Grid::new("field-appearance").num_columns(2).spacing([12.0, 10.0]).show(ui, |ui| {
|
egui::Grid::new("field-appearance").num_columns(2).spacing([12.0, 10.0]).show(ui, |ui| {
|
||||||
ui.label(tl!("Font Size:"));
|
ui.label(tl!("Font Size:"));
|
||||||
let mut auto = d.font_size == 0.0;
|
let mut auto = d.font_size == 0.0;
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
if ui.checkbox(&mut auto, tl!("Auto")).changed() {
|
if ui.add(widgets::checkbox(&mut auto, tl!("Auto"))).changed() {
|
||||||
d.font_size = if auto { 0.0 } else { 12.0 };
|
d.font_size = if auto { 0.0 } else { 12.0 };
|
||||||
}
|
}
|
||||||
ui.add_enabled(!auto, egui::DragValue::new(&mut d.font_size).range(2.0..=100.0).speed(0.25).suffix(" pt"));
|
ui.add_enabled(!auto, egui::DragValue::new(&mut d.font_size).range(2.0..=100.0).speed(0.25).suffix(" pt"));
|
||||||
@@ -1004,7 +1005,7 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Toke
|
|||||||
ui.push_id(("field-color", index), |ui| {
|
ui.push_id(("field-color", index), |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let mut none = slot.is_none();
|
let mut none = slot.is_none();
|
||||||
if ui.checkbox(&mut none, tl!("No color")).changed() {
|
if ui.add(widgets::checkbox(&mut none, tl!("No color"))).changed() {
|
||||||
*slot = if none { None } else { Some([0.0; 3]) };
|
*slot = if none { None } else { Some([0.0; 3]) };
|
||||||
}
|
}
|
||||||
if let Some(c) = crate::comments::swatch_grid(ui, *slot) {
|
if let Some(c) = crate::comments::swatch_grid(ui, *slot) {
|
||||||
@@ -1094,11 +1095,11 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Toke
|
|||||||
ui.end_row();
|
ui.end_row();
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.checkbox(&mut d.multiline, tl!("Multi-line"));
|
ui.add(widgets::checkbox(&mut d.multiline, tl!("Multi-line")));
|
||||||
flag_box(ui, &mut d.flags, ff::DO_NOT_SCROLL, true, tl!("Scroll long text"));
|
flag_box(ui, &mut d.flags, ff::DO_NOT_SCROLL, true, tl!("Scroll long text"));
|
||||||
flag_box(ui, &mut d.flags, ff::RICH_TEXT, false, tl!("Allow Rich Text Formatting"));
|
flag_box(ui, &mut d.flags, ff::RICH_TEXT, false, tl!("Allow Rich Text Formatting"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(&mut d.limit, tl!("Limit of"));
|
ui.add(widgets::checkbox(&mut d.limit, tl!("Limit of")));
|
||||||
ui.add_enabled(d.limit, egui::DragValue::new(&mut d.max_len).range(0..=10_000));
|
ui.add_enabled(d.limit, egui::DragValue::new(&mut d.max_len).range(0..=10_000));
|
||||||
ui.label(tl!("characters"));
|
ui.label(tl!("characters"));
|
||||||
});
|
});
|
||||||
@@ -1126,7 +1127,7 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Toke
|
|||||||
let mut checked = !d.default.is_empty() && d.default == on;
|
let mut checked = !d.default.is_empty() && d.default == on;
|
||||||
let label =
|
let label =
|
||||||
if d.kind == FormFieldKind::CheckBox { tl!("Check box is checked by default") } else { tl!("Button is checked by default") };
|
if d.kind == FormFieldKind::CheckBox { tl!("Check box is checked by default") } else { tl!("Button is checked by default") };
|
||||||
if ui.checkbox(&mut checked, label).changed() {
|
if ui.add(widgets::checkbox(&mut checked, label)).changed() {
|
||||||
d.default = if checked { on } else { String::new() };
|
d.default = if checked { on } else { String::new() };
|
||||||
}
|
}
|
||||||
if let Some(style) = d.check_style.as_mut() {
|
if let Some(style) = d.check_style.as_mut() {
|
||||||
@@ -1210,7 +1211,7 @@ pub(crate) fn body(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Toke
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.checkbox(&mut d.locked, tl!("Locked"));
|
ui.add(widgets::checkbox(&mut d.locked, tl!("Locked")));
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
let (mut apply, mut cancel) = (enter, false);
|
let (mut apply, mut cancel) = (enter, false);
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
@@ -1323,7 +1324,7 @@ fn format_tab(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Tokens) {
|
|||||||
ui.selectable_value(currency, c.to_string(), if c.is_empty() { tl!("None") } else { c });
|
ui.selectable_value(currency, c.to_string(), if c.is_empty() { tl!("None") } else { c });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.checkbox(prepend, tl!("Before the number"));
|
ui.add(widgets::checkbox(prepend, tl!("Before the number")));
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tl!("Negative number style:"));
|
ui.label(tl!("Negative number style:"));
|
||||||
@@ -1406,8 +1407,8 @@ fn format_tab(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Tokens) {
|
|||||||
|
|
||||||
fn validate_tab(ui: &mut egui::Ui, d: &mut FieldDraft) {
|
fn validate_tab(ui: &mut egui::Ui, d: &mut FieldDraft) {
|
||||||
let mut ranged = matches!(d.validate, Validate::Range { .. });
|
let mut ranged = matches!(d.validate, Validate::Range { .. });
|
||||||
ui.radio_value(&mut ranged, false, tl!("Field value is not validated"));
|
widgets::radio_value(ui, &mut ranged, false, tl!("Field value is not validated"));
|
||||||
ui.radio_value(&mut ranged, true, tl!("Field value is in range:"));
|
widgets::radio_value(ui, &mut ranged, true, tl!("Field value is in range:"));
|
||||||
if !ranged {
|
if !ranged {
|
||||||
d.validate = Validate::None;
|
d.validate = Validate::None;
|
||||||
return;
|
return;
|
||||||
@@ -1418,12 +1419,12 @@ fn validate_tab(ui: &mut egui::Ui, d: &mut FieldDraft) {
|
|||||||
if let Validate::Range { min, max } = &mut d.validate {
|
if let Validate::Range { min, max } = &mut d.validate {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let mut has = min.is_some();
|
let mut has = min.is_some();
|
||||||
ui.checkbox(&mut has, tl!("From:"));
|
ui.add(widgets::checkbox(&mut has, tl!("From:")));
|
||||||
let mut v = min.unwrap_or(0.0);
|
let mut v = min.unwrap_or(0.0);
|
||||||
ui.add_enabled(has, egui::DragValue::new(&mut v).speed(1.0));
|
ui.add_enabled(has, egui::DragValue::new(&mut v).speed(1.0));
|
||||||
*min = has.then_some(v);
|
*min = has.then_some(v);
|
||||||
let mut has = max.is_some();
|
let mut has = max.is_some();
|
||||||
ui.checkbox(&mut has, tl!("To:"));
|
ui.add(widgets::checkbox(&mut has, tl!("To:")));
|
||||||
let mut v = max.unwrap_or(100.0);
|
let mut v = max.unwrap_or(100.0);
|
||||||
ui.add_enabled(has, egui::DragValue::new(&mut v).speed(1.0));
|
ui.add_enabled(has, egui::DragValue::new(&mut v).speed(1.0));
|
||||||
*max = has.then_some(v);
|
*max = has.then_some(v);
|
||||||
@@ -1521,8 +1522,8 @@ fn calculate_tab(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Tokens
|
|||||||
Calculate::Notation(_) => Mode::Notation,
|
Calculate::Notation(_) => Mode::Notation,
|
||||||
};
|
};
|
||||||
let mut m = mode;
|
let mut m = mode;
|
||||||
ui.radio_value(&mut m, Mode::None, tl!("Value is not calculated"));
|
widgets::radio_value(ui, &mut m, Mode::None, tl!("Value is not calculated"));
|
||||||
ui.radio_value(&mut m, Mode::Simple, tl!("Value is the"));
|
widgets::radio_value(ui, &mut m, Mode::Simple, tl!("Value is the"));
|
||||||
if m != mode {
|
if m != mode {
|
||||||
d.calculate = match m {
|
d.calculate = match m {
|
||||||
Mode::None => Calculate::None,
|
Mode::None => Calculate::None,
|
||||||
@@ -1543,7 +1544,7 @@ fn calculate_tab(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Tokens
|
|||||||
egui::ScrollArea::vertical().max_height(110.0).show(ui, |ui| {
|
egui::ScrollArea::vertical().max_height(110.0).show(ui, |ui| {
|
||||||
for name in &d.others {
|
for name in &d.others {
|
||||||
let mut on = fields.contains(name);
|
let mut on = fields.contains(name);
|
||||||
if ui.checkbox(&mut on, name).changed() {
|
if ui.add(widgets::checkbox(&mut on, name)).changed() {
|
||||||
if on {
|
if on {
|
||||||
fields.push(name.clone());
|
fields.push(name.clone());
|
||||||
} else {
|
} else {
|
||||||
@@ -1557,7 +1558,9 @@ fn calculate_tab(ui: &mut egui::Ui, d: &mut FieldDraft, t: &crate::theme::Tokens
|
|||||||
Calculate::Notation(_) => Mode::Notation,
|
Calculate::Notation(_) => Mode::Notation,
|
||||||
_ => m,
|
_ => m,
|
||||||
};
|
};
|
||||||
if ui.radio_value(&mut m2, Mode::Notation, tl!("Simplified field notation:")).clicked() && !matches!(d.calculate, Calculate::Notation(_)) {
|
if widgets::radio_value(ui, &mut m2, Mode::Notation, tl!("Simplified field notation:")).clicked()
|
||||||
|
&& !matches!(d.calculate, Calculate::Notation(_))
|
||||||
|
{
|
||||||
d.calculate = Calculate::Notation(String::new());
|
d.calculate = Calculate::Notation(String::new());
|
||||||
}
|
}
|
||||||
if let Calculate::Notation(expr) = &mut d.calculate {
|
if let Calculate::Notation(expr) = &mut d.calculate {
|
||||||
|
|||||||
@@ -591,8 +591,8 @@ pub(crate) fn body(
|
|||||||
ui.label(tl!("Copies:"));
|
ui.label(tl!("Copies:"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.add(egui::DragValue::new(&mut d.copies).range(1..=999));
|
ui.add(egui::DragValue::new(&mut d.copies).range(1..=999));
|
||||||
ui.checkbox(&mut d.collate, tl!("Collate"));
|
ui.add(widgets::checkbox(&mut d.collate, tl!("Collate")));
|
||||||
ui.checkbox(&mut d.grayscale, tl!("Print in grayscale"));
|
ui.add(widgets::checkbox(&mut d.grayscale, tl!("Print in grayscale")));
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tl!("Two-sided:"));
|
ui.label(tl!("Two-sided:"));
|
||||||
@@ -641,9 +641,9 @@ pub(crate) fn body(
|
|||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
widgets::section_title(ui, tl!("Pages to Print"));
|
widgets::section_title(ui, tl!("Pages to Print"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.which, Which::All, tl!("All"));
|
widgets::radio_value(ui, &mut d.which, Which::All, tl!("All"));
|
||||||
ui.radio_value(&mut d.which, Which::Current, tl!("Current page"));
|
widgets::radio_value(ui, &mut d.which, Which::Current, tl!("Current page"));
|
||||||
ui.radio_value(&mut d.which, Which::Range, tl!("Pages"));
|
widgets::radio_value(ui, &mut d.which, Which::Range, tl!("Pages"));
|
||||||
let r = ui.add_enabled(
|
let r = ui.add_enabled(
|
||||||
d.which == Which::Range,
|
d.which == Which::Range,
|
||||||
crate::widgets::line(&mut d.range).hint_text(format!("1-{}", sizes.len())).desired_width(110.0),
|
crate::widgets::line(&mut d.range).hint_text(format!("1-{}", sizes.len())).desired_width(110.0),
|
||||||
@@ -655,7 +655,7 @@ pub(crate) fn body(
|
|||||||
// Offered only when pages were picked before the dialog opened.
|
// Offered only when pages were picked before the dialog opened.
|
||||||
if !d.selected.is_empty() {
|
if !d.selected.is_empty() {
|
||||||
let label = format!("{} ({})", tl!("Selected pages"), d.selected.len());
|
let label = format!("{} ({})", tl!("Selected pages"), d.selected.len());
|
||||||
ui.radio_value(&mut d.which, Which::Selected, label);
|
widgets::radio_value(ui, &mut d.which, Which::Selected, label);
|
||||||
}
|
}
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("More options:"));
|
ui.label(tl!("More options:"));
|
||||||
@@ -666,7 +666,7 @@ pub(crate) fn body(
|
|||||||
&[(Subset::All, tl!("All pages in range")), (Subset::Odd, tl!("Odd pages only")), (Subset::Even, tl!("Even pages only"))],
|
&[(Subset::All, tl!("All pages in range")), (Subset::Odd, tl!("Odd pages only")), (Subset::Even, tl!("Even pages only"))],
|
||||||
150.0,
|
150.0,
|
||||||
);
|
);
|
||||||
ui.checkbox(&mut d.reverse, tl!("Reverse pages"));
|
ui.add(widgets::checkbox(&mut d.reverse, tl!("Reverse pages")));
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
widgets::section_title(ui, tl!("Page Sizing & Handling"));
|
widgets::section_title(ui, tl!("Page Sizing & Handling"));
|
||||||
@@ -687,13 +687,13 @@ pub(crate) fn body(
|
|||||||
match d.handling {
|
match d.handling {
|
||||||
Handling::Size => {
|
Handling::Size => {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.size, SizeMode::Fit, tl!("Fit"));
|
widgets::radio_value(ui, &mut d.size, SizeMode::Fit, tl!("Fit"));
|
||||||
ui.radio_value(&mut d.size, SizeMode::Actual, tl!("Actual size"));
|
widgets::radio_value(ui, &mut d.size, SizeMode::Actual, tl!("Actual size"));
|
||||||
ui.radio_value(&mut d.size, SizeMode::Shrink, tl!("Shrink oversized pages"));
|
widgets::radio_value(ui, &mut d.size, SizeMode::Shrink, tl!("Shrink oversized pages"));
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let custom = matches!(d.size, SizeMode::Custom(_));
|
let custom = matches!(d.size, SizeMode::Custom(_));
|
||||||
if ui.radio(custom, tl!("Custom scale:")).clicked() {
|
if ui.add(widgets::radio(custom, tl!("Custom scale:"))).clicked() {
|
||||||
d.size = SizeMode::Custom(d.custom_scale);
|
d.size = SizeMode::Custom(d.custom_scale);
|
||||||
}
|
}
|
||||||
ui.add_enabled(custom, egui::DragValue::new(&mut d.custom_scale).range(1.0..=1000.0).suffix(" %"));
|
ui.add_enabled(custom, egui::DragValue::new(&mut d.custom_scale).range(1.0..=1000.0).suffix(" %"));
|
||||||
@@ -705,7 +705,7 @@ pub(crate) fn body(
|
|||||||
ui.add(egui::DragValue::new(&mut d.poster_scale).range(10.0..=1000.0).suffix(" %"));
|
ui.add(egui::DragValue::new(&mut d.poster_scale).range(10.0..=1000.0).suffix(" %"));
|
||||||
ui.label(tl!("Overlap:"));
|
ui.label(tl!("Overlap:"));
|
||||||
ui.add(egui::DragValue::new(&mut d.overlap).range(0.0..=144.0).suffix(" pt"));
|
ui.add(egui::DragValue::new(&mut d.overlap).range(0.0..=144.0).suffix(" pt"));
|
||||||
ui.checkbox(&mut d.cut_marks, tl!("Cut marks"));
|
ui.add(widgets::checkbox(&mut d.cut_marks, tl!("Cut marks")));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Handling::Multiple => {
|
Handling::Multiple => {
|
||||||
@@ -728,8 +728,8 @@ pub(crate) fn body(
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(&mut d.border, tl!("Print page border"));
|
ui.add(widgets::checkbox(&mut d.border, tl!("Print page border")));
|
||||||
ui.checkbox(&mut d.auto_rotate, tl!("Auto-rotate pages"));
|
ui.add(widgets::checkbox(&mut d.auto_rotate, tl!("Auto-rotate pages")));
|
||||||
});
|
});
|
||||||
if d.order == PageOrder::CutStack {
|
if d.order == PageOrder::CutStack {
|
||||||
ui.label(tl!(
|
ui.label(tl!(
|
||||||
@@ -759,9 +759,9 @@ pub(crate) fn body(
|
|||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.label(tl!("Orientation:"));
|
ui.label(tl!("Orientation:"));
|
||||||
ui.radio_value(&mut d.orientation, Orientation::Auto, tl!("Auto portrait/landscape"));
|
widgets::radio_value(ui, &mut d.orientation, Orientation::Auto, tl!("Auto portrait/landscape"));
|
||||||
ui.radio_value(&mut d.orientation, Orientation::Portrait, tl!("Portrait"));
|
widgets::radio_value(ui, &mut d.orientation, Orientation::Portrait, tl!("Portrait"));
|
||||||
ui.radio_value(&mut d.orientation, Orientation::Landscape, tl!("Landscape"));
|
widgets::radio_value(ui, &mut d.orientation, Orientation::Landscape, tl!("Landscape"));
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
widgets::section_title(ui, tl!("Comments & Forms"));
|
widgets::section_title(ui, tl!("Comments & Forms"));
|
||||||
|
|||||||
@@ -152,10 +152,10 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
|||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tl!("Encrypt"));
|
ui.label(tl!("Encrypt"));
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.radio_value(&mut p.encrypt_metadata, true, tl!("All document contents"));
|
widgets::radio_value(ui, &mut p.encrypt_metadata, true, tl!("All document contents"));
|
||||||
// Unencrypted metadata needs crypt filters (Acrobat 6.0 and later).
|
// Unencrypted metadata needs crypt filters (Acrobat 6.0 and later).
|
||||||
ui.add_enabled_ui(p.algorithm != A::Rc4_40 && p.algorithm != A::Rc4_128, |ui| {
|
ui.add_enabled_ui(p.algorithm != A::Rc4_40 && p.algorithm != A::Rc4_128, |ui| {
|
||||||
ui.radio_value(&mut p.encrypt_metadata, false, tl!("All document contents except metadata"));
|
widgets::radio_value(ui, &mut p.encrypt_metadata, false, tl!("All document contents except metadata"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
@@ -187,8 +187,8 @@ pub(crate) fn body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> (boo
|
|||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label("");
|
ui.label("");
|
||||||
ui.vertical(|ui| {
|
ui.vertical(|ui| {
|
||||||
ui.checkbox(&mut p.copy, tl!("Enable copying of text, images, and other content"));
|
ui.add(widgets::checkbox(&mut p.copy, tl!("Enable copying of text, images, and other content")));
|
||||||
ui.checkbox(&mut p.accessibility, tl!("Enable text access for screen reader devices for the visually impaired"));
|
ui.add(widgets::checkbox(&mut p.accessibility, tl!("Enable text access for screen reader devices for the visually impaired")));
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -248,9 +248,9 @@ impl PdfCraftApp {
|
|||||||
pub(crate) fn pages_body(ui: &mut egui::Ui, d: &mut PagesDraft, pages: usize, _t: &Tokens) -> (bool, bool) {
|
pub(crate) fn pages_body(ui: &mut egui::Ui, d: &mut PagesDraft, pages: usize, _t: &Tokens) -> (bool, bool) {
|
||||||
ui.label(egui::RichText::new(tl!("Mark Page Range")).font(crate::theme::semibold(18.0)));
|
ui.label(egui::RichText::new(tl!("Mark Page Range")).font(crate::theme::semibold(18.0)));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.radio_value(&mut d.current, true, tl!("Current page"));
|
widgets::radio_value(ui, &mut d.current, true, tl!("Current page"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.radio_value(&mut d.current, false, tl!("Pages from"));
|
widgets::radio_value(ui, &mut d.current, false, tl!("Pages from"));
|
||||||
ui.add_enabled(!d.current, egui::DragValue::new(&mut d.from).range(1..=pages));
|
ui.add_enabled(!d.current, egui::DragValue::new(&mut d.from).range(1..=pages));
|
||||||
ui.label(tl!("to"));
|
ui.label(tl!("to"));
|
||||||
ui.add_enabled(!d.current, egui::DragValue::new(&mut d.to).range(1..=pages));
|
ui.add_enabled(!d.current, egui::DragValue::new(&mut d.to).range(1..=pages));
|
||||||
@@ -266,14 +266,14 @@ pub(crate) fn search_body(ui: &mut egui::Ui, d: &mut SearchDraft, t: &Tokens) ->
|
|||||||
ui.set_width(420.0);
|
ui.set_width(420.0);
|
||||||
ui.label(egui::RichText::new(tl!("Find text and redact")).font(crate::theme::semibold(18.0)));
|
ui.label(egui::RichText::new(tl!("Find text and redact")).font(crate::theme::semibold(18.0)));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
ui.radio_value(&mut d.mode, SearchMode::Phrase, tl!("Single word or phrase"));
|
widgets::radio_value(ui, &mut d.mode, SearchMode::Phrase, tl!("Single word or phrase"));
|
||||||
let mut enter = false;
|
let mut enter = false;
|
||||||
ui.add_enabled_ui(d.mode == SearchMode::Phrase, |ui| {
|
ui.add_enabled_ui(d.mode == SearchMode::Phrase, |ui| {
|
||||||
let r = ui.add(crate::widgets::line(&mut d.text).hint_text(tl!("Text to find")).desired_width(380.0));
|
let r = ui.add(crate::widgets::line(&mut d.text).hint_text(tl!("Text to find")).desired_width(380.0));
|
||||||
enter = r.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
|
enter = r.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter));
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.radio_value(&mut d.mode, SearchMode::Words, tl!("Multiple words or phrases"));
|
widgets::radio_value(ui, &mut d.mode, SearchMode::Words, tl!("Multiple words or phrases"));
|
||||||
let mut import = false;
|
let mut import = false;
|
||||||
ui.add_enabled_ui(d.mode == SearchMode::Words, |ui| {
|
ui.add_enabled_ui(d.mode == SearchMode::Words, |ui| {
|
||||||
egui::ScrollArea::vertical().id_salt("redact-words").max_height(120.0).show(ui, |ui| {
|
egui::ScrollArea::vertical().id_salt("redact-words").max_height(120.0).show(ui, |ui| {
|
||||||
@@ -286,7 +286,7 @@ pub(crate) fn search_body(ui: &mut egui::Ui, d: &mut SearchDraft, t: &Tokens) ->
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.radio_value(&mut d.mode, SearchMode::Patterns, tl!("Patterns"));
|
widgets::radio_value(ui, &mut d.mode, SearchMode::Patterns, tl!("Patterns"));
|
||||||
ui.add_enabled_ui(d.mode == SearchMode::Patterns, |ui| {
|
ui.add_enabled_ui(d.mode == SearchMode::Patterns, |ui| {
|
||||||
egui::ComboBox::from_id_salt("redact-pattern").selected_text(tl!(d.pattern.label())).width(240.0).show_ui(ui, |ui| {
|
egui::ComboBox::from_id_salt("redact-pattern").selected_text(tl!(d.pattern.label())).width(240.0).show_ui(ui, |ui| {
|
||||||
for p in REDACT_PATTERNS {
|
for p in REDACT_PATTERNS {
|
||||||
@@ -320,7 +320,7 @@ pub(crate) fn props_body(ui: &mut egui::Ui, d: &mut RedactPrefs, _t: &Tokens) ->
|
|||||||
ui.label(tl!("Redacted area fill colour:"));
|
ui.label(tl!("Redacted area fill colour:"));
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let mut none = d.fill.is_none();
|
let mut none = d.fill.is_none();
|
||||||
if ui.checkbox(&mut none, tl!("No colour")).changed() {
|
if ui.add(widgets::checkbox(&mut none, tl!("No colour"))).changed() {
|
||||||
d.fill = if none { None } else { Some([0.0, 0.0, 0.0]) };
|
d.fill = if none { None } else { Some([0.0, 0.0, 0.0]) };
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -331,16 +331,16 @@ pub(crate) fn props_body(ui: &mut egui::Ui, d: &mut RedactPrefs, _t: &Tokens) ->
|
|||||||
}
|
}
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label("");
|
ui.label("");
|
||||||
ui.checkbox(&mut d.use_overlay, tl!("Use overlay text"));
|
ui.add(widgets::checkbox(&mut d.use_overlay, tl!("Use overlay text")));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
let on = d.use_overlay;
|
let on = d.use_overlay;
|
||||||
let l = ui.add_enabled(on, egui::RadioButton::new(!d.use_code, tl!("Custom text:")));
|
let l = ui.add_enabled(on, widgets::radio(!d.use_code, tl!("Custom text:")));
|
||||||
if l.clicked() {
|
if l.clicked() {
|
||||||
d.use_code = false;
|
d.use_code = false;
|
||||||
}
|
}
|
||||||
ui.add_enabled(on && !d.use_code, crate::widgets::line(&mut d.overlay).desired_width(220.0)).labelled_by(l.id);
|
ui.add_enabled(on && !d.use_code, crate::widgets::line(&mut d.overlay).desired_width(220.0)).labelled_by(l.id);
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
if ui.add_enabled(on, egui::RadioButton::new(d.use_code, tl!("Redaction code:"))).clicked() {
|
if ui.add_enabled(on, widgets::radio(d.use_code, tl!("Redaction code:"))).clicked() {
|
||||||
d.use_code = true;
|
d.use_code = true;
|
||||||
}
|
}
|
||||||
let codes_on = on && d.use_code;
|
let codes_on = on && d.use_code;
|
||||||
@@ -361,7 +361,7 @@ pub(crate) fn props_body(ui: &mut egui::Ui, d: &mut RedactPrefs, _t: &Tokens) ->
|
|||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
for &code in row {
|
for &code in row {
|
||||||
let mut picked = d.codes.contains(&code);
|
let mut picked = d.codes.contains(&code);
|
||||||
if ui.add_sized([62.0, 18.0], egui::Checkbox::new(&mut picked, code)).changed() {
|
if ui.add_sized([62.0, 18.0], widgets::checkbox(&mut picked, code)).changed() {
|
||||||
d.codes.retain(|c| *c != code);
|
d.codes.retain(|c| *c != code);
|
||||||
if picked {
|
if picked {
|
||||||
d.codes.push(code);
|
d.codes.push(code);
|
||||||
@@ -386,7 +386,7 @@ pub(crate) fn props_body(ui: &mut egui::Ui, d: &mut RedactPrefs, _t: &Tokens) ->
|
|||||||
ui.add_enabled_ui(on, |ui| {
|
ui.add_enabled_ui(on, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
let mut auto = look.size <= 0.0;
|
let mut auto = look.size <= 0.0;
|
||||||
if ui.checkbox(&mut auto, tl!("Auto-size text to fit redaction region")).changed() {
|
if ui.add(widgets::checkbox(&mut auto, tl!("Auto-size text to fit redaction region"))).changed() {
|
||||||
look.size = if auto { 0.0 } else { 10.0 };
|
look.size = if auto { 0.0 } else { 10.0 };
|
||||||
}
|
}
|
||||||
if !auto {
|
if !auto {
|
||||||
@@ -403,13 +403,13 @@ pub(crate) fn props_body(ui: &mut egui::Ui, d: &mut RedactPrefs, _t: &Tokens) ->
|
|||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label("");
|
ui.label("");
|
||||||
ui.add_enabled(on, egui::Checkbox::new(&mut look.repeat, tl!("Repeat overlay text")));
|
ui.add_enabled(on, widgets::checkbox(&mut look.repeat, tl!("Repeat overlay text")));
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tl!("Text alignment:"));
|
ui.label(tl!("Text alignment:"));
|
||||||
ui.add_enabled_ui(on, |ui| {
|
ui.add_enabled_ui(on, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
for (a, label) in [(0u8, tl!("Left")), (1, tl!("Center")), (2, tl!("Right"))] {
|
for (a, label) in [(0u8, tl!("Left")), (1, tl!("Center")), (2, tl!("Right"))] {
|
||||||
ui.radio_value(&mut look.align, a, label);
|
widgets::radio_value(ui, &mut look.align, a, label);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -480,7 +480,7 @@ pub(crate) fn hidden_body(ui: &mut egui::Ui, d: &mut HiddenDraft, t: &Tokens) ->
|
|||||||
for (h, n, on) in d.found.iter_mut() {
|
for (h, n, on) in d.found.iter_mut() {
|
||||||
ui.add_enabled_ui(*n > 0, |ui| {
|
ui.add_enabled_ui(*n > 0, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
ui.checkbox(on, tl!(h.label()));
|
ui.add(widgets::checkbox(on, tl!(h.label())));
|
||||||
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
|
||||||
ui.label(egui::RichText::new(if *n == 0 { tl!("None found").to_string() } else { n.to_string() }).color(t.text_muted));
|
ui.label(egui::RichText::new(if *n == 0 { tl!("None found").to_string() } else { n.to_string() }).color(t.text_muted));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ use egui::{Color32, FontId, TextFormat};
|
|||||||
|
|
||||||
use crate::canvas::DocView;
|
use crate::canvas::DocView;
|
||||||
use crate::theme::{self, Tokens};
|
use crate::theme::{self, Tokens};
|
||||||
|
use crate::widgets;
|
||||||
|
|
||||||
/// Glyphs of context shown on each side of a match.
|
/// Glyphs of context shown on each side of a match.
|
||||||
const CONTEXT: usize = 36;
|
const CONTEXT: usize = 36;
|
||||||
@@ -28,8 +29,8 @@ pub(crate) fn panel(ui: &mut egui::Ui, t: &Tokens, view: &mut DocView, pages: us
|
|||||||
let w = &mut ui.visuals_mut().widgets;
|
let w = &mut ui.visuals_mut().widgets;
|
||||||
w.inactive.bg_stroke = egui::Stroke::new(1.0, t.border);
|
w.inactive.bg_stroke = egui::Stroke::new(1.0, t.border);
|
||||||
w.inactive.bg_fill = t.hover;
|
w.inactive.bg_fill = t.hover;
|
||||||
let a = ui.checkbox(&mut find.whole_words, tl!("Whole words only")).changed();
|
let a = ui.add(widgets::checkbox(&mut find.whole_words, tl!("Whole words only"))).changed();
|
||||||
let b = ui.checkbox(&mut find.case_sensitive, tl!("Case-sensitive")).changed();
|
let b = ui.add(widgets::checkbox(&mut find.case_sensitive, tl!("Case-sensitive"))).changed();
|
||||||
if a || b {
|
if a || b {
|
||||||
find.case_query.clear();
|
find.case_query.clear();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -652,8 +652,8 @@ fn configure(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> bool {
|
|||||||
// Browse… is desktop-only.
|
// Browse… is desktop-only.
|
||||||
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
|
#[cfg_attr(target_arch = "wasm32", allow(unused_mut))]
|
||||||
let mut browse = false;
|
let mut browse = false;
|
||||||
ui.radio_value(&mut d.new_id.create, false, tl!("Use a Digital ID from a file"));
|
widgets::radio_value(ui, &mut d.new_id.create, false, tl!("Use a Digital ID from a file"));
|
||||||
ui.radio_value(&mut d.new_id.create, true, tl!("Create a new Digital ID (self-signed, saved to a password-protected file)"));
|
widgets::radio_value(ui, &mut d.new_id.create, true, tl!("Create a new Digital ID (self-signed, saved to a password-protected file)"));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
let n = &mut d.new_id;
|
let n = &mut d.new_id;
|
||||||
if n.create {
|
if n.create {
|
||||||
@@ -831,24 +831,24 @@ fn sign_as(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) -> bool {
|
|||||||
preview(ui, t, &entry.name, d, graphic.as_ref(), &mut app.sign_graphic_preview);
|
preview(ui, t, &entry.name, d, graphic.as_ref(), &mut app.sign_graphic_preview);
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
let a = &mut d.appearance;
|
let a = &mut d.appearance;
|
||||||
ui.checkbox(&mut a.name, tl!("Name"));
|
ui.add(widgets::checkbox(&mut a.name, tl!("Name")));
|
||||||
ui.checkbox(&mut a.date, tl!("Date"));
|
ui.add(widgets::checkbox(&mut a.date, tl!("Date")));
|
||||||
ui.checkbox(&mut a.reason, tl!("Reason"));
|
ui.add(widgets::checkbox(&mut a.reason, tl!("Reason")));
|
||||||
ui.checkbox(&mut a.location, tl!("Location"));
|
ui.add(widgets::checkbox(&mut a.location, tl!("Location")));
|
||||||
ui.checkbox(&mut a.distinguished_name, tl!("Distinguished name"));
|
ui.add(widgets::checkbox(&mut a.distinguished_name, tl!("Distinguished name")));
|
||||||
ui.checkbox(&mut a.labels, tl!("Labels"));
|
ui.add(widgets::checkbox(&mut a.labels, tl!("Labels")));
|
||||||
});
|
});
|
||||||
// Acrobat: Configure Signature Appearance, Text, Draw or Image.
|
// Acrobat: Configure Signature Appearance, Text, Draw or Image.
|
||||||
ui.horizontal_wrapped(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
ui.label(tl!("Graphic"));
|
ui.label(tl!("Graphic"));
|
||||||
ui.radio_value(&mut d.graphic, SignGraphic::Name, tl!("Signer's name"));
|
widgets::radio_value(ui, &mut d.graphic, SignGraphic::Name, tl!("Signer's name"));
|
||||||
ui.add_enabled_ui(app.signature.is_some(), |ui| {
|
ui.add_enabled_ui(app.signature.is_some(), |ui| {
|
||||||
if ui.radio(d.graphic == SignGraphic::FillSign, tl!("Fill & Sign signature")).clicked() {
|
if ui.add(widgets::radio(d.graphic == SignGraphic::FillSign, tl!("Fill & Sign signature"))).clicked() {
|
||||||
d.graphic = SignGraphic::FillSign;
|
d.graphic = SignGraphic::FillSign;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.add_enabled_ui(d.image.is_some(), |ui| {
|
ui.add_enabled_ui(d.image.is_some(), |ui| {
|
||||||
if ui.radio(matches!(d.graphic, SignGraphic::Image(_)), tl!("Image")).clicked()
|
if ui.add(widgets::radio(matches!(d.graphic, SignGraphic::Image(_)), tl!("Image"))).clicked()
|
||||||
&& let Some(image) = &d.image
|
&& let Some(image) = &d.image
|
||||||
{
|
{
|
||||||
d.graphic = SignGraphic::Image(image.clone());
|
d.graphic = SignGraphic::Image(image.clone());
|
||||||
|
|||||||
@@ -188,6 +188,76 @@ pub fn menu_item(ui: &mut egui::Ui, label: &str, shortcut: &str) -> Response {
|
|||||||
ui.add(egui::Button::new(label).shortcut_text(shortcut))
|
ui.add(egui::Button::new(label).shortcut_text(shortcut))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Corner radius of a checkbox's box. With the widgets' radius (`Tokens::radius`, 6) egui's 14 px
|
||||||
|
/// box is a circle, the shape of a radio button, and View ▸ Page display has both.
|
||||||
|
const CHECKBOX_RADIUS: u8 = 3;
|
||||||
|
|
||||||
|
/// A checkbox whose box is an outlined rounded square, never mistaken for a radio button. Every
|
||||||
|
/// checkbox goes through here: clippy.toml disallows `ui.checkbox` and `egui::Checkbox`.
|
||||||
|
pub fn checkbox<'a>(checked: &'a mut bool, atoms: impl egui::IntoAtoms<'a>) -> impl egui::Widget + 'a {
|
||||||
|
#[expect(clippy::disallowed_methods, reason = "the one place that styles the box")]
|
||||||
|
let widget = egui::Checkbox::new(checked, atoms);
|
||||||
|
toggle(widget, Some(CHECKBOX_RADIUS))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`checkbox`] without a label: the selection box on Organize Pages thumbnails.
|
||||||
|
pub fn checkbox_without_text(checked: &mut bool) -> impl egui::Widget + '_ {
|
||||||
|
#[expect(clippy::disallowed_methods, reason = "the one place that styles the box")]
|
||||||
|
let widget = egui::Checkbox::without_text(checked);
|
||||||
|
toggle(widget, Some(CHECKBOX_RADIUS))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A radio button with an outline and a fill, like [`checkbox`]. Every radio button goes through
|
||||||
|
/// here or [`radio_value`]: clippy.toml disallows `ui.radio`, `ui.radio_value` and
|
||||||
|
/// `egui::RadioButton`.
|
||||||
|
pub fn radio<'a>(selected: bool, atoms: impl egui::IntoAtoms<'a>) -> impl egui::Widget + 'a {
|
||||||
|
#[expect(clippy::disallowed_methods, reason = "the one place that styles the circle")]
|
||||||
|
let widget = egui::RadioButton::new(selected, atoms);
|
||||||
|
toggle(widget, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// [`radio`] selected while `*current == alternative`; a click selects it (`Ui::radio_value`).
|
||||||
|
pub fn radio_value<'a, V: PartialEq>(ui: &mut egui::Ui, current: &mut V, alternative: V, atoms: impl egui::IntoAtoms<'a>) -> Response {
|
||||||
|
let mut response = ui.add(radio(*current == alternative, atoms));
|
||||||
|
if response.clicked() && *current != alternative {
|
||||||
|
*current = alternative;
|
||||||
|
response.mark_changed();
|
||||||
|
}
|
||||||
|
response
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The box of a checkbox or the circle of a radio button. egui draws it with the widgets' visuals
|
||||||
|
/// and has no style of its own for it, so it is set in a scope around the widget, keeping what
|
||||||
|
/// the caller set (dialogs and the search panel already outline theirs):
|
||||||
|
/// - an outline, darker on hover;
|
||||||
|
/// - on the light theme, the plain fill (`Tokens::hover`), as dialogs have: the field colour is
|
||||||
|
/// the panels' white, so the box would vanish;
|
||||||
|
/// - for a checkbox, a corner radius small enough to keep the box square.
|
||||||
|
fn toggle<'a>(widget: impl egui::Widget + 'a, radius: Option<u8>) -> impl egui::Widget + 'a {
|
||||||
|
move |ui: &mut egui::Ui| {
|
||||||
|
ui.scope(|ui| {
|
||||||
|
let t = Tokens::get(ui.ctx());
|
||||||
|
let w = &mut ui.visuals_mut().widgets;
|
||||||
|
if let Some(radius) = radius {
|
||||||
|
for v in [&mut w.noninteractive, &mut w.inactive, &mut w.hovered, &mut w.active, &mut w.open] {
|
||||||
|
v.corner_radius = CornerRadius::same(radius);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !t.dark() && w.inactive.bg_fill == t.field {
|
||||||
|
w.inactive.bg_fill = t.hover;
|
||||||
|
}
|
||||||
|
if w.inactive.bg_stroke.is_empty() {
|
||||||
|
w.inactive.bg_stroke = Stroke::new(1.0, t.border);
|
||||||
|
}
|
||||||
|
if w.hovered.bg_stroke.is_empty() {
|
||||||
|
w.hovered.bg_stroke = Stroke::new(1.0, t.text_muted);
|
||||||
|
}
|
||||||
|
ui.add(widget)
|
||||||
|
})
|
||||||
|
.inner
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn section_title(ui: &mut egui::Ui, text: &str) {
|
pub fn section_title(ui: &mut egui::Ui, text: &str) {
|
||||||
let t = Tokens::get(ui.ctx());
|
let t = Tokens::get(ui.ctx());
|
||||||
ui.add_space(10.0);
|
ui.add_space(10.0);
|
||||||
@@ -443,6 +513,70 @@ mod tests {
|
|||||||
crate::i18n::set_current(previous);
|
crate::i18n::set_current(previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// View ▸ Page display mixes radio buttons and checkboxes. With the theme's 6 px widget radius
|
||||||
|
/// egui's 14 px checkbox box drew as a circle, and on the light theme an unticked box or an
|
||||||
|
/// unselected radio button was white on white with no outline, so it couldn't be seen.
|
||||||
|
#[test]
|
||||||
|
fn checkboxes_and_radio_buttons_are_outlined_filled_and_distinct() {
|
||||||
|
for kind in [theme::ThemeKind::Light, theme::ThemeKind::Dark] {
|
||||||
|
let ctx = egui::Context::default();
|
||||||
|
theme::apply(&ctx, kind);
|
||||||
|
let t = Tokens::for_kind(kind);
|
||||||
|
let side = ctx.global_style().spacing.icon_width;
|
||||||
|
let output = ctx.run_ui(egui::RawInput::default(), |ui| {
|
||||||
|
ui.add(checkbox(&mut false, "Show cover page in two-page view"));
|
||||||
|
ui.add(radio(false, "Single page"));
|
||||||
|
});
|
||||||
|
let boxes: Vec<_> = output
|
||||||
|
.shapes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| match &s.shape {
|
||||||
|
egui::Shape::Rect(r) if (r.rect.width() - side).abs() < 0.5 && (r.rect.height() - side).abs() < 0.5 => Some(r),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let circles: Vec<_> = output
|
||||||
|
.shapes
|
||||||
|
.iter()
|
||||||
|
.filter_map(|s| match &s.shape {
|
||||||
|
egui::Shape::Circle(c) if (c.radius * 2.0 - side).abs() < 1.0 => Some(c),
|
||||||
|
_ => None,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
assert_eq!((boxes.len(), circles.len()), (1, 1), "{kind:?}: one checkbox box and one radio circle");
|
||||||
|
let r = boxes[0].corner_radius;
|
||||||
|
let radius = r.nw.max(r.ne).max(r.sw).max(r.se);
|
||||||
|
assert!(f32::from(radius) <= side / 4.0, "{kind:?}: a {side} px box with corner radius {radius} looks like a radio button");
|
||||||
|
for (what, fill, edge) in [("checkbox", boxes[0].fill, boxes[0].stroke), ("radio button", circles[0].fill, circles[0].stroke)] {
|
||||||
|
assert!(fill != t.panel && fill != t.card, "{kind:?}: an unselected {what} is filled like the panel behind it");
|
||||||
|
assert!(
|
||||||
|
edge.width >= 1.0 && edge.color != t.panel && edge.color != t.card,
|
||||||
|
"{kind:?}: an unselected {what} needs an outline, got {edge:?}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
output.drop_without_applying_deltas();
|
||||||
|
}
|
||||||
|
// An outline the caller set (dialogs) is kept.
|
||||||
|
let ctx = egui::Context::default();
|
||||||
|
let own = Stroke::new(1.5, Color32::RED);
|
||||||
|
let output = ctx.run_ui(egui::RawInput::default(), |ui| {
|
||||||
|
ui.visuals_mut().widgets.inactive.bg_stroke = own;
|
||||||
|
ui.add(checkbox(&mut true, "Collate"));
|
||||||
|
ui.add(radio(false, "All"));
|
||||||
|
});
|
||||||
|
let kept = output
|
||||||
|
.shapes
|
||||||
|
.iter()
|
||||||
|
.filter(|s| match &s.shape {
|
||||||
|
egui::Shape::Rect(r) => r.stroke == own,
|
||||||
|
egui::Shape::Circle(c) => c.stroke == own,
|
||||||
|
_ => false,
|
||||||
|
})
|
||||||
|
.count();
|
||||||
|
assert_eq!(kept, 2, "the checkbox and the radio button keep the caller's outline");
|
||||||
|
output.drop_without_applying_deltas();
|
||||||
|
}
|
||||||
|
|
||||||
fn toast_rect(app: &mut PdfCraftApp, ctx: &egui::Context, width: f32, now: &mut f64) -> Rect {
|
fn toast_rect(app: &mut PdfCraftApp, ctx: &egui::Context, width: f32, now: &mut f64) -> Rect {
|
||||||
// Let the Area settle after a notice/viewport change, without advancing to
|
// Let the Area settle after a notice/viewport change, without advancing to
|
||||||
// expiry. No native rendering or renderer worker is needed for this layout.
|
// expiry. No native rendering or renderer worker is needed for this layout.
|
||||||
|
|||||||
Reference in New Issue
Block a user