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

Rich text for all widgets #855

Merged
merged 19 commits into from
Nov 1, 2021
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w
### Added ⭐
* Add context menus: See `Ui::menu_button` and `Response::context_menu` ([#543](https://github.com/emilk/egui/pull/543)).
* You can now read and write the cursor of a `TextEdit` ([#848](https://github.com/emilk/egui/pull/848)).
* Most widgets containing text (`Label`, `Button` etc) now supports rich text ([#855](https://github.com/emilk/egui/pull/855)).

### Changed 🔧
* Unifiy the four `Memory` data buckets (`data`, `data_temp`, `id_data` and `id_data_temp`) into a single `Memory::data`, with a new interface ([#836](https://github.com/emilk/egui/pull/836)).
* `ui.add(Button::new("…").text_color(…))` is now `ui.button(RichText::new("…").color(…))` (same for `Label` )([#855](https://github.com/emilk/egui/pull/855)).

### Contributors 🙏
* [mankinskin](https://github.com/mankinskin) ([#543](https://github.com/emilk/egui/pull/543))
Expand Down
44 changes: 17 additions & 27 deletions egui/src/containers/collapsing_header.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::hash::Hash;

use crate::{widgets::Label, *};
use crate::*;
use epaint::{Shape, TextStyle};

#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -141,7 +141,7 @@ pub(crate) fn paint_icon(ui: &mut Ui, openness: f32, response: &Response) {
/// ```
#[must_use = "You should call .show()"]
pub struct CollapsingHeader {
label: Label,
text: WidgetText,
default_open: bool,
id_source: Id,
enabled: bool,
Expand All @@ -157,11 +157,11 @@ impl CollapsingHeader {
/// If the label is unique and static this is fine,
/// but if it changes or there are several `CollapsingHeader` with the same title
/// you need to provide a unique id source with [`Self::id_source`].
pub fn new(label: impl ToString) -> Self {
let label = Label::new(label).wrap(false);
let id_source = Id::new(label.text());
pub fn new(text: impl Into<WidgetText>) -> Self {
let text = text.into();
let id_source = Id::new(text.text());
Self {
label,
text,
default_open: false,
id_source,
enabled: true,
Expand All @@ -185,10 +185,9 @@ impl CollapsingHeader {
self
}

/// By default, the `CollapsingHeader` text style is `TextStyle::Button`.
/// Call `.text_style(style)` to change this.
#[deprecated = "Replaced by: CollapsingHeader::new(RichText::new(text).text_style(…))"]
pub fn text_style(mut self, text_style: TextStyle) -> Self {
self.label = self.label.text_style(text_style);
self.text = self.text.text_style(text_style);
self
}

Expand Down Expand Up @@ -252,7 +251,7 @@ impl CollapsingHeader {
"Horizontal collapsing is unimplemented"
);
let Self {
mut label,
text,
default_open,
id_source,
enabled: _,
Expand All @@ -261,35 +260,31 @@ impl CollapsingHeader {
show_background: _,
} = self;

label.text_style = label
.text_style
.or(ui.style().override_text_style)
.or(Some(TextStyle::Button));

// TODO: horizontal layout, with icon and text as labels. Insert background behind using Frame.

let id = ui.make_persistent_id(id_source);
let button_padding = ui.spacing().button_padding;

let available = ui.available_rect_before_wrap();
let text_pos = available.min + vec2(ui.spacing().indent, 0.0);
let galley =
label.layout_width(ui, available.right() - text_pos.x, Color32::TEMPORARY_COLOR);
let text_max_x = text_pos.x + galley.size().x;
let wrap_width = available.right() - text_pos.x;
let wrap = Some(false);
let text = text.into_galley(ui, wrap, wrap_width, TextStyle::Button);
let text_max_x = text_pos.x + text.size().x;

let mut desired_width = text_max_x + button_padding.x - available.left();
if ui.visuals().collapsing_header_frame {
desired_width = desired_width.max(available.width()); // fill full width
}

let mut desired_size = vec2(desired_width, galley.size().y + 2.0 * button_padding.y);
let mut desired_size = vec2(desired_width, text.size().y + 2.0 * button_padding.y);
desired_size = desired_size.at_least(ui.spacing().interact_size);
let (_, rect) = ui.allocate_space(desired_size);

let mut header_response = ui.interact(rect, id, Sense::click());
let text_pos = pos2(
text_pos.x,
header_response.rect.center().y - galley.size().y / 2.0,
header_response.rect.center().y - text.size().y / 2.0,
);

let mut state = State::from_memory_with_default_open(ui.ctx(), id, default_open);
Expand All @@ -298,16 +293,11 @@ impl CollapsingHeader {
header_response.mark_changed();
}
header_response
.widget_info(|| WidgetInfo::labeled(WidgetType::CollapsingHeader, galley.text()));
.widget_info(|| WidgetInfo::labeled(WidgetType::CollapsingHeader, text.text()));

let visuals = ui
.style()
.interact_selectable(&header_response, self.selected);
let text_color = ui
.style()
.visuals
.override_text_color
.unwrap_or_else(|| visuals.text_color());

if ui.visuals().collapsing_header_frame || self.show_background {
ui.painter().add(epaint::RectShape {
Expand Down Expand Up @@ -343,7 +333,7 @@ impl CollapsingHeader {
paint_icon(ui, openness, &icon_response);
}

ui.painter().galley_with_color(text_pos, galley, text_color);
text.paint_with_visuals(ui.painter(), text_pos, &visuals);

Prepared {
id,
Expand Down
29 changes: 11 additions & 18 deletions egui/src/containers/combo_box.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
use crate::{style::WidgetVisuals, *};
use epaint::Shape;

// TODO: this should be builder struct so we can set options like width.

/// A drop-down selection menu with a descriptive label.
///
/// ```
/// # #[derive(Debug, PartialEq)]
/// # enum Enum { First, Second, Third }
/// # let mut selected = Enum::First;
/// # let mut ui = &mut egui::Ui::__test();
/// egui::ComboBox::from_label( "Select one!")
/// egui::ComboBox::from_label("Select one!")
/// .selected_text(format!("{:?}", selected))
/// .show_ui(ui, |ui| {
/// ui.selectable_value(&mut selected, Enum::First, "First");
Expand All @@ -22,14 +20,14 @@ use epaint::Shape;
#[must_use = "You should call .show*"]
pub struct ComboBox {
id_source: Id,
label: Option<Label>,
selected_text: String,
label: Option<WidgetText>,
selected_text: WidgetText,
width: Option<f32>,
}

impl ComboBox {
/// Label shown next to the combo box
pub fn from_label(label: impl Into<Label>) -> Self {
pub fn from_label(label: impl Into<WidgetText>) -> Self {
let label = label.into();
Self {
id_source: Id::new(label.text()),
Expand All @@ -56,9 +54,8 @@ impl ComboBox {
}

/// What we show as the currently selected value
#[allow(clippy::needless_pass_by_value)]
pub fn selected_text(mut self, selected_text: impl ToString) -> Self {
self.selected_text = selected_text.to_string();
pub fn selected_text(mut self, selected_text: impl Into<WidgetText>) -> Self {
self.selected_text = selected_text.into();
self
}

Expand Down Expand Up @@ -95,7 +92,7 @@ impl ComboBox {
if let Some(label) = label {
ir.response
.widget_info(|| WidgetInfo::labeled(WidgetType::ComboBox, label.text()));
ir.response |= ui.add(label);
ir.response |= ui.label(label);
} else {
ir.response
.widget_info(|| WidgetInfo::labeled(WidgetType::ComboBox, ""));
Expand All @@ -115,7 +112,7 @@ impl ComboBox {
/// # let mut ui = &mut egui::Ui::__test();
/// let alternatives = ["a", "b", "c", "d"];
/// let mut selected = 2;
/// egui::ComboBox::from_label( "Select one!").show_index(
/// egui::ComboBox::from_label("Select one!").show_index(
/// ui,
/// &mut selected,
/// alternatives.len(),
Expand Down Expand Up @@ -151,11 +148,10 @@ impl ComboBox {
}
}

#[allow(clippy::needless_pass_by_value)]
fn combo_box_dyn<'c, R>(
ui: &mut Ui,
button_id: Id,
selected: impl ToString,
selected_text: WidgetText,
menu_contents: Box<dyn FnOnce(&mut Ui) -> R + 'c>,
) -> InnerResponse<Option<R>> {
let popup_id = button_id.with("popup");
Expand All @@ -166,9 +162,7 @@ fn combo_box_dyn<'c, R>(
let full_minimum_width = ui.spacing().slider_width;
let icon_size = Vec2::splat(ui.spacing().icon_width);

let galley =
ui.fonts()
.layout_delayed_color(selected.to_string(), TextStyle::Button, f32::INFINITY);
let galley = selected_text.into_galley(ui, Some(false), f32::INFINITY, TextStyle::Button);

let width = galley.size().x + ui.spacing().item_spacing.x + icon_size.x;
let width = width.at_least(full_minimum_width);
Expand All @@ -188,8 +182,7 @@ fn combo_box_dyn<'c, R>(
paint_icon(ui.painter(), icon_rect.expand(visuals.expansion), visuals);

let text_rect = Align2::LEFT_CENTER.align_size_within_rect(galley.size(), rect);
ui.painter()
.galley_with_color(text_rect.min, galley, visuals.text_color());
galley.paint_with_visuals(ui.painter(), text_rect.min, visuals);
});

if button_response.clicked() {
Expand Down
4 changes: 2 additions & 2 deletions egui/src/containers/popup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,9 @@ fn show_tooltip_at_avoid_dyn<'c, R>(
/// egui::show_tooltip_text(ui.ctx(), egui::Id::new("my_tooltip"), "Helpful text");
/// }
/// ```
pub fn show_tooltip_text(ctx: &CtxRef, id: Id, text: impl ToString) -> Option<()> {
pub fn show_tooltip_text(ctx: &CtxRef, id: Id, text: impl Into<WidgetText>) -> Option<()> {
show_tooltip(ctx, id, |ui| {
ui.add(crate::widgets::Label::new(text));
crate::widgets::Label::new(text).ui(ui);
})
}

Expand Down
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,11 @@ 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);
let area = Area::new(title.text());
Self {
title_label,
title,
open: None,
area,
frame: None,
Expand Down Expand Up @@ -250,7 +248,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 +297,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 +334,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 +743,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 +779,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.into_galley(ui, Some(false), 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 +792,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 +826,16 @@ 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_fallback_color(
ui.painter(),
text_pos,
ui.visuals().text_color(),
);

if let Some(content_response) = &content_response {
// paint separator between title and content:
Expand Down
2 changes: 1 addition & 1 deletion egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -915,7 +915,7 @@ impl Context {
let text = format!("{} - {:?}", layer_id.short_debug_format(), area.rect(),);
// TODO: `Sense::hover_highlight()`
if ui
.add(Label::new(text).monospace().sense(Sense::click()))
.add(Label::new(RichText::new(text).monospace()).sense(Sense::click()))
.hovered
&& is_visible
{
Expand Down
Loading