diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index b24761578..a2d2c7fba 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -1557,6 +1557,16 @@ fn grid(p: &egui::Painter, xf: &Xf, spacing: f64, subdiv: u32, look: LineLook) { /// A ruler label for `v` (in the ruler's unit) on a ruler whose labels are `step` apart: whole /// numbers, or the fewest decimals that write `step` exactly, so a binary step keeps its value /// (an inch ruler's 1/16 reads `0.0625`, not the `0.06` that rounding the step's magnitude gave). +/// How far down the left ruler a label of `chars` characters reaches, counting from its top: it +/// is spelled one character per line, [`RULER_CHAR`] apart, and the last one is a line tall. +pub(crate) fn vertical_label_height(chars: usize) -> f32 { + chars.saturating_sub(1) as f32 * RULER_CHAR + RULER_LINE +} + +/// A ruler label's line spacing and line height on the left ruler. +const RULER_CHAR: f32 = 8.5; +const RULER_LINE: f32 = 9.5; + pub(crate) fn ruler_label(v: f64, step: f64) -> String { let exact = |d: u32| (step * 10f64.powi(d as i32)).fract().abs() < 1e-9; let decimals = (0..=7).find(|d| exact(*d)).unwrap_or(7) as usize; @@ -1710,14 +1720,24 @@ fn rulers(ui: &Ui, full: egui::Rect, xf: &Xf, hover: Option, unit: Unit, } }); let clip_left = p.with_clip_rect(left); + // A label spelled down the ruler is tall, and a long one can reach the next label's place: + // "-0.0625" is seven characters, 60 pt of ruler, where a sixteenth-inch step leaves 54 pt at + // zoom 12. The ticks all stay; a label that would run into the one above it is left out. + let mut filled_to = f32::NEG_INFINITY; ticks(left.left_top(), left.left_bottom(), vectorcraft_geom::Vec2::new(sn, cs), &mut |d, y| { let sy = left.top() + d; clip_left.line_segment([pos2(left.right() - tick(y), sy), pos2(left.right(), sy)], Stroke::new(1.0, t.ruler_tick)); if near(y, step) { // Vertical labels read top-to-bottom, one digit per line like Illustrator. - for (k, ch) in ruler_label(y, step).chars().enumerate() { + let text = ruler_label(y, step); + let top = sy + 2.0; + if top < filled_to { + return; + } + filled_to = top + vertical_label_height(text.chars().count()); + for (k, ch) in text.chars().enumerate() { clip_left.text( - pos2(left.left() + 4.0, sy + 2.0 + k as f32 * 8.5), + pos2(left.left() + 4.0, top + k as f32 * RULER_CHAR), egui::Align2::LEFT_TOP, ch.to_string(), font.clone(), diff --git a/crates/ui-egui/src/panels/tests_units.rs b/crates/ui-egui/src/panels/tests_units.rs index b5f18402f..da5be17f4 100644 --- a/crates/ui-egui/src/panels/tests_units.rs +++ b/crates/ui-egui/src/panels/tests_units.rs @@ -117,6 +117,27 @@ fn new_documents_start_in_the_general_preference() { assert_eq!((d.units, d.artboards[0].rect.width()), (Unit::Millimeters, 612.0)); } +/// A label spelled down the left ruler is tall, and a sixteenth-inch step does not always leave +/// room for a long one: at zoom 12 the labels are 54 pt apart while "-0.0625" reaches 60.5 pt, so +/// the ruler leaves that label out rather than drawing it over its neighbour. Every tick stays. +#[test] +fn a_long_vertical_label_does_not_fit_a_sixteenth_inch_step() { + use crate::canvas::vertical_label_height; + use vectorcraft_doc::Unit; + // One character is one line; each further character adds a line. + assert_eq!(vertical_label_height(1), 9.5); + assert_eq!(vertical_label_height(2), 18.0); + assert_eq!(vertical_label_height(7), 60.5); + // The gap a label has to fit into, at the zoom where a sixteenth is first chosen and beyond. + let gap = |zoom: f64| Unit::Inches.ruler_step(zoom) * Unit::Inches.points() * zoom; + assert!((gap(12.0) - 54.0).abs() < 1e-9, "{}", gap(12.0)); + assert!(vertical_label_height(7) > gap(12.0) as f32, "a seven-character label has to be left out"); + // The labels this ruler actually writes below an inch are six characters, which do fit. + assert!(vertical_label_height("0.0625".chars().count()) < gap(12.0) as f32); + // Zoomed further in the gap grows and even a long label fits. + assert!(vertical_label_height(7) < gap(20.0) as f32, "{}", gap(20.0)); +} + #[test] fn ruler_labels_have_the_steps_decimals() { use crate::canvas::ruler_label;