Skip to content

Commit

Permalink
Make RichText richer (strong, weak, code, underline, …)
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Oct 30, 2021
1 parent 617fb39 commit 4059cdf
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 30 deletions.
211 changes: 181 additions & 30 deletions egui/src/widget_text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,17 @@ use crate::{style::WidgetVisuals, text::LayoutJob, Color32, Galley, Pos2, TextSt
#[derive(Default)]
pub struct RichText {
text: String,
wrap: Option<bool>,
text_style: Option<TextStyle>,
background_color: Color32,
text_color: Option<Color32>,
wrap: Option<bool>,
code: bool,
strong: bool,
weak: bool,
strikethrough: bool,
underline: bool,
italics: bool,
raised: bool,
}

impl RichText {
Expand All @@ -23,6 +31,19 @@ impl RichText {
}
}

/// If `true`, the text will wrap at the `max_width`.
///
/// By default [`Self::wrap`] will be true in vertical layouts
/// and horizontal layouts with wrapping,
/// and false on non-wrapping horizontal layouts.
///
/// Note that any `\n` in the text will always produce a new line.
#[inline]
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = Some(wrap);
self
}

#[inline]
pub fn text(&self) -> &str {
&self.text
Expand Down Expand Up @@ -54,26 +75,86 @@ impl RichText {
self.text_style(TextStyle::Monospace)
}

/// Override text color.
/// Monospace label with different background color.
#[inline]
pub fn color(mut self, color: impl Into<Color32>) -> Self {
self.text_color = Some(color.into());
pub fn code(mut self) -> Self {
self.code = true;
self.text_style(TextStyle::Monospace)
}

/// Extra strong text (stronger color).
#[inline]
pub fn strong(mut self) -> Self {
self.strong = true;
self
}

/// If `true`, the text will wrap at the `max_width`.
/// Extra weak text (fainter color).
#[inline]
pub fn weak(mut self) -> Self {
self.weak = true;
self
}

/// Draw a line under the text.
///
/// By default [`Self::wrap`] will be true in vertical layouts
/// and horizontal layouts with wrapping,
/// and false on non-wrapping horizontal layouts.
/// If you want to control the line color, use [`LayoutJob`] instead.
#[inline]
pub fn underline(mut self) -> Self {
self.underline = true;
self
}

/// Draw a line through the text, crossing it out.
///
/// Note that any `\n` in the text will always produce a new line.
/// If you want to control the strikethrough line color, use [`LayoutJob`] instead.
#[inline]
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = Some(wrap);
pub fn strikethrough(mut self) -> Self {
self.strikethrough = true;
self
}

/// Tilt the characters to the right.
#[inline]
pub fn italics(mut self) -> Self {
self.italics = true;
self
}

/// Smaller text.
#[inline]
pub fn small(self) -> Self {
self.text_style(TextStyle::Small)
}

/// For e.g. exponents.
#[inline]
pub fn small_raised(self) -> Self {
self.text_style(TextStyle::Small).raised()
}

/// Align text to top. Only applicable together with [`Self::small()`].
#[inline]
pub fn raised(mut self) -> Self {
self.raised = true;
self
}

/// Fill-color behind the text.
#[inline]
pub fn background_color(mut self, background_color: impl Into<Color32>) -> Self {
self.background_color = background_color.into();
self
}

/// Override text color.
#[inline]
pub fn color(mut self, color: impl Into<Color32>) -> Self {
self.text_color = Some(color.into());
self
}

/// Read the font height of the selected text style.
pub fn font_height(&self, fonts: &epaint::text::Fonts, style: &crate::Style) -> f32 {
let text_style = self
.text_style
Expand All @@ -82,46 +163,98 @@ impl RichText {
fonts.row_height(text_style)
}

pub fn layout(
pub fn layout_job(
self,
ui: &Ui,
wrap_width: f32,
default_text_style: TextStyle,
) -> WidgetTextGalley {
) -> WidgetTextJob {
let text_color = self.get_text_color(ui);

let Self {
text,
text_style,
text_color,
wrap,
text_style,
background_color,
text_color: _, // already used by `get_text_color`
code,
strong: _, // already used by `get_text_color`
weak: _, // already used by `get_text_color`
strikethrough,
underline,
italics,
raised,
} = self;

let job_has_color = text_color.is_some();
let line_color = text_color.unwrap_or_else(|| ui.visuals().text_color());
let text_color = text_color.unwrap_or(crate::Color32::TEMPORARY_COLOR);

let wrap = wrap.unwrap_or_else(|| ui.wrap_text());
let wrap_width = if wrap { wrap_width } else { f32::INFINITY };

let text_style = text_style
.or(ui.style().override_text_style)
.unwrap_or(default_text_style);

let text_color = text_color.or(ui.visuals().override_text_color);

if let Some(text_color) = text_color {
let galley = ui.fonts().layout(text, text_style, text_color, wrap_width);
let mut background_color = background_color;
if code {
background_color = ui.visuals().code_bg_color;
}
let underline = if underline {
crate::Stroke::new(1.0, line_color)
} else {
crate::Stroke::none()
};
let strikethrough = if strikethrough {
crate::Stroke::new(1.0, line_color)
} else {
crate::Stroke::none()
};

WidgetTextGalley {
galley,
galley_has_color: true,
}
let valign = if raised {
crate::Align::TOP
} else {
let galley = ui
.fonts()
.layout_delayed_color(text, text_style, wrap_width);
ui.layout().vertical_align()
};

let text_format = crate::text::TextFormat {
style: text_style,
color: text_color,
background: background_color,
italics,
underline,
strikethrough,
valign,
};

let mut job = LayoutJob::single_section(text, text_format);
job.wrap_width = wrap_width;

WidgetTextJob { job, job_has_color }
}

WidgetTextGalley {
galley,
galley_has_color: false,
}
fn get_text_color(&self, ui: &Ui) -> Option<Color32> {
if let Some(text_color) = self.text_color {
Some(text_color)
} else if self.strong {
Some(ui.visuals().strong_text_color())
} else if self.weak {
Some(ui.visuals().weak_text_color())
} else {
ui.visuals().override_text_color
}
}

pub fn layout(
self,
ui: &Ui,
wrap_width: f32,
default_text_style: TextStyle,
) -> WidgetTextGalley {
let job = self.layout_job(ui, wrap_width, default_text_style);
job.layout(ui.fonts())
}
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -258,6 +391,24 @@ impl From<Arc<Galley>> for WidgetText {

// ----------------------------------------------------------------------------

pub struct WidgetTextJob {
job: LayoutJob,
job_has_color: bool,
}

impl WidgetTextJob {
pub fn layout(self, fonts: &crate::text::Fonts) -> WidgetTextGalley {
let Self { job, job_has_color } = self;
let galley = fonts.layout_job(job);
WidgetTextGalley {
galley,
galley_has_color: job_has_color,
}
}
}

// ----------------------------------------------------------------------------

/// Text that has been layed out and ready to be painted.
pub struct WidgetTextGalley {
galley: Arc<Galley>,
Expand Down
15 changes: 15 additions & 0 deletions epaint/src/text/text_layout_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,21 @@ impl LayoutJob {
}
}

#[inline]
pub fn single_section(text: String, format: TextFormat) -> Self {
Self {
sections: vec![LayoutSection {
leading_space: 0.0,
byte_range: 0..text.len(),
format,
}],
text,
wrap_width: f32::INFINITY,
break_on_newline: true,
..Default::default()
}
}

#[inline(always)]
pub fn is_empty(&self) -> bool {
self.sections.is_empty()
Expand Down

0 comments on commit 4059cdf

Please sign in to comment.