diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index 276f3f9e2a3..7f1cd865710 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -238,6 +238,12 @@ fn rows_from_paragraphs( } fn line_break(paragraph: &Paragraph, job: &LayoutJob, out_rows: &mut Vec, 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, 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, diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index c14348ecf29..fde36ec6a50 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -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); } }