1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 21:30:03 -04:00

Add justified and/or center- and right-aligned text

Label text will now be centered, right-aligned and/or
justified based on the layout.

Galleys are no longer always pivoted in the left top corner,
so now have a Rect rather than just a size.
This commit is contained in:
Emil Ernerfeldt
2021-09-07 20:37:50 +02:00
parent cbafd10ee4
commit acb5501fe4
23 changed files with 316 additions and 112 deletions

View File

@@ -163,14 +163,14 @@ impl DemoWindows {
ScrollArea::vertical().show(ui, |ui| {
use egui::special_emojis::{GITHUB, OS_APPLE, OS_LINUX, OS_WINDOWS};
ui.label("egui is an immediate mode GUI library written in Rust.");
ui.label(format!(
"egui runs on the web, or natively on {}{}{}",
OS_APPLE, OS_LINUX, OS_WINDOWS,
));
ui.vertical_centered(|ui| {
ui.label("egui is an immediate mode GUI library written in Rust.");
ui.label(format!(
"egui runs on the web, or natively on {}{}{}",
OS_APPLE, OS_LINUX, OS_WINDOWS,
));
ui.hyperlink_to(
format!("{} egui home page", GITHUB),
"https://github.com/emilk/egui",

View File

@@ -4,10 +4,7 @@ use egui::*;
#[cfg_attr(feature = "persistence", serde(default))]
pub struct LayoutTest {
// Identical to contents of `egui::Layout`
main_dir: Direction,
main_wrap: bool,
cross_align: Align,
cross_justify: bool,
layout: LayoutSettings,
// Extra for testing wrapping:
wrap_column_width: f32,
@@ -16,15 +13,62 @@ pub struct LayoutTest {
impl Default for LayoutTest {
fn default() -> Self {
Self {
layout: LayoutSettings::top_down(),
wrap_column_width: 150.0,
wrap_row_height: 20.0,
}
}
}
#[derive(Clone, Copy, PartialEq)]
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "persistence", serde(default))]
pub struct LayoutSettings {
// Similar to the contents of `egui::Layout`
main_dir: Direction,
main_wrap: bool,
cross_align: Align,
cross_justify: bool,
}
impl Default for LayoutSettings {
fn default() -> Self {
Self::top_down()
}
}
impl LayoutSettings {
fn top_down() -> Self {
Self {
main_dir: Direction::TopDown,
main_wrap: false,
cross_align: Align::Min,
cross_justify: false,
wrap_column_width: 150.0,
wrap_row_height: 20.0,
}
}
fn top_down_justified_centered() -> Self {
Self {
main_dir: Direction::TopDown,
main_wrap: false,
cross_align: Align::Center,
cross_justify: true,
}
}
fn horizontal_wrapped() -> Self {
Self {
main_dir: Direction::LeftToRight,
main_wrap: true,
cross_align: Align::Center,
cross_justify: false,
}
}
fn layout(&self) -> Layout {
Layout::from_main_dir_and_cross_align(self.main_dir, self.cross_align)
.with_main_wrap(self.main_wrap)
.with_cross_justify(self.cross_justify)
}
}
impl super::Demo for LayoutTest {
@@ -48,22 +92,22 @@ impl super::View for LayoutTest {
ui.label("Tests and demonstrates the limits of the egui layouts");
self.content_ui(ui);
Resize::default()
.default_size([300.0, 200.0])
.default_size([150.0, 200.0])
.show(ui, |ui| {
if self.main_wrap {
if self.main_dir.is_horizontal() {
if self.layout.main_wrap {
if self.layout.main_dir.is_horizontal() {
ui.allocate_ui(
vec2(ui.available_size_before_wrap().x, self.wrap_row_height),
|ui| ui.with_layout(self.layout(), demo_ui),
|ui| ui.with_layout(self.layout.layout(), demo_ui),
);
} else {
ui.allocate_ui(
vec2(self.wrap_column_width, ui.available_size_before_wrap().y),
|ui| ui.with_layout(self.layout(), demo_ui),
|ui| ui.with_layout(self.layout.layout(), demo_ui),
);
}
} else {
ui.with_layout(self.layout(), demo_ui);
ui.with_layout(self.layout.layout(), demo_ui);
}
});
ui.label("Resize to see effect");
@@ -71,28 +115,19 @@ impl super::View for LayoutTest {
}
impl LayoutTest {
fn layout(&self) -> Layout {
Layout::from_main_dir_and_cross_align(self.main_dir, self.cross_align)
.with_main_wrap(self.main_wrap)
.with_cross_justify(self.cross_justify)
}
pub fn content_ui(&mut self, ui: &mut Ui) {
ui.horizontal(|ui| {
if ui.button("Top-down").clicked() {
*self = Default::default();
}
if ui.button("Top-down, centered and justified").clicked() {
*self = Default::default();
self.cross_align = Align::Center;
self.cross_justify = true;
}
if ui.button("Horizontal wrapped").clicked() {
*self = Default::default();
self.main_dir = Direction::LeftToRight;
self.cross_align = Align::Center;
self.main_wrap = true;
}
ui.selectable_value(&mut self.layout, LayoutSettings::top_down(), "Top-down");
ui.selectable_value(
&mut self.layout,
LayoutSettings::top_down_justified_centered(),
"Top-down, centered and justified",
);
ui.selectable_value(
&mut self.layout,
LayoutSettings::horizontal_wrapped(),
"Horizontal wrapped",
);
});
ui.horizontal(|ui| {
@@ -103,16 +138,16 @@ impl LayoutTest {
Direction::TopDown,
Direction::BottomUp,
] {
ui.radio_value(&mut self.main_dir, dir, format!("{:?}", dir));
ui.radio_value(&mut self.layout.main_dir, dir, format!("{:?}", dir));
}
});
ui.horizontal(|ui| {
ui.checkbox(&mut self.main_wrap, "Main wrap")
ui.checkbox(&mut self.layout.main_wrap, "Main wrap")
.on_hover_text("Wrap when next widget doesn't fit the current row/column");
if self.main_wrap {
if self.main_dir.is_horizontal() {
if self.layout.main_wrap {
if self.layout.main_dir.is_horizontal() {
ui.add(Slider::new(&mut self.wrap_row_height, 0.0..=200.0).text("Row height"));
} else {
ui.add(
@@ -125,25 +160,19 @@ impl LayoutTest {
ui.horizontal(|ui| {
ui.label("Cross Align:");
for &align in &[Align::Min, Align::Center, Align::Max] {
ui.radio_value(&mut self.cross_align, align, format!("{:?}", align));
ui.radio_value(&mut self.layout.cross_align, align, format!("{:?}", align));
}
});
ui.checkbox(&mut self.cross_justify, "Cross Justified")
ui.checkbox(&mut self.layout.cross_justify, "Cross Justified")
.on_hover_text("Try to fill full width/height (e.g. buttons)");
}
}
fn demo_ui(ui: &mut Ui) {
ui.monospace("Example widgets:");
for _ in 0..3 {
ui.label("label");
}
for _ in 0..3 {
let mut dummy = false;
ui.checkbox(&mut dummy, "checkbox");
}
for _ in 0..3 {
let _ = ui.button("button");
}
ui.add(egui::Label::new("Wrapping text followed by example widgets:").wrap(true));
let mut dummy = false;
ui.checkbox(&mut dummy, "checkbox");
ui.radio_value(&mut dummy, false, "radio");
let _ = ui.button("button");
}

View File

@@ -569,7 +569,7 @@ fn text_layout_ui(ui: &mut egui::Ui) {
let galley = ui.fonts().layout_job(job);
let (response, painter) = ui.allocate_painter(galley.size, Sense::hover());
let (response, painter) = ui.allocate_painter(galley.size(), Sense::hover());
painter.add(Shape::galley(response.rect.min, galley));
ui.vertical_centered(|ui| {

View File

@@ -373,7 +373,7 @@ pub struct WindowResizeTest {
impl Default for WindowResizeTest {
fn default() -> Self {
Self {
text: crate::LOREM_IPSUM.to_owned(),
text: crate::LOREM_IPSUM_LONG.to_owned(),
}
}
}
@@ -393,7 +393,7 @@ impl super::Demo for WindowResizeTest {
ui.label("This window will auto-size based on its contents.");
ui.heading("Resize this area:");
Resize::default().show(ui, |ui| {
ui.code(crate::LOREM_IPSUM);
lorem_ipsum(ui, crate::LOREM_IPSUM);
});
ui.heading("Resize the above area!");
});
@@ -405,10 +405,10 @@ impl super::Demo for WindowResizeTest {
.default_height(300.0)
.show(ctx, |ui| {
ui.label(
"This window is resizable and has a scroll area. You can shrink it to any size",
"This window is resizable and has a scroll area. You can shrink it to any size.",
);
ui.separator();
ui.code(crate::LOREM_IPSUM_LONG);
lorem_ipsum(ui, crate::LOREM_IPSUM_LONG);
});
Window::new("↔ resizable + embedded scroll")
@@ -421,8 +421,9 @@ impl super::Demo for WindowResizeTest {
ui.label("However, we have a sub-region with a scroll bar:");
ui.separator();
ScrollArea::vertical().show(ui, |ui| {
ui.code(crate::LOREM_IPSUM_LONG);
ui.code(crate::LOREM_IPSUM_LONG);
let lorem_ipsum_extra_long =
format!("{}\n\n{}", crate::LOREM_IPSUM_LONG, crate::LOREM_IPSUM_LONG);
lorem_ipsum(ui, &lorem_ipsum_extra_long);
});
// ui.heading("Some additional text here, that should also be visible"); // this works, but messes with the resizing a bit
});
@@ -435,7 +436,7 @@ impl super::Demo for WindowResizeTest {
ui.label("This window is resizable but has no scroll area. This means it can only be resized to a size where all the contents is visible.");
ui.label("egui will not clip the contents of a window, nor add whitespace to it.");
ui.separator();
ui.code(crate::LOREM_IPSUM);
lorem_ipsum(ui, crate::LOREM_IPSUM);
});
Window::new("↔ resizable with TextEdit")
@@ -459,3 +460,12 @@ impl super::Demo for WindowResizeTest {
});
}
}
fn lorem_ipsum(ui: &mut egui::Ui, text: &str) {
ui.with_layout(
egui::Layout::top_down(egui::Align::LEFT).with_cross_justify(true),
|ui| {
ui.add(egui::Label::new(text).weak());
},
);
}

View File

@@ -30,7 +30,7 @@ impl super::View for WindowWithPanels {
ui.vertical_centered(|ui| {
ui.heading("Expandable Upper Panel");
});
ui.add(egui::Label::new(crate::LOREM_IPSUM_LONG).small().weak());
lorem_ipsum(ui);
});
});
@@ -43,7 +43,7 @@ impl super::View for WindowWithPanels {
ui.heading("Left Panel");
});
egui::ScrollArea::vertical().show(ui, |ui| {
ui.add(egui::Label::new(crate::LOREM_IPSUM_LONG).small().weak());
lorem_ipsum(ui);
});
});
@@ -56,7 +56,7 @@ impl super::View for WindowWithPanels {
ui.heading("Right Panel");
});
egui::ScrollArea::vertical().show(ui, |ui| {
ui.add(egui::Label::new(crate::LOREM_IPSUM_LONG).small().weak());
lorem_ipsum(ui);
});
});
@@ -74,8 +74,17 @@ impl super::View for WindowWithPanels {
ui.heading("Central Panel");
});
egui::ScrollArea::vertical().show(ui, |ui| {
ui.add(egui::Label::new(crate::LOREM_IPSUM_LONG).small().weak());
lorem_ipsum(ui);
});
});
}
}
fn lorem_ipsum(ui: &mut egui::Ui) {
ui.with_layout(
egui::Layout::top_down(egui::Align::LEFT).with_cross_justify(true),
|ui| {
ui.add(egui::Label::new(crate::LOREM_IPSUM_LONG).small().weak());
},
);
}

View File

@@ -361,7 +361,7 @@ impl ColoredText {
let mut job = self.0.clone();
job.wrap_width = ui.available_width();
let galley = ui.fonts().layout_job(job);
let (response, painter) = ui.allocate_painter(galley.size, egui::Sense::hover());
let (response, painter) = ui.allocate_painter(galley.size(), egui::Sense::hover());
painter.add(egui::Shape::galley(response.rect.min, galley));
}
}