diff --git a/egui/src/containers/window.rs b/egui/src/containers/window.rs index 01545bb2b16..bad59e01c77 100644 --- a/egui/src/containers/window.rs +++ b/egui/src/containers/window.rs @@ -1,6 +1,6 @@ // WARNING: the code in here is horrible. It is a behemoth that needs breaking up into simpler parts. -use crate::{widgets::*, *}; +use crate::{widget_text::WidgetTextGalley, *}; use epaint::*; use super::*; @@ -23,7 +23,7 @@ use super::*; /// }); #[must_use = "You should call .show()"] pub struct Window<'open> { - title_label: Label, + title: WidgetText, open: Option<&'open mut bool>, area: Area, frame: Option, @@ -37,13 +37,14 @@ impl<'open> Window<'open> { /// The window title is used as a unique [`Id`] and must be unique, and should not change. /// This is true even if you disable the title bar with `.title_bar(false)`. /// If you need a changing title, you must call `window.id(…)` with a fixed id. - #[allow(clippy::needless_pass_by_value)] - pub fn new(title: impl ToString) -> Self { - let title = title.to_string(); - let area = Area::new(&title); - let title_label = Label::new(title).text_style(TextStyle::Heading).wrap(false); + pub fn new(title: impl Into) -> Self { + let title = title + .into() + .fallback_text_style(TextStyle::Heading) + .wrap(false); + let area = Area::new(title.text()); Self { - title_label, + title, open: None, area, frame: None, @@ -250,7 +251,7 @@ impl<'open> Window<'open> { add_contents: Box R + 'c>, ) -> Option>> { let Window { - title_label, + title, open, area, frame, @@ -299,7 +300,7 @@ impl<'open> Window<'open> { .and_then(|window_interaction| { // Calculate roughly how much larger the window size is compared to the inner rect let title_bar_height = if with_title_bar { - title_label.font_height(ctx.fonts(), &ctx.style()) + title_content_spacing + title.font_height(ctx.fonts(), &ctx.style()) + title_content_spacing } else { 0.0 }; @@ -336,7 +337,7 @@ impl<'open> Window<'open> { let title_bar = if with_title_bar { let title_bar = show_title_bar( &mut frame.content_ui, - title_label, + title, show_close_button, collapsing_id, &mut collapsing, @@ -745,22 +746,21 @@ fn paint_frame_interaction( struct TitleBar { id: Id, - title_label: Label, - title_galley: std::sync::Arc, + title_galley: WidgetTextGalley, min_rect: Rect, rect: Rect, } fn show_title_bar( ui: &mut Ui, - title_label: Label, + title: WidgetText, show_close_button: bool, collapsing_id: Id, collapsing: &mut collapsing_header::State, collapsible: bool, ) -> TitleBar { let inner_response = ui.horizontal(|ui| { - let height = title_label + let height = title .font_height(ui.fonts(), ui.style()) .max(ui.spacing().interact_size.y); ui.set_min_height(height); @@ -782,7 +782,7 @@ fn show_title_bar( collapsing_header::paint_icon(ui, openness, &collapse_button_response); } - let title_galley = title_label.layout(ui); + let title_galley = title.layout(ui, f32::INFINITY, TextStyle::Heading); 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): @@ -795,7 +795,6 @@ fn show_title_bar( TitleBar { id, - title_label, title_galley, min_rect, rect: Rect::NAN, // Will be filled in later @@ -830,20 +829,13 @@ impl TitleBar { } } - // Always have inactive style for the window. - // It is VERY annoying to e.g. change it when moving the window. - let style = ui.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 = emath::align::center_size_in_rect(self.title_galley.size(), full_top_rect).left_top(); - let text_pos = text_pos - self.title_galley.rect.min.to_vec2(); + let text_pos = text_pos - self.title_galley.galley().rect.min.to_vec2(); let text_pos = text_pos - 1.5 * Vec2::Y; // HACK: center on x-height of text (looks better) - let text_color = ui.visuals().text_color(); - self.title_label - .paint_galley(ui, text_pos, self.title_galley, false, text_color); + self.title_galley + .paint_with_color(ui, text_pos, ui.visuals().text_color()); if let Some(content_response) = &content_response { // paint separator between title and content: diff --git a/egui/src/widget_text.rs b/egui/src/widget_text.rs index 88bda692618..4efda002091 100644 --- a/egui/src/widget_text.rs +++ b/egui/src/widget_text.rs @@ -35,6 +35,13 @@ impl RichText { self } + /// Set the [`TextStyle`] unless it has already been set + #[inline] + pub fn fallback_text_style(mut self, text_style: TextStyle) -> Self { + self.text_style.get_or_insert(text_style); + self + } + /// Use [`TextStyle::Heading`]. #[inline] pub fn heading(self) -> Self { @@ -67,12 +74,20 @@ impl RichText { self } + pub fn font_height(&self, fonts: &epaint::text::Fonts, style: &crate::Style) -> f32 { + let text_style = self + .text_style + .or(style.override_text_style) + .unwrap_or(style.body_text_style); + fonts.row_height(text_style) + } + pub fn layout( self, ui: &Ui, wrap_width: f32, default_text_style: TextStyle, - ) -> WidgetTextLayout { + ) -> WidgetTextGalley { let Self { text, text_style, @@ -92,7 +107,7 @@ impl RichText { if let Some(text_color) = text_color { let galley = ui.fonts().layout(text, text_style, text_color, wrap_width); - WidgetTextLayout { + WidgetTextGalley { galley, galley_has_color: true, } @@ -101,7 +116,7 @@ impl RichText { .fonts() .layout_delayed_color(text, text_style, wrap_width); - WidgetTextLayout { + WidgetTextGalley { galley, galley_has_color: false, } @@ -142,6 +157,15 @@ impl WidgetText { } } + /// Set the [`TextStyle`] unless it has already been set + #[inline] + pub fn fallback_text_style(self, text_style: TextStyle) -> Self { + match self { + Self::RichText(text) => Self::RichText(text.fallback_text_style(text_style)), + Self::LayoutJob(_) | Self::Galley(_) => self, + } + } + /// Override text color if, and only if, this is a [`RichText`]. #[inline] pub fn color(self, color: impl Into) -> Self { @@ -160,22 +184,36 @@ impl WidgetText { } } + pub fn font_height(&self, fonts: &epaint::text::Fonts, style: &crate::Style) -> f32 { + match self { + Self::RichText(text) => text.font_height(fonts, style), + Self::LayoutJob(job) => job.font_height(fonts), + Self::Galley(galley) => { + if let Some(row) = galley.rows.first() { + row.height() + } else { + galley.size().y + } + } + } + } + pub fn layout( self, ui: &Ui, wrap_width: f32, default_text_style: TextStyle, - ) -> WidgetTextLayout { + ) -> WidgetTextGalley { match self { Self::RichText(text) => text.layout(ui, wrap_width, default_text_style), Self::LayoutJob(mut job) => { job.wrap_width = wrap_width; - WidgetTextLayout { + WidgetTextGalley { galley: ui.fonts().layout_job(job), galley_has_color: true, } } - Self::Galley(galley) => WidgetTextLayout { + Self::Galley(galley) => WidgetTextGalley { galley, galley_has_color: true, }, @@ -221,12 +259,12 @@ impl From> for WidgetText { // ---------------------------------------------------------------------------- /// Text that has been layed out and ready to be painted. -pub struct WidgetTextLayout { +pub struct WidgetTextGalley { galley: Arc, galley_has_color: bool, } -impl WidgetTextLayout { +impl WidgetTextGalley { /// Size of the layed out text. #[inline] pub fn size(&self) -> crate::Vec2 { @@ -239,6 +277,11 @@ impl WidgetTextLayout { self.galley.text() } + #[inline] + pub fn galley(&self) -> &Arc { + &self.galley + } + pub fn paint(self, ui: &Ui, text_pos: Pos2, visuals: &WidgetVisuals) { if self.galley_has_color { ui.painter().galley(text_pos, self.galley); @@ -247,4 +290,9 @@ impl WidgetTextLayout { .galley_with_color(text_pos, self.galley, visuals.text_color()); } } + + pub fn paint_with_color(self, ui: &Ui, text_pos: Pos2, text_color: Color32) { + ui.painter() + .galley_with_color(text_pos, self.galley, text_color); + } } diff --git a/epaint/src/text/text_layout_types.rs b/epaint/src/text/text_layout_types.rs index 26ce416d1e0..c70efcfe58e 100644 --- a/epaint/src/text/text_layout_types.rs +++ b/epaint/src/text/text_layout_types.rs @@ -134,6 +134,15 @@ impl LayoutJob { format, }); } + + /// The height of the tallest used font in the job. + pub fn font_height(&self, fonts: &crate::Fonts) -> f32 { + let mut max_height = 0.0_f32; + for section in &self.sections { + max_height = max_height.max(fonts.row_height(section.format.style)); + } + max_height + } } impl std::hash::Hash for LayoutJob {