Round text galley sizes to nearest ui point size (#4578)
Previously, many labels had non-integer widths. This lead to rounding errors. This was most notable for the new `Area` sizing code: We would run the initial sizing pass, to measure the size of e.g. a tooltip. Say the tooltip contains text that was 100.123 ui points wide. With a 16pt border, that becomes 116.123, which is stored in the `Area` state as the width. The next frame, we use that stored size as the wrapping width. With perfect precision, we would then tell the label to wrap to 100.123 pts, which the text would _just_ fit in. However, due to rounding errors we might end up asking it to wrap to 100.12**2** pts, meaning the last word would now wrap and end up on the next line. By rounding label sizes to perfect integers, we avoid such rounding errors, and most ui elements will now end up on perfect integer point coordinates (and `f32` can precisely express and do arithmetic on all integers < 2^24). Visually this has very little impact. Some labels move by a pixel here and there, mostly for the better.
This commit is contained in:
parent
66f40de7a1
commit
cc3b3629b8
|
@ -238,6 +238,12 @@ fn rows_from_paragraphs(
|
|||
}
|
||||
|
||||
fn line_break(paragraph: &Paragraph, job: &LayoutJob, out_rows: &mut Vec<Row>, elided: &mut bool) {
|
||||
let wrap_width_margin = if job.round_output_size_to_nearest_ui_point {
|
||||
0.5
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
// Keeps track of good places to insert row break if we exceed `wrap_width`.
|
||||
let mut row_break_candidates = RowBreakCandidates::default();
|
||||
|
||||
|
@ -253,7 +259,7 @@ fn line_break(paragraph: &Paragraph, job: &LayoutJob, out_rows: &mut Vec<Row>, e
|
|||
|
||||
let potential_row_width = paragraph.glyphs[i].max_x() - row_start_x;
|
||||
|
||||
if job.wrap.max_width < potential_row_width {
|
||||
if job.wrap.max_width + wrap_width_margin < potential_row_width {
|
||||
// Row break:
|
||||
|
||||
if first_row_indentation > 0.0
|
||||
|
@ -630,7 +636,24 @@ fn galley_from_rows(
|
|||
num_indices += row.visuals.mesh.indices.len();
|
||||
}
|
||||
|
||||
let rect = Rect::from_min_max(pos2(min_x, 0.0), pos2(max_x, cursor_y));
|
||||
let mut rect = Rect::from_min_max(pos2(min_x, 0.0), pos2(max_x, cursor_y));
|
||||
|
||||
if job.round_output_size_to_nearest_ui_point {
|
||||
let did_exceed_wrap_width_by_a_lot = rect.width() > job.wrap.max_width + 1.0;
|
||||
|
||||
// We round the size to whole ui points here (not pixels!) so that the egui layout code
|
||||
// can have the advantage of working in integer units, avoiding rounding errors.
|
||||
rect.min = rect.min.round();
|
||||
rect.max = rect.max.round();
|
||||
|
||||
if did_exceed_wrap_width_by_a_lot {
|
||||
// If the user picked a too aggressive wrap width (e.g. more narrow than any individual glyph),
|
||||
// we should let the user know.
|
||||
} else {
|
||||
// Make sure we don't over the max wrap width the user picked:
|
||||
rect.max.x = rect.max.x.at_most(rect.min.x + job.wrap.max_width);
|
||||
}
|
||||
}
|
||||
|
||||
Galley {
|
||||
job,
|
||||
|
|
|
@ -74,6 +74,10 @@ pub struct LayoutJob {
|
|||
|
||||
/// Justify text so that word-wrapped rows fill the whole [`TextWrapping::max_width`].
|
||||
pub justify: bool,
|
||||
|
||||
/// Rounding to the closest ui point (not pixel!) allows the rest of the
|
||||
/// layout code to run on perfect integers, avoiding rounding errors.
|
||||
pub round_output_size_to_nearest_ui_point: bool,
|
||||
}
|
||||
|
||||
impl Default for LayoutJob {
|
||||
|
@ -87,6 +91,7 @@ impl Default for LayoutJob {
|
|||
break_on_newline: true,
|
||||
halign: Align::LEFT,
|
||||
justify: false,
|
||||
round_output_size_to_nearest_ui_point: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -180,6 +185,7 @@ impl std::hash::Hash for LayoutJob {
|
|||
break_on_newline,
|
||||
halign,
|
||||
justify,
|
||||
round_output_size_to_nearest_ui_point,
|
||||
} = self;
|
||||
|
||||
text.hash(state);
|
||||
|
@ -189,6 +195,7 @@ impl std::hash::Hash for LayoutJob {
|
|||
break_on_newline.hash(state);
|
||||
halign.hash(state);
|
||||
justify.hash(state);
|
||||
round_output_size_to_nearest_ui_point.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue