mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-10-29 20:47:32 -06:00
Improve reading-colour preferences and add a settings shortcut (#2627)
* M3: improve reading-colour preferences and add settings shortcut * M3: clarify the disabled reading-colour preview * M3: adapt rebased preferences to current UI dependencies
This commit is contained in:
@@ -69,10 +69,10 @@ pub fn tab_strip(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
app.active = None;
|
||||
app.combine_tab.focused = false;
|
||||
}
|
||||
// Keep the two icon buttons, Discord's text/padding and three gaps outside the scrolling tabs.
|
||||
// Reserve the three icon buttons, Discord and their gaps outside the scrolling tabs.
|
||||
let controls_width = ui.fonts_mut(|f| f.layout_no_wrap("Discord".into(), theme::medium(13.0), t.text).size().x)
|
||||
+ 2.0 * button
|
||||
+ 50.0
|
||||
+ 3.0 * button
|
||||
+ 54.0
|
||||
+ window_controls::width(&controls.right);
|
||||
// Open stays in sight after the tabs: its icon, text and padding, and the gaps around it.
|
||||
let open_width = ui.fonts_mut(|f| f.layout_no_wrap(tl!("Open").into(), theme::medium(13.0), t.text).size().x) + 38.0 + 12.0;
|
||||
@@ -287,6 +287,9 @@ pub fn tab_strip(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
});
|
||||
ui.add_space(window_controls::SIDE_GAP - 4.0);
|
||||
}
|
||||
if icons::button(ui, "settings", button, false, tl!("Preferences")).clicked() {
|
||||
app.execute("app.preferences");
|
||||
}
|
||||
let (icon, label) = match app.theme_preference {
|
||||
ThemePreference::System => ("monitor", tl!("Use system setting")),
|
||||
ThemePreference::Light => ("sun", tl!("Light gray")),
|
||||
|
||||
@@ -254,6 +254,10 @@ fn english(labeled: Labeled<'_>) -> Option<String> {
|
||||
}
|
||||
|
||||
fn translate(english: &'static str) -> String {
|
||||
// The bundled UI fonts do not all cover the menu's mathematical ellipsis.
|
||||
if english == "\u{22ef}" {
|
||||
return "...".to_owned();
|
||||
}
|
||||
tl!(english).to_owned()
|
||||
}
|
||||
|
||||
|
||||
@@ -265,18 +265,6 @@ pub(crate) fn document_js_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
||||
close
|
||||
}
|
||||
|
||||
/// The label for a [`pdfcraft_render::ReadingColors`] mode name (`mode_name`).
|
||||
fn reading_mode_label(mode: &str) -> &'static str {
|
||||
match mode {
|
||||
"black-on-white" => "Black on white",
|
||||
"white-on-black" => "White on black",
|
||||
"yellow-on-black" => "Yellow on black",
|
||||
"green-on-black" => "Green on black",
|
||||
"custom" => "Custom",
|
||||
_ => "Off",
|
||||
}
|
||||
}
|
||||
|
||||
/// The height the Preferences dialog keeps below its scrolling settings for the OK row: the
|
||||
/// button, and the space above it.
|
||||
pub(crate) const PREFERENCES_BUTTON_ROW: f32 = 52.0;
|
||||
@@ -394,6 +382,8 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
||||
ui.add_space(8.0);
|
||||
crate::appearance::preferences_section(ui, app, t);
|
||||
ui.add_space(8.0);
|
||||
reading_colours_section(ui, app, t);
|
||||
ui.add_space(8.0);
|
||||
ui.label(egui::RichText::new(tl!("Documents and view")).font(theme::semibold(13.0)));
|
||||
ui.horizontal(|ui| {
|
||||
ui.label(tl!("Default workspace mode"));
|
||||
@@ -478,70 +468,6 @@ pub(crate) fn preferences_body(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tok
|
||||
.on_hover_text(tl!("Show a basic title bar for the window buttons instead of the tab strip. GNOME doesn't draw title bars for apps, so this one doesn't follow its theme."));
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
// Reading colours (Acrobat: Replace Document Colors): solid text and line-art paints are
|
||||
// remapped while rendering, and the page clears to the background colour. The preference
|
||||
// is app-wide: every open document follows it, and documents open with it.
|
||||
ui.label(egui::RichText::new(tl!("Reading colours")).font(theme::semibold(13.0)));
|
||||
ui.horizontal(|ui| {
|
||||
let label = ui.label(tl!("Replace document colours"));
|
||||
let mode = app.reading_colors.mode_name();
|
||||
craft_ui::ComboBox::from_id_salt("reading-colors")
|
||||
.selected_text(tl!(reading_mode_label(mode)))
|
||||
.show_ui(ui, |ui| {
|
||||
for (m, label) in [
|
||||
("off", "Off"),
|
||||
("black-on-white", "Black on white"),
|
||||
("white-on-black", "White on black"),
|
||||
("yellow-on-black", "Yellow on black"),
|
||||
("green-on-black", "Green on black"),
|
||||
("custom", "Custom"),
|
||||
] {
|
||||
if ui.selectable_label(mode == m, tl!(label)).clicked() {
|
||||
match m {
|
||||
"off" => app.set_reading_colors(pdfcraft_render::ReadingColors::default()),
|
||||
"custom" => {
|
||||
// Custom starts from the colours already chosen, or black on white.
|
||||
let colors = app.reading_colors.params.map_or(pdfcraft_render::ReadingColors::BLACK_ON_WHITE, |params| {
|
||||
pdfcraft_render::ReadingColors { params: Some(params) }
|
||||
});
|
||||
app.set_reading_colors(colors);
|
||||
}
|
||||
// `parse` knows these names; a preset comes with its own flags.
|
||||
preset => {
|
||||
if let Ok(colors) = pdfcraft_render::ReadingColors::parse(preset, None, None, None, None) {
|
||||
app.set_reading_colors(colors);
|
||||
}
|
||||
}
|
||||
}
|
||||
ui.close();
|
||||
}
|
||||
}
|
||||
})
|
||||
.response
|
||||
.labelled_by(label.id);
|
||||
});
|
||||
if app.reading_colors.params.is_some() {
|
||||
let mut changed = false;
|
||||
if let Some(params) = app.reading_colors.params.as_mut() {
|
||||
ui.horizontal(|ui| {
|
||||
let text = ui.label(tl!("Text"));
|
||||
changed |= ui.color_edit_button_srgb(&mut params.text).labelled_by(text.id).changed();
|
||||
let background = ui.label(tl!("Page background"));
|
||||
changed |= ui.color_edit_button_srgb(&mut params.background).labelled_by(background.id).changed();
|
||||
});
|
||||
if ui.add(crate::widgets::checkbox(&mut params.only_black, tl!("Only change the colour of black text or line art"))).changed() {
|
||||
changed = true;
|
||||
}
|
||||
if ui.add(crate::widgets::checkbox(&mut params.line_art, tl!("Change the colour of line art as well as text"))).changed() {
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if changed {
|
||||
app.set_reading_colors(app.reading_colors);
|
||||
}
|
||||
}
|
||||
ui.label(egui::RichText::new(tl!("Colours are remapped while rendering; the document itself never changes.")).small().color(t.text_muted));
|
||||
ui.add_space(8.0);
|
||||
// Identity: the author of new comments (Acrobat: Preferences ▸ Identity).
|
||||
ui.label(egui::RichText::new(tl!("Identity")).font(theme::semibold(13.0)));
|
||||
ui.horizontal(|ui| {
|
||||
@@ -679,6 +605,75 @@ fn date_format(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) {
|
||||
);
|
||||
}
|
||||
|
||||
fn reading_colours_section(ui: &mut egui::Ui, app: &mut PdfCraftApp, t: &Tokens) {
|
||||
// Reading colours (Acrobat: Replace Document Colors): solid text and line-art paints are
|
||||
// remapped while rendering, and the page clears to the background colour. The preference
|
||||
// is app-wide: every open document follows it, and documents open with it.
|
||||
ui.label(egui::RichText::new(tl!("Reading colours")).font(theme::semibold(13.0)));
|
||||
let mode = app.reading_colors.mode_name();
|
||||
ui.horizontal_wrapped(|ui| {
|
||||
for (name, label, colors) in [
|
||||
("off", "Off", pdfcraft_render::ReadingColors::default()),
|
||||
("black-on-white", "Black on white", pdfcraft_render::ReadingColors::BLACK_ON_WHITE),
|
||||
("white-on-black", "White on black", pdfcraft_render::ReadingColors::WHITE_ON_BLACK),
|
||||
("yellow-on-black", "Yellow on black", pdfcraft_render::ReadingColors::YELLOW_ON_BLACK),
|
||||
("green-on-black", "Green on black", pdfcraft_render::ReadingColors::GREEN_ON_BLACK),
|
||||
] {
|
||||
let params = colors.params.unwrap_or(pdfcraft_render::ReadingParams {
|
||||
text: [0, 0, 0],
|
||||
background: [255, 255, 255],
|
||||
only_black: false,
|
||||
line_art: true,
|
||||
});
|
||||
ui.vertical(|ui| {
|
||||
ui.set_width(104.0);
|
||||
let sample = egui::RichText::new("Aa").size(22.0).color(egui::Color32::from_rgb(params.text[0], params.text[1], params.text[2]));
|
||||
let button = egui::Button::new(sample)
|
||||
.fill(egui::Color32::from_rgb(params.background[0], params.background[1], params.background[2]))
|
||||
.min_size(egui::vec2(94.0, 44.0))
|
||||
.stroke(egui::Stroke::new(if mode == name { 2.0 } else { 1.0 }, if mode == name { t.accent } else { t.border }));
|
||||
let response = ui.add(button);
|
||||
response.widget_info(|| egui::WidgetInfo::selected(egui::Role::Button, true, mode == name, tl!(label)));
|
||||
if response.clicked() {
|
||||
app.set_reading_colors(colors);
|
||||
}
|
||||
ui.label(tl!(label));
|
||||
});
|
||||
}
|
||||
});
|
||||
ui.add_space(8.0);
|
||||
if let Some(params) = app.reading_colors.params {
|
||||
let text = egui::Color32::from_rgb(params.text[0], params.text[1], params.text[2]);
|
||||
let background = egui::Color32::from_rgb(params.background[0], params.background[1], params.background[2]);
|
||||
egui::Frame::new().fill(background).inner_margin(16.0).show(ui, |ui| {
|
||||
ui.set_min_width((ui.available_width() - 32.0).max(0.0));
|
||||
ui.label(egui::RichText::new(tl!("Reading colours")).size(18.0).color(text));
|
||||
ui.label(egui::RichText::new(tl!("Colours are remapped while rendering; the document itself never changes.")).color(text));
|
||||
});
|
||||
} else {
|
||||
ui.label(tl!("Original"));
|
||||
}
|
||||
egui::CollapsingHeader::new(tl!("Custom")).default_open(mode == "custom").show(ui, |ui| {
|
||||
let mut colors = if app.reading_colors.params.is_some() { app.reading_colors } else { pdfcraft_render::ReadingColors::BLACK_ON_WHITE };
|
||||
let mut changed = false;
|
||||
if let Some(params) = colors.params.as_mut() {
|
||||
ui.horizontal(|ui| {
|
||||
let text = ui.label(tl!("Text"));
|
||||
changed |= ui.color_edit_button_srgb(&mut params.text).labelled_by(text.id).changed();
|
||||
let background = ui.label(tl!("Page background"));
|
||||
changed |= ui.color_edit_button_srgb(&mut params.background).labelled_by(background.id).changed();
|
||||
});
|
||||
changed |= ui.add(crate::widgets::checkbox(&mut params.only_black, tl!("Only change the colour of black text or line art"))).changed();
|
||||
changed |= ui.add(crate::widgets::checkbox(&mut params.line_art, tl!("Change the colour of line art as well as text"))).changed();
|
||||
}
|
||||
if changed {
|
||||
app.set_reading_colors(colors);
|
||||
}
|
||||
});
|
||||
ui.label(egui::RichText::new(tl!("Colours are remapped while rendering; the document itself never changes.")).small().color(t.text_muted));
|
||||
ui.add_space(8.0);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -29,7 +29,7 @@ fn ok_is_on_screen_without_scrolling_at_any_window_height() {
|
||||
let h = harness(height);
|
||||
let ok = h.get_by_label("OK").rect();
|
||||
assert!(on_screen(ok, height), "{height}: OK at {ok:?} is off the {height} pt window");
|
||||
let title = h.get_by_label("Preferences").rect();
|
||||
let title = h.get_by(|n| n.role() == egui::accesskit::Role::Label && n.value().as_deref() == Some("Preferences")).rect();
|
||||
assert!(on_screen(title, height), "{height}: the title at {title:?} is off the window");
|
||||
// The OK row sits below the scrolled settings, never over them: the settings' last visible
|
||||
// row ends above it. The title is above everything.
|
||||
@@ -43,13 +43,13 @@ fn ok_closes_the_dialog_in_a_short_window_and_the_settings_still_scroll() {
|
||||
// A setting far down the list starts out below the visible settings; scrolling brings it in
|
||||
// between the title and the OK row, and OK did not move.
|
||||
let ok_before = h.get_by_label("OK").rect();
|
||||
let title = h.get_by_label("Preferences").rect();
|
||||
let far = h.get_by_label_contains("Reading colours").rect();
|
||||
assert!(far.min.y >= ok_before.min.y, "Reading colours starts out of sight: {far:?} vs OK {ok_before:?}");
|
||||
h.get_by_label_contains("Reading colours").scroll_to_me();
|
||||
let title = h.get_by(|n| n.role() == egui::accesskit::Role::Label && n.value().as_deref() == Some("Preferences")).rect();
|
||||
let far = h.get_by_label_contains("Enable Acrobat JavaScript").rect();
|
||||
assert!(far.min.y >= ok_before.min.y, "JavaScript setting starts out of sight: {far:?} vs OK {ok_before:?}");
|
||||
h.get_by_label_contains("Enable Acrobat JavaScript").scroll_to_me();
|
||||
h.run_steps(4);
|
||||
let far = h.get_by_label_contains("Reading colours").rect();
|
||||
assert!(far.min.y >= title.max.y && far.max.y <= ok_before.min.y, "Reading colours scrolled in between the title and OK: {far:?}");
|
||||
let far = h.get_by_label_contains("Enable Acrobat JavaScript").rect();
|
||||
assert!(far.min.y >= title.max.y && far.max.y <= ok_before.min.y, "JavaScript setting scrolled in between the title and OK: {far:?}");
|
||||
assert_eq!(h.get_by_label("OK").rect(), ok_before, "OK stays put while the settings scroll");
|
||||
assert!(h.state().dialog.is_some());
|
||||
h.get_by_label("OK").click();
|
||||
@@ -64,9 +64,15 @@ fn the_dialog_opens_at_its_full_size_and_does_not_grow_afterwards() {
|
||||
// in its first frames landed on controls that were still moving.
|
||||
for height in [260.0, 560.0, 900.0] {
|
||||
let mut h = harness(height);
|
||||
let settled = (h.get_by_label("Preferences").rect(), h.get_by_label("OK").rect());
|
||||
let settled = (
|
||||
h.get_by(|n| n.role() == egui::accesskit::Role::Label && n.value().as_deref() == Some("Preferences")).rect(),
|
||||
h.get_by_label("OK").rect(),
|
||||
);
|
||||
h.run_steps(8);
|
||||
let later = (h.get_by_label("Preferences").rect(), h.get_by_label("OK").rect());
|
||||
let later = (
|
||||
h.get_by(|n| n.role() == egui::accesskit::Role::Label && n.value().as_deref() == Some("Preferences")).rect(),
|
||||
h.get_by_label("OK").rect(),
|
||||
);
|
||||
assert_eq!(settled, later, "{height}: the dialog moved after it had been shown for six frames");
|
||||
}
|
||||
// And it is in place within a few frames of opening, for the first click: a frame or two of
|
||||
|
||||
@@ -100,17 +100,6 @@ fn png() -> Vec<u8> {
|
||||
out
|
||||
}
|
||||
|
||||
/// The Reading colours combo of the preferences dialog: it shows the chosen mode as its value,
|
||||
/// and no other combo in the dialog shows `Off` while the setting is off.
|
||||
fn combo(h: &mut Harness<'static, PdfCraftApp>) {
|
||||
// The preferences dialog grew taller than the window, which leaves this combo below the
|
||||
// fold where a click lands outside the dialog and dismisses it; scroll it into view and
|
||||
// let the scroll settle before clicking.
|
||||
h.get_by(|n| n.role() == Role::ComboBox && n.value().is_some_and(|v| v == "Off")).scroll_to_me();
|
||||
h.run_steps(4);
|
||||
h.get_by(|n| n.role() == Role::ComboBox && n.value().is_some_and(|v| v == "Off")).click();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reading_colours_preference_round_trips() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| {
|
||||
@@ -122,11 +111,9 @@ fn reading_colours_preference_round_trips() {
|
||||
});
|
||||
h.run_steps(4);
|
||||
assert_eq!(h.state().reading_colors, ReadingColors::default());
|
||||
// Switch the preset to white-on-black in the dialog's Reading colours combo. The open
|
||||
// document keeps repainting, so run a fixed number of steps and let the popup settle.
|
||||
combo(&mut h);
|
||||
h.run_steps(20);
|
||||
h.get_by_label("White on black").click();
|
||||
h.get_by(|n| n.role() == Role::Button && n.label().as_deref() == Some("White on black")).scroll_to_me();
|
||||
h.run_steps(4);
|
||||
h.get_by(|n| n.role() == Role::Button && n.label().as_deref() == Some("White on black")).click();
|
||||
h.run_steps(2);
|
||||
assert_eq!(h.state().reading_colors, ReadingColors::WHITE_ON_BLACK);
|
||||
// The preference is app-wide: the already open document follows it too.
|
||||
@@ -151,6 +138,41 @@ fn reading_colours_preference_round_trips() {
|
||||
assert_eq!(fresh.reading_colors, ReadingColors::default());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reading_presets_follow_appearance_and_do_not_change_the_interface_theme() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 1000.0)).build_eframe(|_cc| {
|
||||
let mut app = PdfCraftApp::new();
|
||||
app.set_option("language", "en").unwrap();
|
||||
app.set_option("dialog", "preferences").unwrap();
|
||||
app
|
||||
});
|
||||
h.run_steps(6);
|
||||
assert!(h.query_by_label("Original").is_some());
|
||||
let theme = h.state().theme_preference;
|
||||
let appearance = h.get_by_label("Light theme").rect();
|
||||
let preset = h.get_by(|n| n.role() == Role::Button && n.label().as_deref() == Some("White on black")).rect();
|
||||
let documents = h.get_by_label("Documents and view").rect();
|
||||
assert!(appearance.bottom() < preset.top() && preset.bottom() < documents.top());
|
||||
for (label, expected) in [
|
||||
("White on black", ReadingColors::WHITE_ON_BLACK),
|
||||
("Yellow on black", ReadingColors::YELLOW_ON_BLACK),
|
||||
("Green on black", ReadingColors::GREEN_ON_BLACK),
|
||||
("Black on white", ReadingColors::BLACK_ON_WHITE),
|
||||
("Off", ReadingColors::default()),
|
||||
] {
|
||||
h.get_by(|n| n.role() == Role::Button && n.label().as_deref() == Some(label)).scroll_to_me();
|
||||
h.run_steps(4);
|
||||
h.get_by(|n| n.role() == Role::Button && n.label().as_deref() == Some(label)).click();
|
||||
h.run_steps(4);
|
||||
assert_eq!(h.state().reading_colors, expected);
|
||||
assert_eq!(h.state().theme_preference, theme);
|
||||
assert_eq!(h.query_by_label("Original").is_some(), expected.params.is_none());
|
||||
}
|
||||
if let Ok(path) = std::env::var("PDFCRAFT_READING_SHOT") {
|
||||
h.render().unwrap().save(path).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn reading_colours_seed_on_open() {
|
||||
let mut h = Harness::builder().with_size(egui::vec2(1400.0, 900.0)).build_eframe(|_cc| {
|
||||
|
||||
@@ -19,6 +19,20 @@ fn preference(h: &Harness<'static, PdfCraftApp>) -> String {
|
||||
format!("{:?}", h.state().theme_preference)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preferences_gear_is_right_of_theme_and_opens_preferences() {
|
||||
let mut h = harness();
|
||||
let theme = h.get_by_label("Display theme: Use system setting").rect();
|
||||
let gear = h.get_by_label("Preferences").rect();
|
||||
assert!(gear.left() >= theme.right());
|
||||
h.get_by_label("Preferences").click();
|
||||
h.run_steps(6);
|
||||
assert!(h.query_by_label("Appearance mode").is_some());
|
||||
assert!(h.query_by_label("Light theme").is_some());
|
||||
assert!(h.query_by_label("Dark theme").is_some());
|
||||
assert_eq!(preference(&h), "System");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_click_cycles_the_theme_and_a_right_click_opens_the_menu() {
|
||||
let mut h = harness();
|
||||
|
||||
@@ -706,11 +706,11 @@ fn narrow_tab_strips_accept_vertical_wheel_scrolling() {
|
||||
app
|
||||
});
|
||||
h.run_steps(4);
|
||||
let last = h.get_by_label("narrow-11.pdf").rect();
|
||||
let last = h.get_by(|n| n.role() == egui::accesskit::Role::Tab && n.label().as_deref() == Some("narrow-11.pdf")).rect();
|
||||
assert!(last.center().x > 28.0 && last.right() < width - 100.0, "active tab: {last:?}");
|
||||
h.state_mut().active = Some(0);
|
||||
h.run_steps(4);
|
||||
let first = h.get_by_label("narrow-00.pdf").rect();
|
||||
let first = h.get_by(|n| n.role() == egui::accesskit::Role::Tab && n.label().as_deref() == Some("narrow-00.pdf")).rect();
|
||||
assert!(first.center().x > 28.0 && first.right() < width - 100.0);
|
||||
h.event(egui::Event::PointerMoved(first.center()));
|
||||
for _ in 0..8 {
|
||||
@@ -723,9 +723,9 @@ fn narrow_tab_strips_accept_vertical_wheel_scrolling() {
|
||||
});
|
||||
h.run_steps(8);
|
||||
}
|
||||
let last = h.get_by_label("narrow-11.pdf").rect();
|
||||
let last = h.get_by(|n| n.role() == egui::accesskit::Role::Tab && n.label().as_deref() == Some("narrow-11.pdf")).rect();
|
||||
assert!(last.center().x > 28.0 && last.right() < width - 100.0, "scrolled tab: {last:?}");
|
||||
h.get_by_label("narrow-11.pdf").click();
|
||||
h.get_by(|n| n.role() == egui::accesskit::Role::Tab && n.label().as_deref() == Some("narrow-11.pdf")).click();
|
||||
h.run_steps(4);
|
||||
assert_eq!(h.state().active, Some(11));
|
||||
assert!(h.get_by_label_contains("Display theme:").rect().right() <= width);
|
||||
|
||||
@@ -9180,8 +9180,8 @@ milestone = "M12"
|
||||
status = "shipped"
|
||||
commands = []
|
||||
tools = ["view_reading_colors"]
|
||||
evidence = ["crates/automation/tests/it/automation.rs::reading_colors_through_tools", "crates/render/src/raster/tests.rs::reading_colors_map_solid_paints_and_leave_images", "crates/render/src/raster/tests.rs::reading_colors_tiles_match_full_render", "crates/render/src/raster/tests.rs::print_preview_requests_render_with_reading_colors_off", "crates/render/src/raster/tests.rs::reading_colors_keep_highlight_annotations_readable", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_preference_round_trips", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_seed_on_open", "crates/ui-egui/tests/it/reading_colors.rs::pending_pages_use_document_reading_background"]
|
||||
notes = "View only, off by default: Preferences ▸ Reading colours holds the app-wide preference, seeded into every document as it opens (including created ones), and view_reading_colors reads and sets it per document (Acrobat's Replace Document Colors). Solid text paints and, by default, line-art paints (including stencil-mask image paints) are remapped by luminance — each lerps between the preset's text and background colour by how dark or light the original paint is — while images, shadings and tiling patterns pass through untouched. Annotations keep the colours they were given (both /AP appearances and the no-/AP Highlight fallback), so a Multiply highlight over a dark background shows the marked-up words in the highlight's colour instead of a flat bar that hides them (#1548). Presets: black on white, white on black, yellow on black, green on black, or a custom #RRGGBB pair; only_black limits the mapping to near-black paints (a 10% luminance threshold — our choice; Acrobat's was not observed). Display only: the document is not changed and print and export are unaffected; print previews and view snapshots render with the document's true colours (a preview matches the uncoloured printout, and a snapshot is an export surface), so reading colours apply to the reading view only. Pending canvas pages use the configured reading background before their raster arrives (#2059). Closes #897 (duplicate #488)."
|
||||
evidence = ["crates/automation/tests/it/automation.rs::reading_colors_through_tools", "crates/render/src/raster/tests.rs::reading_colors_map_solid_paints_and_leave_images", "crates/render/src/raster/tests.rs::reading_colors_tiles_match_full_render", "crates/render/src/raster/tests.rs::print_preview_requests_render_with_reading_colors_off", "crates/render/src/raster/tests.rs::reading_colors_keep_highlight_annotations_readable", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_preference_round_trips", "crates/ui-egui/tests/it/reading_colors.rs::reading_colours_seed_on_open", "crates/ui-egui/tests/it/reading_colors.rs::pending_pages_use_document_reading_background", "crates/ui-egui/tests/it/reading_colors.rs::reading_presets_follow_appearance_and_do_not_change_the_interface_theme", "crates/ui-egui/tests/it/theme_button.rs::preferences_gear_is_right_of_theme_and_opens_preferences"]
|
||||
notes = "View only, off by default: Preferences ▸ Reading colours, directly below Appearance, offers colour samples and a live preview; the gear beside the theme button opens Preferences. Reading colours holds the app-wide preference, seeded into every document as it opens (including created ones), and view_reading_colors reads and sets it per document (Acrobat's Replace Document Colors). Solid text paints and, by default, line-art paints (including stencil-mask image paints) are remapped by luminance — each lerps between the preset's text and background colour by how dark or light the original paint is — while images, shadings and tiling patterns pass through untouched. Annotations keep the colours they were given (both /AP appearances and the no-/AP Highlight fallback), so a Multiply highlight over a dark background shows the marked-up words in the highlight's colour instead of a flat bar that hides them (#1548). Presets: black on white, white on black, yellow on black, green on black, or a custom #RRGGBB pair; only_black limits the mapping to near-black paints (a 10% luminance threshold — our choice; Acrobat's was not observed). Display only: the document is not changed and print and export are unaffected; print previews and view snapshots render with the document's true colours (a preview matches the uncoloured printout, and a snapshot is an export surface), so reading colours apply to the reading view only. Pending canvas pages use the configured reading background before their raster arrives (#2059). Closes #897 (duplicate #488)."
|
||||
|
||||
[[feature]]
|
||||
id = "a11y.reflow-tagged"
|
||||
|
||||
Reference in New Issue
Block a user