Browse Source

Widgets will now always line break at `\n` characters

demo-node-graph
Emil Ernerfeldt 4 years ago
parent
commit
91ce18d62f
  1. 4
      CHANGELOG.md
  2. 4
      egui/src/containers/collapsing_header.rs
  3. 2
      egui/src/containers/combo_box.rs
  4. 4
      egui/src/containers/window.rs
  5. 2
      egui/src/introspection.rs
  6. 7
      egui/src/style.rs
  7. 11
      egui/src/ui.rs
  8. 30
      egui/src/widgets/button.rs
  9. 36
      egui/src/widgets/label.rs
  10. 7
      egui/src/widgets/selected_label.rs
  11. 7
      egui/src/widgets/slider.rs
  12. 10
      epaint/src/text/font.rs

4
CHANGELOG.md

@ -19,11 +19,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Add `Slider::clamp_to_range(bool)`: if set, clamp the incoming and outgoing values to the slider range.
* Add: `ui.spacing()`, `ui.spacing_mut()`, `ui.visuals()`, `ui.visuals_mut()`.
* Add: `ctx.set_visuals()`.
* You can now control text wrapping with `Style::wrap`.
### Changed 🔧
* Text will now wrap at newlines, spaces, dashes, punctuation or in the middle of a words if necessary, in that order of priority.
* `mouse` has be renamed `pointer` everywhere (to make it clear it includes touches too).
* Widgets will now always line break at `\n` characters.
* `mouse` has been renamed `pointer` everywhere (to make it clear it includes touches too).
* Most parts of `Response` are now methods, so `if ui.button("…").clicked {` is now `if ui.button("…").clicked() {`.
* `Response::active` is now gone. You can use `response.dragged()` or `response.clicked()` instead.
* Backend: pointer (mouse/touch) position and buttons are now passed to egui in the event stream.

4
egui/src/containers/collapsing_header.rs

