mirror of
https://github.com/storytold/pdfcraft.git
synced 2026-11-09 20:43:12 +00:00
fix(ui): the rail's page box fits, and the zoom field looks like a field (#5636)
The page box lost its right edge to the rail. The page and zoom fields now share one helper that boxes them and sizes them to the rail, with the same 12 pt font. Zoom out and zoom in are small buttons side by side under the zoom field. The rail's side padding is 4 pt, not 6. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
47f42ba07b
commit
889472f16f
2 files changed
+55
-24
No files matched your search
@@ -531,6 +531,23 @@ pub fn mode_bar(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
});
|
||||
}
|
||||
|
||||
/// Padding inside the rail's page and zoom fields.
|
||||
const RAIL_FIELD_MARGIN: egui::Vec2 = egui::vec2(1.0, 4.0);
|
||||
|
||||
/// A field the rail can type in: boxed, and the same size whatever it holds, so the page and zoom
|
||||
/// fields match and fit the rail's width. Sized outright: a text field is at least a control wide.
|
||||
fn rail_field(ui: &mut egui::Ui, t: &Tokens, edit: egui::TextEdit<'_>) -> egui::Response {
|
||||
egui::Frame::NONE
|
||||
.fill(t.field)
|
||||
.stroke(Stroke::new(1.0, t.border))
|
||||
.corner_radius(CornerRadius::same(5))
|
||||
.show(ui, |ui| {
|
||||
ui.spacing_mut().interact_size.x = 0.0;
|
||||
ui.add_sized([34.0, 22.0], edit)
|
||||
})
|
||||
.inner
|
||||
}
|
||||
|
||||
/// Draw the right tool rail with page navigation, history, zoom, rotation and page-display controls for the
|
||||
/// active view.
|
||||
pub fn right_rail(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
@@ -573,20 +590,12 @@ pub fn right_rail(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
let mut page_display = None;
|
||||
let rail = Rail::new(egui::Id::unique("rail"))
|
||||
.edge(Edge::End)
|
||||
.frame(egui::Frame::NONE.fill(t.chrome).inner_margin(egui::Margin::symmetric(6, 8)).stroke(Stroke::new(1.0, t.divider)))
|
||||
.frame(egui::Frame::NONE.fill(t.chrome).inner_margin(egui::Margin::symmetric(4, 8)).stroke(Stroke::new(1.0, t.divider)))
|
||||
.show(ui, &items, app.right.as_ref(), |ui| {
|
||||
// Page navigation cluster at the bottom, a panel of its own so that in a short
|
||||
// window the buttons above scroll instead of running into it (#3282).
|
||||
ui.with_layout(Layout::bottom_up(Align::Center), |ui| {
|
||||
ui.spacing_mut().item_spacing.y = 2.0;
|
||||
let tip = crate::commands::key_tip(ui.ctx(), tl!("Zoom out ({key})"), crate::commands::ZOOM_OUT);
|
||||
if icons::button(ui, "zoom-out", 32.0, false, &tip).clicked() {
|
||||
view.zoom_step(false);
|
||||
}
|
||||
let tip = crate::commands::key_tip(ui.ctx(), tl!("Zoom in ({key})"), crate::commands::ZOOM_IN);
|
||||
if icons::button(ui, "zoom-in", 32.0, false, &tip).clicked() {
|
||||
view.zoom_step(true);
|
||||
}
|
||||
// Between rotate and zoom, as in Acrobat. Its command runs once `view` is free.
|
||||
let tip = crate::i18n::fmt(tl!("Page display: {layout}"), &[("layout", tl!(view.layout.label()))]);
|
||||
let resp = icons::button(ui, view.layout.icon(), 32.0, false, &tip);
|
||||
@@ -606,18 +615,29 @@ pub fn right_rail(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
view.fit = if view.fit == Fit::Width { Fit::Page } else { Fit::Width };
|
||||
view.goto = Some((view.current, 0.0));
|
||||
}
|
||||
// Out and in side by side right under the zoom field, so they are found with it.
|
||||
ui.horizontal(|ui| {
|
||||
ui.spacing_mut().item_spacing.x = 0.0;
|
||||
let tip = crate::commands::key_tip(ui.ctx(), tl!("Zoom out ({key})"), crate::commands::ZOOM_OUT);
|
||||
if icons::button(ui, "zoom-out", 17.0, false, &tip).clicked() {
|
||||
view.zoom_step(false);
|
||||
}
|
||||
let tip = crate::commands::key_tip(ui.ctx(), tl!("Zoom in ({key})"), crate::commands::ZOOM_IN);
|
||||
if icons::button(ui, "zoom-in", 17.0, false, &tip).clicked() {
|
||||
view.zoom_step(true);
|
||||
}
|
||||
});
|
||||
let id = egui::Id::unique("zoom-input");
|
||||
let draft = id.with("draft");
|
||||
let mut typed = ui.data(|d| d.get_temp::<String>(draft)).unwrap_or_else(|| format!("{:.0}%", view.zoom * 100.0));
|
||||
let edit = egui::TextEdit::singleline(&mut typed)
|
||||
.id(id)
|
||||
.desired_width(40.0)
|
||||
.align(egui::Align2::CENTER_TOP)
|
||||
.font(theme::medium(11.0))
|
||||
.text_color(t.text_muted)
|
||||
.align(egui::Align2::CENTER_CENTER)
|
||||
.font(theme::medium(12.0))
|
||||
.text_color(t.text)
|
||||
.frame(egui::Frame::NONE)
|
||||
.margin(vec2(2.0, 2.0));
|
||||
let r = ui.add(edit).on_hover_text(tl!("Zoom — type a percentage and press Enter"));
|
||||
.margin(RAIL_FIELD_MARGIN);
|
||||
let r = rail_field(ui, &t, edit).on_hover_text(tl!("Zoom — type a percentage and press Enter"));
|
||||
ui.ctx().accesskit_node_builder(id, |node| node.set_label(tl!("Zoom")));
|
||||
if r.gained_focus() {
|
||||
let mut state = egui::TextEdit::load_state(ui.ctx(), id).unwrap_or_default();
|
||||
@@ -677,16 +697,11 @@ pub fn right_rail(app: &mut PdfCraftApp, ui: &mut egui::Ui) {
|
||||
}
|
||||
let edit = egui::TextEdit::singleline(&mut view.page_input)
|
||||
.id(page_input_id)
|
||||
.desired_width(34.0)
|
||||
.align(egui::Align2::CENTER_TOP)
|
||||
.align(egui::Align2::CENTER_CENTER)
|
||||
.font(theme::medium(12.0))
|
||||
.margin(vec2(2.0, 4.0));
|
||||
let r = egui::Frame::NONE
|
||||
.fill(t.field)
|
||||
.stroke(Stroke::new(1.0, t.border))
|
||||
.corner_radius(CornerRadius::same(5))
|
||||
.show(ui, |ui| ui.add(edit))
|
||||
.inner
|
||||
.frame(egui::Frame::NONE)
|
||||
.margin(RAIL_FIELD_MARGIN);
|
||||
let r = rail_field(ui, &t, edit)
|
||||
.on_hover_text(tl!("Current page — type a page number or label (such as iv) and press Enter"))
|
||||
.accessible_name(tl!("Current page"));
|
||||
if r.lost_focus() {
|
||||
|
||||
@@ -1425,3 +1425,19 @@ fn typing_in_one_tab_never_reaches_another_tabs_added_text() {
|
||||
h.run_steps(3);
|
||||
assert_eq!(h.state().views[1].content.draft.as_ref().map(|d| d.text.as_str()), Some("Reviewed"), "the other tab's typing stays out");
|
||||
}
|
||||
|
||||
/// The rail's page and zoom fields are the same size, and each sits wholly inside the rail, so
|
||||
/// neither box is cut off at the edge (the page box lost its right side once the rail's contents
|
||||
/// changed width).
|
||||
#[test]
|
||||
fn rail_page_and_zoom_fields_match_and_fit_the_rail() {
|
||||
let mut h = harness();
|
||||
h.run_steps(3);
|
||||
let rail = h.get_by_label("Bookmarks").rect();
|
||||
let (page, zoom) = (h.get_by_label("Current page").rect(), h.get_by_label("Zoom").rect());
|
||||
assert_eq!((page.width(), page.height()), (zoom.width(), zoom.height()), "{page:?} vs {zoom:?}");
|
||||
for r in [page, zoom] {
|
||||
assert!(r.left() >= rail.left() - 2.0 && r.right() <= rail.right() + 2.0, "{r:?} is cut off by the rail ({rail:?})");
|
||||
assert!((r.center().x - rail.center().x).abs() < 1.0, "{r:?} is off the rail's centre ({rail:?})");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user