From ad7147671ca46c1728f57a99284e00c13bd364fc Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Wed, 30 Apr 2025 15:51:53 +0200 Subject: [PATCH 1/4] Fix links and text selection in horizontal_wrapped layout --- crates/egui/src/widgets/label.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index 3656af92bfd..50227c7c81d 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -216,7 +216,9 @@ impl Label { let pos = pos2(ui.max_rect().left(), ui.cursor().top()); assert!(!galley.rows.is_empty(), "Galleys are never empty"); // collect a response from many rows: - let rect = galley.rows[0].rect().translate(pos.to_vec2()); + let mut rect = galley.rows[0].rect().translate(pos.to_vec2()); + // The rect ignores first_row_indentation, so we add it back: + rect.min.x += first_row_indentation; let mut response = ui.allocate_rect(rect, sense); for placed_row in galley.rows.iter().skip(1) { let rect = placed_row.rect().translate(pos.to_vec2()); From e6f1fef9665ea22d7e6be66f4cbadf623e5f1a82 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 6 May 2025 16:38:59 +0200 Subject: [PATCH 2/4] Fix `PlacedRow::rect` --- crates/egui/src/widgets/label.rs | 10 +++------- crates/epaint/src/text/text_layout_types.rs | 7 ++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index 50227c7c81d..79fd399929c 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -1,12 +1,10 @@ use std::sync::Arc; use crate::{ - epaint, pos2, text_selection, Align, Direction, FontSelection, Galley, Pos2, Response, Sense, - Stroke, TextWrapMode, Ui, Widget, WidgetInfo, WidgetText, WidgetType, + epaint, pos2, text_selection::LabelSelectionState, Align, Direction, FontSelection, Galley, + Pos2, Response, Sense, Stroke, TextWrapMode, Ui, Widget, WidgetInfo, WidgetText, WidgetType, }; -use self::text_selection::LabelSelectionState; - /// Static text. /// /// Usually it is more convenient to use [`Ui::label`]. @@ -216,9 +214,7 @@ impl Label { let pos = pos2(ui.max_rect().left(), ui.cursor().top()); assert!(!galley.rows.is_empty(), "Galleys are never empty"); // collect a response from many rows: - let mut rect = galley.rows[0].rect().translate(pos.to_vec2()); - // The rect ignores first_row_indentation, so we add it back: - rect.min.x += first_row_indentation; + let rect = galley.rows[0].rect().translate(pos.to_vec2()); let mut response = ui.allocate_rect(rect, sense); for placed_row in galley.rows.iter().skip(1) { let rect = placed_row.rect().translate(pos.to_vec2()); diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 49ec2908745..92d44a79f83 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -561,9 +561,14 @@ pub struct PlacedRow { impl PlacedRow { /// Logical bounding rectangle on font heights etc. + /// /// Use this when drawing a selection or similar! + /// + /// If `LayoutSection::leading_space` is set, the returned [`Rect`] will be offset by that. pub fn rect(&self) -> Rect { - Rect::from_min_size(self.pos, self.row.size) + let x = self.glyphs.first().map_or(self.pos.x, |g| g.pos.x); + let size_x = self.size.x - x; + Rect::from_min_size(Pos2::new(x, self.pos.y), Vec2::new(size_x, self.size.y)) } } From 304dbe3c5df6fb0444c398e51e5d1bdd1297c5c6 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 6 May 2025 17:26:35 +0200 Subject: [PATCH 3/4] Revert "Fix `PlacedRow::rect`" This reverts commit e6f1fef9665ea22d7e6be66f4cbadf623e5f1a82. --- crates/egui/src/widgets/label.rs | 10 +++++++--- crates/epaint/src/text/text_layout_types.rs | 7 +------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index 79fd399929c..50227c7c81d 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -1,10 +1,12 @@ use std::sync::Arc; use crate::{ - epaint, pos2, text_selection::LabelSelectionState, Align, Direction, FontSelection, Galley, - Pos2, Response, Sense, Stroke, TextWrapMode, Ui, Widget, WidgetInfo, WidgetText, WidgetType, + epaint, pos2, text_selection, Align, Direction, FontSelection, Galley, Pos2, Response, Sense, + Stroke, TextWrapMode, Ui, Widget, WidgetInfo, WidgetText, WidgetType, }; +use self::text_selection::LabelSelectionState; + /// Static text. /// /// Usually it is more convenient to use [`Ui::label`]. @@ -214,7 +216,9 @@ impl Label { let pos = pos2(ui.max_rect().left(), ui.cursor().top()); assert!(!galley.rows.is_empty(), "Galleys are never empty"); // collect a response from many rows: - let rect = galley.rows[0].rect().translate(pos.to_vec2()); + let mut rect = galley.rows[0].rect().translate(pos.to_vec2()); + // The rect ignores first_row_indentation, so we add it back: + rect.min.x += first_row_indentation; let mut response = ui.allocate_rect(rect, sense); for placed_row in galley.rows.iter().skip(1) { let rect = placed_row.rect().translate(pos.to_vec2()); diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 92d44a79f83..49ec2908745 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -561,14 +561,9 @@ pub struct PlacedRow { impl PlacedRow { /// Logical bounding rectangle on font heights etc. - /// /// Use this when drawing a selection or similar! - /// - /// If `LayoutSection::leading_space` is set, the returned [`Rect`] will be offset by that. pub fn rect(&self) -> Rect { - let x = self.glyphs.first().map_or(self.pos.x, |g| g.pos.x); - let size_x = self.size.x - x; - Rect::from_min_size(Pos2::new(x, self.pos.y), Vec2::new(size_x, self.size.y)) + Rect::from_min_size(self.pos, self.row.size) } } From 8d736539b725de1678786aebd0f04603a05acfc6 Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Tue, 6 May 2025 17:33:35 +0200 Subject: [PATCH 4/4] Add rect_without_leading_space and use that --- crates/egui/src/text_selection/accesskit_text.rs | 2 +- crates/egui/src/widgets/label.rs | 12 +++++------- crates/epaint/src/text/text_layout_types.rs | 10 +++++++++- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/egui/src/text_selection/accesskit_text.rs b/crates/egui/src/text_selection/accesskit_text.rs index de193e3b0d9..e04a54d1824 100644 --- a/crates/egui/src/text_selection/accesskit_text.rs +++ b/crates/egui/src/text_selection/accesskit_text.rs @@ -45,7 +45,7 @@ pub fn update_accesskit_for_text_widget( let row_id = parent_id.with(row_index); ctx.accesskit_node_builder(row_id, |builder| { builder.set_role(accesskit::Role::TextRun); - let rect = global_from_galley * row.rect(); + let rect = global_from_galley * row.rect_without_leading_space(); builder.set_bounds(accesskit::Rect { x0: rect.min.x.into(), y0: rect.min.y.into(), diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index 50227c7c81d..2f131d8e38e 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -1,12 +1,10 @@ use std::sync::Arc; use crate::{ - epaint, pos2, text_selection, Align, Direction, FontSelection, Galley, Pos2, Response, Sense, - Stroke, TextWrapMode, Ui, Widget, WidgetInfo, WidgetText, WidgetType, + epaint, pos2, text_selection::LabelSelectionState, Align, Direction, FontSelection, Galley, + Pos2, Response, Sense, Stroke, TextWrapMode, Ui, Widget, WidgetInfo, WidgetText, WidgetType, }; -use self::text_selection::LabelSelectionState; - /// Static text. /// /// Usually it is more convenient to use [`Ui::label`]. @@ -216,9 +214,9 @@ impl Label { let pos = pos2(ui.max_rect().left(), ui.cursor().top()); assert!(!galley.rows.is_empty(), "Galleys are never empty"); // collect a response from many rows: - let mut rect = galley.rows[0].rect().translate(pos.to_vec2()); - // The rect ignores first_row_indentation, so we add it back: - rect.min.x += first_row_indentation; + let rect = galley.rows[0] + .rect_without_leading_space() + .translate(pos.to_vec2()); let mut response = ui.allocate_rect(rect, sense); for placed_row in galley.rows.iter().skip(1) { let rect = placed_row.rect().translate(pos.to_vec2()); diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 49ec2908745..5198518515e 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -561,10 +561,18 @@ pub struct PlacedRow { impl PlacedRow { /// Logical bounding rectangle on font heights etc. - /// Use this when drawing a selection or similar! + /// + /// This ignores / includes the `LayoutSection::leading_space`. pub fn rect(&self) -> Rect { Rect::from_min_size(self.pos, self.row.size) } + + /// Same as [`Self::rect`] but excluding the `LayoutSection::leading_space`. + pub fn rect_without_leading_space(&self) -> Rect { + let x = self.glyphs.first().map_or(self.pos.x, |g| g.pos.x); + let size_x = self.size.x - x; + Rect::from_min_size(Pos2::new(x, self.pos.y), Vec2::new(size_x, self.size.y)) + } } impl std::ops::Deref for PlacedRow {