From f0aac51adab1cd27da9412f170c5ea06a19ee3ad Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Mon, 12 Feb 2024 22:11:22 +0800 Subject: [PATCH 01/11] egui: Change the width of the resize corner lines to 1.0 to prevent overlapping --- crates/egui/src/containers/resize.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs index 22286368b7c..ce8dea2f1e0 100644 --- a/crates/egui/src/containers/resize.rs +++ b/crates/egui/src/containers/resize.rs @@ -381,7 +381,10 @@ pub fn paint_resize_corner_with_style( let painter = ui.painter(); let cp = painter.round_pos_to_pixels(corner.pos_in_rect(rect)); let mut w = 2.0; - let stroke = stroke.into(); + let stroke = Stroke { + width: 1.0, // Set width to 1.0 to prevent overlapping + ..stroke.into() + }; while w <= rect.width() && w <= rect.height() { painter.line_segment( From 7838d56d878be8fe31369d8e4cfd53b9b089cb53 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Mon, 12 Feb 2024 22:14:33 +0800 Subject: [PATCH 02/11] egui: Fixed Window resize corner position with large rounding --- crates/egui/src/containers/window.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 8cf5475ebbd..6566edf8892 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -495,7 +495,13 @@ impl<'open> Window<'open> { .map_or((None, None), |ir| (Some(ir.inner), Some(ir.response))); let outer_rect = frame.end(&mut area_content_ui).rect; - paint_resize_corner(&area_content_ui, &possible, outer_rect, frame_stroke); + paint_resize_corner( + &area_content_ui, + &possible, + outer_rect, + frame_stroke, + window_frame.rounding, + ); // END FRAME -------------------------------- @@ -558,6 +564,7 @@ fn paint_resize_corner( possible: &PossibleInteractions, outer_rect: Rect, stroke: impl Into, + rounding: impl Into, ) { let corner = if possible.resize_right && possible.resize_bottom { Align2::RIGHT_BOTTOM @@ -571,9 +578,20 @@ fn paint_resize_corner( return; }; + let stroke = stroke.into(); + let rounding = rounding.into(); + let radius = rounding.se; + // Adjust the corner offset to accommodate the stroke width and window rounding + let offset = if radius <= 2.0 && stroke.width < 2.0 { + 2.0 + } else { + // The corner offset is calculated to make the corner appear to be in the correct position + (2.0_f32.sqrt() * (1.0 + radius + stroke.width / 2.0) - radius) + * 45.0_f32.to_radians().cos() + }; let corner_size = Vec2::splat(ui.visuals().resize_corner_size); let corner_rect = corner.align_size_within_rect(corner_size, outer_rect); - let corner_rect = corner_rect.translate(-2.0 * corner.to_sign()); // move away from corner + let corner_rect = corner_rect.translate(-offset * corner.to_sign()); // move away from corner crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke, corner); } From e667ea64f1397adbf22bc016e929f2b2cdcd132f Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Mon, 12 Feb 2024 22:19:08 +0800 Subject: [PATCH 03/11] egui: Fixed Window frame with wide stroke width --- crates/egui/src/containers/window.rs | 49 ++++++++++++++++++++-------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 6566edf8892..efba2e13bec 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -392,7 +392,14 @@ impl<'open> Window<'open> { let header_color = frame.map_or_else(|| ctx.style().visuals.widgets.open.weak_bg_fill, |f| f.fill); - let window_frame = frame.unwrap_or_else(|| Frame::window(&ctx.style())); + let mut window_frame = frame.unwrap_or_else(|| Frame::window(&ctx.style())); + // Keep the original inner margin for later use + let window_margin = window_frame.inner_margin; + // Add padding to the inner margin if the border large then 1.0 + let border_padding = window_frame.stroke.width / 2.0; + if window_frame.stroke.width > 1.0 { + window_frame.inner_margin = window_frame.inner_margin + Margin::same(border_padding); + } let is_explicitly_closed = matches!(open, Some(false)); let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible()); @@ -420,9 +427,10 @@ impl<'open> Window<'open> { // Calculate roughly how much larger the window size is compared to the inner rect let (title_bar_height, title_content_spacing) = if with_title_bar { let style = ctx.style(); - let window_margin = window_frame.inner_margin; let spacing = window_margin.top + window_margin.bottom; let height = ctx.fonts(|f| title.font_height(f, &style)) + 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) } else { (0.0, 0.0) @@ -506,16 +514,31 @@ impl<'open> Window<'open> { // END FRAME -------------------------------- if let Some(title_bar) = title_bar { - if on_top && area_content_ui.visuals().window_highlight_topmost { - let rect = Rect::from_min_size( - outer_rect.min, - Vec2 { - x: outer_rect.size().x, - y: title_bar_height, - }, - ); + let mut title_rect = Rect::from_min_size( + outer_rect.min + vec2(border_padding, border_padding), + Vec2 { + x: outer_rect.size().x - border_padding * 2.0, + y: title_bar_height, + }, + ); + title_rect.min = area_content_ui + .painter() + .round_pos_to_pixels(title_rect.min); + title_rect.max = area_content_ui + .painter() + .round_pos_to_pixels(title_rect.max); + + if on_top && area_content_ui.visuals().window_highlight_topmost { let mut round = window_frame.rounding; + + // Eliminate the rounding gap between the title bar and the window frame + if border_padding > 1.0 { + round.ne -= border_padding; + round.nw -= border_padding; + round.se -= border_padding; + round.sw -= border_padding; + } if !is_collapsed { round.se = 0.0; round.sw = 0.0; @@ -523,18 +546,18 @@ impl<'open> Window<'open> { area_content_ui.painter().set( *where_to_put_header_background, - RectShape::filled(rect, round, header_color), + RectShape::filled(title_rect, round, header_color), ); }; // Fix title bar separator line position if let Some(response) = &mut content_response { - response.rect.min.y = outer_rect.min.y + title_bar_height; + response.rect.min.y = outer_rect.min.y + title_bar_height + border_padding; } title_bar.ui( &mut area_content_ui, - outer_rect, + title_rect, &content_response, open, &mut collapsing, From b3a69fbfc76cd13ab4a3394cf39141eb633ebee7 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Tue, 13 Feb 2024 16:59:06 +0800 Subject: [PATCH 04/11] epaint: impl `Add,AddAssign,Sub,SubAssign,Div,DivAssign,Mul,MulAssign` for Rounding --- crates/epaint/src/shape.rs | 124 +++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/crates/epaint/src/shape.rs b/crates/epaint/src/shape.rs index 033d4dfc5ed..d1cd62172b7 100644 --- a/crates/epaint/src/shape.rs +++ b/crates/epaint/src/shape.rs @@ -739,6 +739,130 @@ impl Rounding { } } +impl std::ops::Add for Rounding { + type Output = Self; + #[inline] + fn add(self, rhs: Self) -> Self { + Self { + nw: self.nw + rhs.nw, + ne: self.ne + rhs.ne, + sw: self.sw + rhs.sw, + se: self.se + rhs.se, + } + } +} + +impl std::ops::AddAssign for Rounding { + #[inline] + fn add_assign(&mut self, rhs: Self) { + *self = Self { + nw: self.nw + rhs.nw, + ne: self.ne + rhs.ne, + sw: self.sw + rhs.sw, + se: self.se + rhs.se, + }; + } +} + +impl std::ops::AddAssign for Rounding { + #[inline] + fn add_assign(&mut self, rhs: f32) { + *self = Self { + nw: self.nw + rhs, + ne: self.ne + rhs, + sw: self.sw + rhs, + se: self.se + rhs, + }; + } +} + +impl std::ops::Sub for Rounding { + type Output = Self; + #[inline] + fn sub(self, rhs: Self) -> Self { + Self { + nw: self.nw - rhs.nw, + ne: self.ne - rhs.ne, + sw: self.sw - rhs.sw, + se: self.se - rhs.se, + } + } +} + +impl std::ops::SubAssign for Rounding { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + *self = Self { + nw: self.nw - rhs.nw, + ne: self.ne - rhs.ne, + sw: self.sw - rhs.sw, + se: self.se - rhs.se, + }; + } +} + +impl std::ops::SubAssign for Rounding { + #[inline] + fn sub_assign(&mut self, rhs: f32) { + *self = Self { + nw: self.nw - rhs, + ne: self.ne - rhs, + sw: self.sw - rhs, + se: self.se - rhs, + }; + } +} + +impl std::ops::Div for Rounding { + type Output = Self; + #[inline] + fn div(self, rhs: f32) -> Self { + Self { + nw: self.nw / rhs, + ne: self.ne / rhs, + sw: self.sw / rhs, + se: self.se / rhs, + } + } +} + +impl std::ops::DivAssign for Rounding { + #[inline] + fn div_assign(&mut self, rhs: f32) { + *self = Self { + nw: self.nw / rhs, + ne: self.ne / rhs, + sw: self.sw / rhs, + se: self.se / rhs, + }; + } +} + +impl std::ops::Mul for Rounding { + type Output = Self; + #[inline] + fn mul(self, rhs: f32) -> Self { + Self { + nw: self.nw * rhs, + ne: self.ne * rhs, + sw: self.sw * rhs, + se: self.se * rhs, + } + } +} + +impl std::ops::MulAssign for Rounding { + #[inline] + fn mul_assign(&mut self, rhs: f32) { + *self = Self { + nw: self.nw * rhs, + ne: self.ne * rhs, + sw: self.sw * rhs, + se: self.se * rhs, + }; + } +} + // ---------------------------------------------------------------------------- /// How to paint some text on screen. From 849f4531e3ccdd03b8e149840c81bb601d866eb7 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Tue, 13 Feb 2024 17:16:52 +0800 Subject: [PATCH 05/11] egui: impl `Add,AddAssign,Div,DivAssign,Mul,MulAssign,Sub,SubAssign` for Margin --- crates/egui/src/style.rs | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 6b77e8a14ec..a5b19e14d9d 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -702,6 +702,116 @@ impl std::ops::Add for Margin { } } +impl std::ops::Add for Margin { + type Output = Self; + + #[inline] + fn add(self, v: f32) -> Self { + Self { + left: self.left + v, + right: self.right + v, + top: self.top + v, + bottom: self.bottom + v, + } + } +} + +impl std::ops::AddAssign for Margin { + #[inline] + fn add_assign(&mut self, v: f32) { + self.left += v; + self.right += v; + self.top += v; + self.bottom += v; + } +} + +impl std::ops::Div for Margin { + type Output = Self; + + #[inline] + fn div(self, v: f32) -> Self { + Self { + left: self.left / v, + right: self.right / v, + top: self.top / v, + bottom: self.bottom / v, + } + } +} + +impl std::ops::DivAssign for Margin { + #[inline] + fn div_assign(&mut self, v: f32) { + self.left /= v; + self.right /= v; + self.top /= v; + self.bottom /= v; + } +} + +impl std::ops::Mul for Margin { + type Output = Self; + + #[inline] + fn mul(self, v: f32) -> Self { + Self { + left: self.left * v, + right: self.right * v, + top: self.top * v, + bottom: self.bottom * v, + } + } +} + +impl std::ops::MulAssign for Margin { + #[inline] + fn mul_assign(&mut self, v: f32) { + self.left *= v; + self.right *= v; + self.top *= v; + self.bottom *= v; + } +} + +impl std::ops::Sub for Margin { + type Output = Self; + + #[inline] + fn sub(self, other: Self) -> Self { + Self { + left: self.left - other.left, + right: self.right - other.right, + top: self.top - other.top, + bottom: self.bottom - other.bottom, + } + } +} + +impl std::ops::Sub for Margin { + type Output = Self; + + #[inline] + fn sub(self, v: f32) -> Self { + Self { + left: self.left - v, + right: self.right - v, + top: self.top - v, + bottom: self.bottom - v, + } + } +} + +impl std::ops::SubAssign for Margin { + #[inline] + fn sub_assign(&mut self, v: f32) { + self.left -= v; + self.right -= v; + self.top -= v; + self.bottom -= v; + } +} + // ---------------------------------------------------------------------------- /// How and when interaction happens. From f74dcbcec4ef0356cb20f047b2f4b98e424bd219 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Tue, 13 Feb 2024 17:32:46 +0800 Subject: [PATCH 06/11] egui: Fixed incorrect Window resize corner rounding selection --- crates/egui/src/containers/window.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index efba2e13bec..804c2f2df47 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -589,21 +589,20 @@ fn paint_resize_corner( stroke: impl Into, rounding: impl Into, ) { - let corner = if possible.resize_right && possible.resize_bottom { - Align2::RIGHT_BOTTOM + let stroke = stroke.into(); + let rounding = rounding.into(); + let (corner, radius) = if possible.resize_right && possible.resize_bottom { + (Align2::RIGHT_BOTTOM, rounding.se) } else if possible.resize_left && possible.resize_bottom { - Align2::LEFT_BOTTOM + (Align2::LEFT_BOTTOM, rounding.sw) } else if possible.resize_left && possible.resize_top { - Align2::LEFT_TOP + (Align2::LEFT_TOP, rounding.nw) } else if possible.resize_right && possible.resize_top { - Align2::RIGHT_TOP + (Align2::RIGHT_TOP, rounding.ne) } else { return; }; - let stroke = stroke.into(); - let rounding = rounding.into(); - let radius = rounding.se; // Adjust the corner offset to accommodate the stroke width and window rounding let offset = if radius <= 2.0 && stroke.width < 2.0 { 2.0 From 0f75118e93017ce76ae89447dd889c5057322b13 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Tue, 13 Feb 2024 17:40:25 +0800 Subject: [PATCH 07/11] egui: Adjust Window border padding to avoid title bar overlap --- crates/egui/src/containers/window.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 804c2f2df47..53d09c9356f 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -395,11 +395,9 @@ impl<'open> Window<'open> { let mut window_frame = frame.unwrap_or_else(|| Frame::window(&ctx.style())); // Keep the original inner margin for later use let window_margin = window_frame.inner_margin; - // Add padding to the inner margin if the border large then 1.0 let border_padding = window_frame.stroke.width / 2.0; - if window_frame.stroke.width > 1.0 { - window_frame.inner_margin = window_frame.inner_margin + Margin::same(border_padding); - } + // Add border padding to the inner margin to prevent it from covering the contents + window_frame.inner_margin += border_padding; let is_explicitly_closed = matches!(open, Some(false)); let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible()); @@ -533,12 +531,8 @@ impl<'open> Window<'open> { let mut round = window_frame.rounding; // Eliminate the rounding gap between the title bar and the window frame - if border_padding > 1.0 { - round.ne -= border_padding; - round.nw -= border_padding; - round.se -= border_padding; - round.sw -= border_padding; - } + round -= border_padding; + if !is_collapsed { round.se = 0.0; round.sw = 0.0; From 7d899eee959a2b7fdd2e92c647e646b97b8ae502 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Tue, 13 Feb 2024 22:11:23 +0800 Subject: [PATCH 08/11] egui: Shrink by 0.1 to prevent the title bar hline from infringing the border --- crates/egui/src/containers/window.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 53d09c9356f..cef921a5049 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -1070,7 +1070,10 @@ impl TitleBar { let y = content_response.rect.top(); // let y = lerp(self.rect.bottom()..=content_response.rect.top(), 0.5); let stroke = ui.visuals().widgets.noninteractive.bg_stroke; - ui.painter().hline(outer_rect.x_range(), y, stroke); + // Workaround: To prevent border infringement, + // the 0.1 value should ideally be calculated using TessellationOptions::feathering_size_in_pixels + let x_range = outer_rect.x_range().shrink(0.1); + ui.painter().hline(x_range, y, stroke); } // Don't cover the close- and collapse buttons: From 6b5010c815acdca3d16089b9674384aa52a3f155 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Mon, 19 Feb 2024 10:13:27 +0800 Subject: [PATCH 09/11] egui: Modify `paint_resize_corner_with_style()` to take a `Color32` instead of a `Stroke` --- crates/egui/src/containers/resize.rs | 6 +++--- crates/egui/src/containers/window.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs index ce8dea2f1e0..ef0d045de6f 100644 --- a/crates/egui/src/containers/resize.rs +++ b/crates/egui/src/containers/resize.rs @@ -369,13 +369,13 @@ use epaint::Stroke; pub fn paint_resize_corner(ui: &Ui, response: &Response) { let stroke = ui.style().interact(response).fg_stroke; - paint_resize_corner_with_style(ui, &response.rect, stroke, Align2::RIGHT_BOTTOM); + paint_resize_corner_with_style(ui, &response.rect, stroke.color, Align2::RIGHT_BOTTOM); } pub fn paint_resize_corner_with_style( ui: &Ui, rect: &Rect, - stroke: impl Into, + color: impl Into, corner: Align2, ) { let painter = ui.painter(); @@ -383,7 +383,7 @@ pub fn paint_resize_corner_with_style( let mut w = 2.0; let stroke = Stroke { width: 1.0, // Set width to 1.0 to prevent overlapping - ..stroke.into() + color: color.into(), }; while w <= rect.width() && w <= rect.height() { diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index cef921a5049..19aee2082f9 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -608,7 +608,7 @@ fn paint_resize_corner( let corner_size = Vec2::splat(ui.visuals().resize_corner_size); let corner_rect = corner.align_size_within_rect(corner_size, outer_rect); let corner_rect = corner_rect.translate(-offset * corner.to_sign()); // move away from corner - crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke, corner); + crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke.color, corner); } // ---------------------------------------------------------------------------- From 3d5ed3ec0346b0638f1c1665d6baed0b4ef3163a Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Mon, 19 Feb 2024 10:20:00 +0800 Subject: [PATCH 10/11] egui: Added `Painter::round_rect_to_pixels` helper --- crates/egui/src/painter.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/egui/src/painter.rs b/crates/egui/src/painter.rs index 8689ec81530..788319dc936 100644 --- a/crates/egui/src/painter.rs +++ b/crates/egui/src/painter.rs @@ -152,6 +152,12 @@ impl Painter { pub fn round_pos_to_pixels(&self, pos: Pos2) -> Pos2 { self.ctx().round_pos_to_pixels(pos) } + + /// Useful for pixel-perfect rendering. + #[inline] + pub fn round_rect_to_pixels(&self, rect: Rect) -> Rect { + self.ctx().round_rect_to_pixels(rect) + } } /// ## Low level From 6567a8a74c1d14e758aaf537a717148a5aa7d2b5 Mon Sep 17 00:00:00 2001 From: Varphone Wong Date: Mon, 19 Feb 2024 10:23:05 +0800 Subject: [PATCH 11/11] egui: Simplify code using `Painter::round_rect_to_pixels` --- crates/egui/src/containers/window.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 19aee2082f9..eaea0f5ac2b 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -520,12 +520,7 @@ impl<'open> Window<'open> { }, ); - title_rect.min = area_content_ui - .painter() - .round_pos_to_pixels(title_rect.min); - title_rect.max = area_content_ui - .painter() - .round_pos_to_pixels(title_rect.max); + title_rect = area_content_ui.painter().round_rect_to_pixels(title_rect); if on_top && area_content_ui.visuals().window_highlight_topmost { let mut round = window_frame.rounding;