Skip to content

Commit

Permalink
Use WidgetText for Window title
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Oct 30, 2021
1 parent 88a51d1 commit 617fb39
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 35 deletions.
46 changes: 19 additions & 27 deletions egui/src/containers/window.rs
Original file line number Diff line number Diff line change
@@ -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::*;
Expand All @@ -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<Frame>,
Expand All @@ -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<WidgetText>) -> 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,
Expand Down Expand Up @@ -250,7 +251,7 @@ impl<'open> Window<'open> {
add_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
) -> Option<InnerResponse<Option<R>>> {
let Window {
title_label,
title,
open,
area,
frame,
Expand Down Expand Up @@ -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
};
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -745,22 +746,21 @@ fn paint_frame_interaction(

struct TitleBar {
id: Id,
title_label: Label,
title_galley: std::sync::Arc<Galley>,
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);
Expand All @@ -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):
Expand All @@ -795,7 +795,6 @@ fn show_title_bar(

TitleBar {
id,
title_label,
title_galley,
min_rect,
rect: Rect::NAN, // Will be filled in later
Expand Down Expand Up @@ -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:
Expand Down
64 changes: 56 additions & 8 deletions egui/src/widget_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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,
}
Expand All @@ -101,7 +116,7 @@ impl RichText {
.fonts()
.layout_delayed_color(text, text_style, wrap_width);

WidgetTextLayout {
WidgetTextGalley {
galley,
galley_has_color: false,
}
Expand Down Expand Up @@ -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<Color32>) -> Self {
Expand All @@ -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,
},
Expand Down Expand Up @@ -221,12 +259,12 @@ impl From<Arc<Galley>> for WidgetText {
// ----------------------------------------------------------------------------

/// Text that has been layed out and ready to be painted.
pub struct WidgetTextLayout {
pub struct WidgetTextGalley {
galley: Arc<Galley>,
galley_has_color: bool,
}

impl WidgetTextLayout {
impl WidgetTextGalley {
/// Size of the layed out text.
#[inline]
pub fn size(&self) -> crate::Vec2 {
Expand All @@ -239,6 +277,11 @@ impl WidgetTextLayout {
self.galley.text()
}

#[inline]
pub fn galley(&self) -> &Arc<Galley> {
&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);
Expand All @@ -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);
}
}
9 changes: 9 additions & 0 deletions epaint/src/text/text_layout_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit 617fb39

Please sign in to comment.