@ -134,9 +134,7 @@ pub struct CollapsingHeader {
impl CollapsingHeader {
/// The `CollapsingHeader` starts out collapsed unless you call `default_open`.
pub fn new(label: impl Into<String>) -> Self {
let label = Label::new(label)
.text_style(TextStyle::Button)
.multiline(false);
let label = Label::new(label).text_style(TextStyle::Button).wrap(false);
let id_source = Id::new(label.text());
Self {
label,

2
egui/src/containers/combo_box.rs

@ -64,7 +64,7 @@ pub fn combo_box(
let text_style = TextStyle::Button;
let font = &ui.fonts()[text_style];
let galley = font.layout_single_line(selected.into());
let galley = font.layout_no_wrap(selected.into());
let width = galley.size.x + ui.spacing().item_spacing.x + icon_size.x;
let width = width.at_least(full_minimum_width);

4
egui/src/containers/window.rs

@ -38,9 +38,7 @@ impl<'open> Window<'open> {
pub fn new(title: impl Into<String>) -> Self {
let title = title.into();
let area = Area::new(&title);
let title_label = Label::new(title)
.text_style(TextStyle::Heading)
.multiline(false);
let title_label = Label::new(title).text_style(TextStyle::Heading).wrap(false);
Self {
title_label,
open: None,

2
egui/src/introspection.rs

@ -116,7 +116,7 @@ impl Widget for &epaint::stats::PaintStats {
}
pub fn label(ui: &mut Ui, alloc_info: &epaint::stats::AllocInfo, what: &str) -> Response {
ui.add(Label::new(alloc_info.format(what)).multiline(false))
ui.add(Label::new(alloc_info.format(what)).wrap(false))
}
impl Widget for &mut epaint::TessellationOptions {

7
egui/src/style.rs

@ -17,6 +17,11 @@ pub struct Style {
/// Default `TextStyle` for normal text (i.e. for `Label` and `TextEdit`).
pub body_text_style: TextStyle,
/// If set, labels buttons wtc will use this to determine whether or not
/// to wrap the text at the right edge of the `Ui` they are in.
/// By default this is `None`.
pub wrap: Option<bool>,
pub spacing: Spacing,
pub interaction: Interaction,
pub visuals: Visuals,
@ -263,6 +268,7 @@ impl Default for Style {
fn default() -> Self {
Self {
body_text_style: TextStyle::Body,
wrap: None,
spacing: Spacing::default(),
interaction: Interaction::default(),
visuals: Visuals::default(),
@ -460,6 +466,7 @@ impl Style {
pub fn ui(&mut self, ui: &mut crate::Ui) {
let Self {
body_text_style,
wrap: _,
spacing,
interaction,
visuals,

11
egui/src/ui.rs

@ -170,6 +170,17 @@ impl Ui {
self.placer.layout()
}
/// Should text wrap in this `Ui`?
/// This is determined first by [`Style::wrap`], and then by the layout of this `Ui`.
pub fn wrap_text(&self) -> bool {
if let Some(wrap) = self.style.wrap {
wrap
} else {
// In vertical layouts we wrap text, but in horizontal we keep going.
self.layout().is_vertical()
}
}
/// Create a painter for a sub-region of this Ui.
///
/// The clip-rect of the returned `Painter` will be the intersection

30
egui/src/widgets/button.rs

@ -87,19 +87,19 @@ impl Widget for Button {
small,
frame,
} = self;
let font = &ui.fonts()[text_style];
let single_line = ui.layout().is_horizontal();
let galley = if single_line {
font.layout_single_line(text)
} else {
font.layout_multiline(text, ui.available_width())
};
let mut button_padding = ui.spacing().button_padding;
if small {
button_padding.y = 0.0;
}
let total_extra = button_padding + button_padding;
let font = &ui.fonts()[text_style];
let galley = if ui.wrap_text() {
font.layout_multiline(text, ui.available_width() - total_extra.x)
} else {
font.layout_no_wrap(text)
};
let mut desired_size = galley.size + 2.0 * button_padding;
if !small {
@ -180,11 +180,10 @@ impl<'a> Widget for Checkbox<'a> {
let button_padding = spacing.button_padding;
let total_extra = button_padding + vec2(icon_width + icon_spacing, 0.0) + button_padding;
let single_line = ui.layout().is_horizontal();
let galley = if single_line {
font.layout_single_line(text)
} else {
let galley = if ui.wrap_text() {
font.layout_multiline(text, ui.available_width() - total_extra.x)
} else {
font.layout_no_wrap(text)
};
let mut desired_size = total_extra + galley.size;
@ -272,11 +271,10 @@ impl Widget for RadioButton {
let button_padding = ui.spacing().button_padding;
let total_extra = button_padding + vec2(icon_width + icon_spacing, 0.0) + button_padding;
let single_line = ui.layout().is_horizontal();
let galley = if single_line {
font.layout_single_line(text)
} else {
let galley = if ui.wrap_text() {
font.layout_multiline(text, ui.available_width() - total_extra.x)
} else {
font.layout_no_wrap(text)
};
let mut desired_size = total_extra + galley.size;

36
egui/src/widgets/label.rs

@ -5,7 +5,7 @@ use crate::{paint::Galley, *};
pub struct Label {
// TODO: not pub
pub(crate) text: String,
pub(crate) multiline: Option<bool>,
pub(crate) wrap: Option<bool>,
pub(crate) text_style: Option<TextStyle>,
pub(crate) background_color: Color32,
pub(crate) text_color: Option<Color32>,
@ -21,7 +21,7 @@ impl Label {
pub fn new(text: impl Into<String>) -> Self {
Self {
text: text.into(),
multiline: None,
wrap: None,
text_style: None,
background_color: Color32::TRANSPARENT,
text_color: None,
@ -39,16 +39,21 @@ impl Label {
}
/// If `true`, the text will wrap at the `max_width`.
/// By default `multiline` will be true in vertical layouts
/// By default [`wrap`] will be true in vertical layouts
/// and horizontal layouts with wrapping,
/// and false on non-wrapping horizontal layouts.
///
/// If the text has any newlines (`\n`) in it, multiline will automatically turn on.
pub fn multiline(mut self, multiline: bool) -> Self {
self.multiline = Some(multiline);
/// Note that any `\n` in the text label will always produce a new line.
pub fn wrap(mut self, wrap: bool) -> Self {
self.wrap = Some(wrap);
self
}
#[deprecated = "Use Label::wrap instead"]
pub fn multiline(self, multiline: bool) -> Self {
self.wrap(multiline)
}
/// The default is [`Style::body_text_style`] (generally [`TextStyle::Body`]).
pub fn text_style(mut self, text_style: TextStyle) -> Self {
self.text_style = Some(text_style);
@ -122,11 +127,12 @@ impl Label {
pub fn layout_width(&self, ui: &Ui, max_width: f32) -> Galley {
let text_style = self.text_style_or_default(ui.style());
let font = &ui.fonts()[text_style];
if self.is_multiline(ui) {
font.layout_multiline(self.text.clone(), max_width) // TODO: avoid clone
let wrap_width = if self.should_wrap(ui) {
max_width
} else {
font.layout_single_line(self.text.clone()) // TODO: avoid clone
}
f32::INFINITY
};
font.layout_multiline(self.text.clone(), wrap_width) // TODO: avoid clone
}
pub fn font_height(&self, fonts: &paint::text::Fonts, style: &Style) -> f32 {
@ -207,19 +213,17 @@ impl Label {
self.text_style.unwrap_or(style.body_text_style)
}
fn is_multiline(&self, ui: &Ui) -> bool {
self.multiline.unwrap_or_else(|| {
fn should_wrap(&self, ui: &Ui) -> bool {
self.wrap.or(ui.style().wrap).unwrap_or_else(|| {
let layout = ui.layout();
layout.is_vertical()
|| layout.is_horizontal() && layout.main_wrap()
|| self.text.contains('\n')
layout.is_vertical() || layout.is_horizontal() && layout.main_wrap()
})
}
}
impl Widget for Label {
fn ui(self, ui: &mut Ui) -> Response {
if self.is_multiline(ui)
if self.should_wrap(ui)
&& ui.layout().main_dir() == Direction::LeftToRight
&& ui.layout().main_wrap()
{

7
egui/src/widgets/selected_label.rs

@ -29,11 +29,10 @@ impl Widget for SelectableLabel {
let button_padding = ui.spacing().button_padding;
let total_extra = button_padding + button_padding;
let single_line = ui.layout().is_horizontal();
let galley = if single_line {
font.layout_single_line(text)
} else {
let galley = if ui.wrap_text() {
font.layout_multiline(text, ui.available_width() - total_extra.x)
} else {
font.layout_no_wrap(text)
};
let mut desired_size = total_extra + galley.size;

7
egui/src/widgets/slider.rs

@ -324,12 +324,7 @@ impl<'a> Slider<'a> {
fn label_ui(&mut self, ui: &mut Ui) {
if let Some(label_text) = self.text.as_deref() {
let text_color = self.text_color.unwrap_or_else(|| ui.visuals().text_color());
ui.add(
Label::new(label_text)
.multiline(false)
.text_color(text_color),
);
ui.add(Label::new(label_text).wrap(false).text_color(text_color));
}
}

10
epaint/src/text/font.rs

@ -275,6 +275,9 @@ impl Font {
/// Typeset the given text onto one row.
/// Any `\n` will show up as the replacement character.
/// Always returns exactly one `Row` in the `Galley`.
///
/// Most often you probably want `\n` to produce a new row,
/// and so [`Self::layout_no_wrap`] may be a better choice.
pub fn layout_single_line(&self, text: String) -> Galley {
let x_offsets = self.layout_single_row_fragment(&text);
let row = Row {
@ -295,6 +298,13 @@ impl Font {
}
/// Always returns at least one row.
/// Will line break at `\n`.
pub fn layout_no_wrap(&self, text: String) -> Galley {
self.layout_multiline(text, f32::INFINITY)
}
/// Always returns at least one row.
/// Will wrap text at the given width.
pub fn layout_multiline(&self, text: String, max_width_in_points: f32) -> Galley {
self.layout_multiline_with_indentation_and_max_width(text, 0.0, max_width_in_points)
}

Loading…
Cancel
Save