Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

egui: Fixed the incorrect display of the Window frame with a wide border or large rounding #4032

Merged
merged 11 commits into from
Mar 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions crates/egui/src/containers/resize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -369,19 +369,22 @@ 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<Stroke>,
color: impl Into<Color32>,
corner: Align2,
) {
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
varphone marked this conversation as resolved.
Show resolved Hide resolved
color: color.into(),
};

while w <= rect.width() && w <= rect.height() {
painter.line_segment(
Expand Down
76 changes: 54 additions & 22 deletions crates/egui/src/containers/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,12 @@ 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;
let border_padding = window_frame.stroke.width / 2.0;
// 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());
Expand Down Expand Up @@ -420,9 +425,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)
Expand Down Expand Up @@ -495,40 +501,52 @@ 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 --------------------------------

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 = 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;

// Eliminate the rounding gap between the title bar and the window frame
round -= border_padding;

if !is_collapsed {
round.se = 0.0;
round.sw = 0.0;
}

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,
Expand Down Expand Up @@ -558,23 +576,34 @@ fn paint_resize_corner(
possible: &PossibleInteractions,
outer_rect: Rect,
stroke: impl Into<Stroke>,
rounding: impl Into<Rounding>,
) {
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;
};

// 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
crate::resize::paint_resize_corner_with_style(ui, &corner_rect, stroke, 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.color, corner);
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -1036,7 +1065,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:
Expand Down
6 changes: 6 additions & 0 deletions crates/egui/src/painter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
110 changes: 110 additions & 0 deletions crates/egui/src/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,116 @@ impl std::ops::Add for Margin {
}
}

impl std::ops::Add<f32> 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<f32> 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<f32> 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<f32> 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<f32> 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<f32> 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<f32> 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<f32> 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.
Expand Down
Loading
Loading