From e079ac5b4660b23eb4bef886f48d92138755bf7e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 10 Jan 2021 10:41:04 +0100 Subject: [PATCH] Center window titles --- CHANGELOG.md | 1 + egui/src/align.rs | 22 ++++++++++++++++- egui/src/containers/window.rs | 45 ++++++++++++++++++++--------------- egui/src/math/rect.rs | 7 ++++++ 4 files changed, 55 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b4f4102..25677675 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### Changed 🔧 +* Center window titles. * Tweak size and alignment of some emojis to match other text. ### Fixed 🐛 diff --git a/egui/src/align.rs b/egui/src/align.rs index fd54ab8c..e4540055 100644 --- a/egui/src/align.rs +++ b/egui/src/align.rs @@ -1,6 +1,6 @@ //! One- and two-dimensional alignment ([`Align::Center`], [`LEFT_TOP`] etc). -use crate::math::{pos2, Rect}; +use crate::math::*; /// left/center/right or top/center/bottom alignment for e.g. anchors and `Layout`s. #[derive(Clone, Copy, Debug, PartialEq)] @@ -77,3 +77,23 @@ pub(crate) fn anchor_rect(rect: Rect, anchor: (Align, Align)) -> Rect { }; Rect::from_min_size(pos2(x, y), rect.size()) } + +/// e.g. center a size within a given frame +pub fn align_size_within_rect(align: (Align, Align), size: Vec2, frame: Rect) -> Rect { + let x = match align.0 { + Align::Min => frame.left(), + Align::Center => frame.center().x - size.x / 2.0, + Align::Max => frame.right() - size.x, + }; + let y = match align.1 { + Align::Min => frame.top(), + Align::Center => frame.center().y - size.y / 2.0, + Align::Max => frame.bottom() - size.y, + }; + + Rect::from_min_size(Pos2::new(x, y), size) +} + +pub fn center_size_in_rect(size: Vec2, frame: Rect) -> Rect { + align_size_within_rect(CENTER_CENTER, size, frame) +} diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 32e30c79..a070f1e8 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -632,7 +632,7 @@ struct TitleBar { id: Id, title_label: Label, title_galley: Galley, - title_rect: Rect, + min_rect: Rect, rect: Rect, } @@ -645,7 +645,8 @@ fn show_title_bar( collapsible: bool, ) -> TitleBar { let (title_bar, response) = ui.horizontal(|ui| { - ui.set_min_height(title_label.font_height(ui.fonts(), ui.style())); + let height = title_label.font_height(ui.fonts(), ui.style()); + ui.set_min_height(height); let item_spacing = ui.style().spacing.item_spacing; let button_size = ui.style().spacing.icon_width; @@ -663,27 +664,22 @@ fn show_title_bar( } let title_galley = title_label.layout(ui); - let (id, title_rect) = ui.allocate_space(title_galley.size); - if show_close_button { - // Reserve space for close button which will be added later (once we know our full width): - let close_max_x = title_rect.right() + item_spacing.x + button_size + item_spacing.x; - let close_max_x = close_max_x.max(ui.max_rect_finite().right()); - let close_rect = Rect::from_min_size( - pos2( - close_max_x - button_size, - title_rect.center().y - 0.5 * button_size, - ), - Vec2::splat(button_size), - ); - ui.expand_to_include_rect(close_rect); - } + let minimum_width = if collapsible || show_close_button { + // If at least one button is shown we make room for both buttons (since title is centered) + let space_x = item_spacing.x; + space_x + button_size + space_x + title_galley.size.x + space_x + button_size + space_x + } else { + item_spacing.x + title_galley.size.x + item_spacing.x + }; + let min_rect = Rect::from_min_size(ui.min_rect().min, vec2(minimum_width, height)); + let id = ui.advance_cursor_after_rect(min_rect); TitleBar { id, title_label, title_galley, - title_rect, + min_rect, rect: Rect::invalid(), // Will be filled in later } }); @@ -716,9 +712,18 @@ impl TitleBar { } } - // TODO: pick style for title based on move interaction + let style = if ui.ui_contains_mouse() { + ui.style().visuals.widgets.hovered + } else { + ui.style().visuals.widgets.inactive + }; + self.title_label = self.title_label.text_color(style.fg_stroke.color); + + let full_top_rect = Rect::from_x_y_ranges(self.rect.x_range(), self.min_rect.y_range()); + let text_pos = align::center_size_in_rect(self.title_galley.size, full_top_rect); + let text_pos = text_pos.left_top() - 2.0 * Vec2::Y; // HACK: center on x-height of text (looks better) self.title_label - .paint_galley(ui, self.title_rect.min, self.title_galley); + .paint_galley(ui, text_pos, self.title_galley); if let Some(content_response) = &content_response { // paint separator between title and content: @@ -760,6 +765,8 @@ fn close_button(ui: &mut Ui, rect: Rect) -> Response { let response = ui.interact(rect, close_id, Sense::click()); ui.expand_to_include_rect(response.rect); + let rect = rect.shrink(2.0); + let stroke = ui.style().interact(&response).fg_stroke; ui.painter() .line_segment([rect.left_top(), rect.right_bottom()], stroke); diff --git a/egui/src/math/rect.rs b/egui/src/math/rect.rs index 7d0ca0a8..cef3d1d5 100644 --- a/egui/src/math/rect.rs +++ b/egui/src/math/rect.rs @@ -56,6 +56,13 @@ impl Rect { } } + pub fn from_x_y_ranges(x_range: RangeInclusive, y_range: RangeInclusive) -> Self { + Rect { + min: pos2(*x_range.start(), *y_range.start()), + max: pos2(*x_range.end(), *y_range.end()), + } + } + /// Expand by this much in each direction, keeping the center #[must_use] pub fn expand(self, amnt: f32) -> Self {