From b7ea43f5196cbdf14d89d1f0bc0c219fa0704cff Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 30 Sep 2024 16:44:42 +0200 Subject: [PATCH 1/4] Add `Rect::round` --- crates/emath/src/rect.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index 6c0677ad55e..0fb4ac91d08 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -245,6 +245,16 @@ impl Rect { ) } + /// Round to integer + #[must_use] + #[inline] + pub fn round(self) -> Self { + Self { + min: self.min.round(), + max: self.max.round(), + } + } + #[must_use] #[inline] pub fn intersects(self, other: Self) -> bool { From 6a33dab6e373cedb72c4f1216775b4ea671b478a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 30 Sep 2024 16:46:08 +0200 Subject: [PATCH 2/4] Round font row heights --- crates/egui/src/containers/window.rs | 2 +- crates/egui/src/ui.rs | 1 + crates/egui/src/widget_text.rs | 2 +- crates/egui/src/widgets/button.rs | 4 +++- crates/egui/src/widgets/text_edit/builder.rs | 2 +- crates/egui_demo_lib/src/demo/scrolling.rs | 2 +- crates/egui_demo_lib/src/easy_mark/easy_mark_viewer.rs | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 438d562ede7..90b28a1d399 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -470,7 +470,7 @@ impl<'open> Window<'open> { let (title_bar_height, title_content_spacing) = if with_title_bar { let style = ctx.style(); let spacing = window_margin.top + window_margin.bottom; - let height = ctx.fonts(|f| title.font_height(f, &style)) + spacing; + let height = ctx.fonts(|f| title.font_height(f, &style)).round() + spacing; window_frame.rounding.ne = window_frame.rounding.ne.clamp(0.0, height / 2.0); window_frame.rounding.nw = window_frame.rounding.nw.clamp(0.0, height / 2.0); (height, spacing) diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index ee77888f811..f0a30ee2fcf 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -708,6 +708,7 @@ impl Ui { /// The height of text of this text style pub fn text_style_height(&self, style: &TextStyle) -> f32 { self.fonts(|f| f.row_height(&style.resolve(self.style()))) + .round() } /// Screen-space rectangle for clipping what we paint in this ui. diff --git a/crates/egui/src/widget_text.rs b/crates/egui/src/widget_text.rs index 03b8cbb9751..b7a44083005 100644 --- a/crates/egui/src/widget_text.rs +++ b/crates/egui/src/widget_text.rs @@ -269,7 +269,7 @@ impl RichText { if let Some(family) = &self.family { font_id.family = family.clone(); } - fonts.row_height(&font_id) + fonts.row_height(&font_id).round() } /// Append to an existing [`LayoutJob`] diff --git a/crates/egui/src/widgets/button.rs b/crates/egui/src/widgets/button.rs index 28a578ee694..a3f72c089a6 100644 --- a/crates/egui/src/widgets/button.rs +++ b/crates/egui/src/widgets/button.rs @@ -204,7 +204,9 @@ impl Widget for Button<'_> { } let space_available_for_image = if let Some(text) = &text { - let font_height = ui.fonts(|fonts| text.font_height(fonts, ui.style())); + let font_height = ui + .fonts(|fonts| text.font_height(fonts, ui.style())) + .round(); Vec2::splat(font_height) // Reasonable? } else { ui.available_size() - 2.0 * button_padding diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 7bd4e8db8e5..5f355e13983 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -487,7 +487,7 @@ impl<'t> TextEdit<'t> { let prev_text = text.as_str().to_owned(); let font_id = font_selection.resolve(ui.style()); - let row_height = ui.fonts(|f| f.row_height(&font_id)); + let row_height = ui.fonts(|f| f.row_height(&font_id)).round(); const MIN_WIDTH: f32 = 24.0; // Never make a [`TextEdit`] more narrow than this. let available_width = (ui.available_width() - margin.sum().x).at_least(MIN_WIDTH); let desired_width = desired_width.unwrap_or_else(|| ui.spacing().text_edit_width); diff --git a/crates/egui_demo_lib/src/demo/scrolling.rs b/crates/egui_demo_lib/src/demo/scrolling.rs index 7a2ca4d7c9f..75deb90e6e6 100644 --- a/crates/egui_demo_lib/src/demo/scrolling.rs +++ b/crates/egui_demo_lib/src/demo/scrolling.rs @@ -191,7 +191,7 @@ fn huge_content_painter(ui: &mut egui::Ui) { ui.add_space(4.0); let font_id = TextStyle::Body.resolve(ui.style()); - let row_height = ui.fonts(|f| f.row_height(&font_id)) + ui.spacing().item_spacing.y; + let row_height = ui.fonts(|f| f.row_height(&font_id)).round() + ui.spacing().item_spacing.y; let num_rows = 10_000; ScrollArea::vertical() diff --git a/crates/egui_demo_lib/src/easy_mark/easy_mark_viewer.rs b/crates/egui_demo_lib/src/easy_mark/easy_mark_viewer.rs index 17d3858f7a7..13702fb3027 100644 --- a/crates/egui_demo_lib/src/easy_mark/easy_mark_viewer.rs +++ b/crates/egui_demo_lib/src/easy_mark/easy_mark_viewer.rs @@ -162,7 +162,7 @@ fn bullet_point(ui: &mut Ui, width: f32) -> Response { fn numbered_point(ui: &mut Ui, width: f32, number: &str) -> Response { let font_id = TextStyle::Body.resolve(ui.style()); - let row_height = ui.fonts(|f| f.row_height(&font_id)); + let row_height = ui.fonts(|f| f.row_height(&font_id)).round(); let (rect, response) = ui.allocate_exact_size(vec2(width, row_height), Sense::hover()); let text = format!("{number}."); let text_color = ui.visuals().strong_text_color(); From 1a45dd5342dbe9bfe6996593e4387a8586d95491 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 30 Sep 2024 16:46:58 +0200 Subject: [PATCH 3/4] Round areas/windows --- crates/egui/src/containers/area.rs | 19 +++++++++++-------- crates/egui/src/containers/window.rs | 15 +++++++-------- crates/egui/src/context.rs | 6 ++---- 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index fa8e5fc203b..4f6464f4150 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -66,21 +66,25 @@ impl AreaState { pivot_pos.x - self.pivot.x().to_factor() * size.x, pivot_pos.y - self.pivot.y().to_factor() * size.y, ) + .round() } /// Move the left top positions of the area. pub fn set_left_top_pos(&mut self, pos: Pos2) { let size = self.size.unwrap_or_default(); - self.pivot_pos = Some(pos2( - pos.x + self.pivot.x().to_factor() * size.x, - pos.y + self.pivot.y().to_factor() * size.y, - )); + self.pivot_pos = Some( + pos2( + pos.x + self.pivot.x().to_factor() * size.x, + pos.y + self.pivot.y().to_factor() * size.y, + ) + .round(), + ); } /// Where the area is on screen. pub fn rect(&self) -> Rect { let size = self.size.unwrap_or_default(); - Rect::from_min_size(self.left_top_pos(), size) + Rect::from_min_size(self.left_top_pos(), size).round() } } @@ -493,12 +497,11 @@ impl Area { if constrain { state.set_left_top_pos( - ctx.constrain_window_rect_to_area(state.rect(), constrain_rect) - .min, + Context::constrain_window_rect_to_area(state.rect(), constrain_rect).min, ); } - state.set_left_top_pos(ctx.round_pos_to_pixels(state.left_top_pos())); + state.set_left_top_pos(state.left_top_pos().round()); // Update response with possibly moved/constrained rect: move_response.rect = state.rect(); diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 90b28a1d399..1f99b7c72ba 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -771,13 +771,12 @@ fn resize_response( area: &mut area::Prepared, resize_id: Id, ) { - let Some(new_rect) = move_and_resize_window(ctx, &resize_interaction) else { + let Some(mut new_rect) = move_and_resize_window(ctx, &resize_interaction) else { return; }; - let mut new_rect = ctx.round_rect_to_pixels(new_rect); if area.constrain() { - new_rect = ctx.constrain_window_rect_to_area(new_rect, area.constrain_rect()); + new_rect = Context::constrain_window_rect_to_area(new_rect, area.constrain_rect()); } // TODO(emilk): add this to a Window state instead as a command "move here next frame" @@ -802,18 +801,18 @@ fn move_and_resize_window(ctx: &Context, interaction: &ResizeInteraction) -> Opt let mut rect = interaction.start_rect; // prevent drift if interaction.left.drag { - rect.min.x = ctx.round_to_pixel(pointer_pos.x); + rect.min.x = pointer_pos.x; } else if interaction.right.drag { - rect.max.x = ctx.round_to_pixel(pointer_pos.x); + rect.max.x = pointer_pos.x; } if interaction.top.drag { - rect.min.y = ctx.round_to_pixel(pointer_pos.y); + rect.min.y = pointer_pos.y; } else if interaction.bottom.drag { - rect.max.y = ctx.round_to_pixel(pointer_pos.y); + rect.max.y = pointer_pos.y; } - Some(rect) + Some(rect.round()) } fn resize_interaction( diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index c8875dc3bf0..359e26e6222 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -2065,7 +2065,7 @@ impl Context { // --------------------------------------------------------------------- /// Constrain the position of a window/area so it fits within the provided boundary. - pub(crate) fn constrain_window_rect_to_area(&self, window: Rect, area: Rect) -> Rect { + pub(crate) fn constrain_window_rect_to_area(window: Rect, area: Rect) -> Rect { let mut pos = window.min; // Constrain to screen, unless window is too large to fit: @@ -2077,9 +2077,7 @@ impl Context { pos.y = pos.y.at_most(area.bottom() + margin_y - window.height()); // move right if needed pos.y = pos.y.at_least(area.top() - margin_y); // move down if needed - pos = self.round_pos_to_pixels(pos); - - Rect::from_min_size(pos, window.size()) + Rect::from_min_size(pos, window.size()).round() } } From 609de0fb58f59ef19b40c971f020b90a5d044c24 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Mon, 30 Sep 2024 16:48:07 +0200 Subject: [PATCH 4/4] Round misc things --- crates/egui/src/containers/resize.rs | 3 ++- crates/egui/src/containers/window.rs | 5 +++-- crates/egui/src/grid.rs | 6 ++++-- crates/egui/src/layout.rs | 4 ++-- crates/egui/src/widgets/label.rs | 4 ++-- crates/egui_extras/src/layout.rs | 2 +- 6 files changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs index 458bf11229d..ea8d8776f0e 100644 --- a/crates/egui/src/containers/resize.rs +++ b/crates/egui/src/containers/resize.rs @@ -261,7 +261,8 @@ impl Resize { state.desired_size = state .desired_size .at_least(self.min_size) - .at_most(self.max_size); + .at_most(self.max_size) + .round(); // ------------------------------ diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 1f99b7c72ba..58059b5f7ee 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -1046,13 +1046,14 @@ impl TitleBar { let inner_response = ui.horizontal(|ui| { let height = ui .fonts(|fonts| title.font_height(fonts, ui.style())) - .max(ui.spacing().interact_size.y); + .max(ui.spacing().interact_size.y) + .round(); ui.set_min_height(height); let item_spacing = ui.spacing().item_spacing; let button_size = Vec2::splat(ui.spacing().icon_width); - let pad = (height - button_size.y) / 2.0; // calculated so that the icon is on the diagonal (if window padding is symmetrical) + let pad = ((height - button_size.y) / 2.0).round(); // calculated so that the icon is on the diagonal (if window padding is symmetrical) if collapsible { ui.add_space(pad); diff --git a/crates/egui/src/grid.rs b/crates/egui/src/grid.rs index 3c4986fcad0..9c84647386d 100644 --- a/crates/egui/src/grid.rs +++ b/crates/egui/src/grid.rs @@ -179,13 +179,15 @@ impl GridLayout { let width = self.prev_state.col_width(self.col).unwrap_or(0.0); let height = self.prev_row_height(self.row); let size = child_size.max(vec2(width, height)); - Rect::from_min_size(cursor.min, size) + Rect::from_min_size(cursor.min, size).round() } #[allow(clippy::unused_self)] pub(crate) fn align_size_within_rect(&self, size: Vec2, frame: Rect) -> Rect { // TODO(emilk): allow this alignment to be customized - Align2::LEFT_CENTER.align_size_within_rect(size, frame) + Align2::LEFT_CENTER + .align_size_within_rect(size, frame) + .round() } pub(crate) fn justify_and_align(&self, frame: Rect, size: Vec2) -> Rect { diff --git a/crates/egui/src/layout.rs b/crates/egui/src/layout.rs index 32fd0d03ac4..41630802c1c 100644 --- a/crates/egui/src/layout.rs +++ b/crates/egui/src/layout.rs @@ -394,7 +394,7 @@ impl Layout { pub fn align_size_within_rect(&self, size: Vec2, outer: Rect) -> Rect { debug_assert!(size.x >= 0.0 && size.y >= 0.0); debug_assert!(!outer.is_negative()); - self.align2().align_size_within_rect(size, outer) + self.align2().align_size_within_rect(size, outer).round() } fn initial_cursor(&self, max_rect: Rect) -> Rect { @@ -634,7 +634,7 @@ impl Layout { debug_assert!(!frame_rect.any_nan()); debug_assert!(!frame_rect.is_negative()); - frame_rect + frame_rect.round() } /// Apply justify (fill width/height) and/or alignment after calling `next_space`. diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index b6ade45ae30..b5c750b79b2 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -195,10 +195,10 @@ impl Label { assert!(!galley.rows.is_empty(), "Galleys are never empty"); // collect a response from many rows: let rect = galley.rows[0].rect.translate(vec2(pos.x, pos.y)); - let mut response = ui.allocate_rect(rect, sense); + let mut response = ui.allocate_rect(rect.round(), sense); for row in galley.rows.iter().skip(1) { let rect = row.rect.translate(vec2(pos.x, pos.y)); - response |= ui.allocate_rect(rect, sense); + response |= ui.allocate_rect(rect.round(), sense); } (pos, galley, response) } else { diff --git a/crates/egui_extras/src/layout.rs b/crates/egui_extras/src/layout.rs index 239e7b1d29b..83f05ffd215 100644 --- a/crates/egui_extras/src/layout.rs +++ b/crates/egui_extras/src/layout.rs @@ -123,7 +123,7 @@ impl<'l> StripLayout<'l> { // Make sure we don't have a gap in the stripe/frame/selection background: let item_spacing = self.ui.spacing().item_spacing; - let gapless_rect = max_rect.expand2(0.5 * item_spacing); + let gapless_rect = max_rect.expand2(0.5 * item_spacing).round(); if flags.striped { self.ui.painter().rect_filled(