Add Grid::max_col_width
This commit is contained in:
parent
23581eee27
commit
26f966563a
10 changed files with 184 additions and 64 deletions
|
@ -20,11 +20,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
* Add: `ui.spacing()`, `ui.spacing_mut()`, `ui.visuals()`, `ui.visuals_mut()`.
|
||||
* Add: `ctx.set_visuals()`.
|
||||
* You can now control text wrapping with `Style::wrap`.
|
||||
* Add `Grid::max_col_width`.
|
||||
|
||||
### Changed 🔧
|
||||
|
||||
* Text will now wrap at newlines, spaces, dashes, punctuation or in the middle of a words if necessary, in that order of priority.
|
||||
* Widgets will now always line break at `\n` characters.
|
||||
* Widgets will now more intelligently choose wether or not to wrap text.
|
||||
* `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.
|
||||
|
|
|
@ -52,6 +52,7 @@ pub(crate) struct GridLayout {
|
|||
striped: bool,
|
||||
initial_x: f32,
|
||||
min_cell_size: Vec2,
|
||||
max_cell_size: Vec2,
|
||||
col: usize,
|
||||
row: usize,
|
||||
}
|
||||
|
@ -70,6 +71,7 @@ impl GridLayout {
|
|||
striped: false,
|
||||
initial_x: ui.cursor().x,
|
||||
min_cell_size: ui.spacing().interact_size,
|
||||
max_cell_size: Vec2::INFINITY,
|
||||
col: 0,
|
||||
row: 0,
|
||||
}
|
||||
|
@ -88,6 +90,10 @@ impl GridLayout {
|
|||
.unwrap_or(self.min_cell_size.y)
|
||||
}
|
||||
|
||||
pub(crate) fn wrap_text(&self) -> bool {
|
||||
self.max_cell_size.x.is_finite()
|
||||
}
|
||||
|
||||
pub(crate) fn available_rect(&self, region: &Region) -> Rect {
|
||||
// let mut rect = Rect::from_min_max(region.cursor, region.max_rect.max);
|
||||
// rect.set_height(rect.height().at_least(self.min_cell_size.y));
|
||||
|
@ -98,14 +104,22 @@ impl GridLayout {
|
|||
}
|
||||
|
||||
pub(crate) fn available_rect_finite(&self, region: &Region) -> Rect {
|
||||
// If we want to allow width-filling widgets like `Separator` in one of the first cells
|
||||
// then we need to make sure they don't spill out of the first cell:
|
||||
let width = self.prev_state.col_width(self.col);
|
||||
let width = width.or_else(|| self.curr_state.col_width(self.col));
|
||||
let width = width.unwrap_or_default().at_least(self.min_cell_size.x);
|
||||
let width = if self.max_cell_size.x.is_finite() {
|
||||
// TODO: should probably heed `prev_state` here too
|
||||
self.max_cell_size.x
|
||||
} else {
|
||||
// If we want to allow width-filling widgets like `Separator` in one of the first cells
|
||||
// then we need to make sure they don't spill out of the first cell:
|
||||
self.prev_state
|
||||
.col_width(self.col)
|
||||
.or_else(|| self.curr_state.col_width(self.col))
|
||||
.unwrap_or(self.min_cell_size.x)
|
||||
};
|
||||
|
||||
let height = region.max_rect_finite().max.y - region.cursor.y;
|
||||
let height = height.at_least(self.min_cell_size.y);
|
||||
let height = height
|
||||
.at_least(self.min_cell_size.y)
|
||||
.at_most(self.max_cell_size.y);
|
||||
|
||||
Rect::from_min_size(region.cursor, vec2(width, height))
|
||||
}
|
||||
|
@ -227,6 +241,7 @@ pub struct Grid {
|
|||
striped: bool,
|
||||
min_col_width: Option<f32>,
|
||||
min_row_height: Option<f32>,
|
||||
max_cell_size: Vec2,
|
||||
spacing: Option<Vec2>,
|
||||
}
|
||||
|
||||
|
@ -238,6 +253,7 @@ impl Grid {
|
|||
striped: false,
|
||||
min_col_width: None,
|
||||
min_row_height: None,
|
||||
max_cell_size: Vec2::INFINITY,
|
||||
spacing: None,
|
||||
}
|
||||
}
|
||||
|
@ -265,6 +281,12 @@ impl Grid {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set soft maximum width (wrapping width) of each column.
|
||||
pub fn max_col_width(mut self, max_col_width: f32) -> Self {
|
||||
self.max_cell_size.x = max_col_width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set spacing between columns/rows.
|
||||
/// Default: [`crate::style::Spacing::item_spacing`].
|
||||
pub fn spacing(mut self, spacing: impl Into<Vec2>) -> Self {
|
||||
|
@ -280,6 +302,7 @@ impl Grid {
|
|||
striped,
|
||||
min_col_width,
|
||||
min_row_height,
|
||||
max_cell_size,
|
||||
spacing,
|
||||
} = self;
|
||||
let min_col_width = min_col_width.unwrap_or_else(|| ui.spacing().interact_size.x);
|
||||
|
@ -296,6 +319,7 @@ impl Grid {
|
|||
striped,
|
||||
spacing,
|
||||
min_cell_size: vec2(min_col_width, min_row_height),
|
||||
max_cell_size,
|
||||
..GridLayout::new(ui, id)
|
||||
};
|
||||
|
||||
|
|
|
@ -27,6 +27,10 @@ impl Placer {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn grid(&self) -> Option<&grid::GridLayout> {
|
||||
self.grid.as_ref()
|
||||
}
|
||||
|
||||
pub(crate) fn is_grid(&self) -> bool {
|
||||
self.grid.is_some()
|
||||
}
|
||||
|
|
|
@ -175,6 +175,8 @@ impl Ui {
|
|||
pub fn wrap_text(&self) -> bool {
|
||||
if let Some(wrap) = self.style.wrap {
|
||||
wrap
|
||||
} else if let Some(grid) = self.placer.grid() {
|
||||
grid.wrap_text()
|
||||
} else {
|
||||
// In vertical layouts we wrap text, but in horizontal we keep going.
|
||||
self.layout().is_vertical()
|
||||
|
@ -1226,6 +1228,10 @@ impl Ui {
|
|||
self.placer.is_grid()
|
||||
}
|
||||
|
||||
pub(crate) fn grid(&self) -> Option<&grid::GridLayout> {
|
||||
self.placer.grid()
|
||||
}
|
||||
|
||||
/// Move to the next row in a grid layout or wrapping layout.
|
||||
/// Otherwise does nothing.
|
||||
pub fn end_row(&mut self) {
|
||||
|
|
|
@ -215,8 +215,12 @@ impl Label {
|
|||
|
||||
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()
|
||||
if let Some(grid) = ui.grid() {
|
||||
grid.wrap_text()
|
||||
} else {
|
||||
let layout = ui.layout();
|
||||
layout.is_vertical() || layout.is_horizontal() && layout.main_wrap()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ impl Default for Demos {
|
|||
// Tests:
|
||||
Box::new(super::layout_test::LayoutTest::default()),
|
||||
Box::new(super::tests::IdTest::default()),
|
||||
Box::new(super::input_test::InputTest::default()),
|
||||
Box::new(super::tests::TableTest::default()),
|
||||
Box::new(super::tests::InputTest::default()),
|
||||
];
|
||||
Self {
|
||||
open: vec![false; demos.len()],
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Default)]
|
||||
pub struct InputTest {
|
||||
info: String,
|
||||
}
|
||||
|
||||
impl super::Demo for InputTest {
|
||||
fn name(&self) -> &str {
|
||||
"🖱 Input Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
use super::View;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for InputTest {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let response = ui.add(
|
||||
egui::Button::new("Click, double-click or drag me with any mouse button")
|
||||
.sense(egui::Sense::click_and_drag()),
|
||||
);
|
||||
|
||||
let mut new_info = String::new();
|
||||
for &button in &[
|
||||
egui::PointerButton::Primary,
|
||||
egui::PointerButton::Secondary,
|
||||
egui::PointerButton::Middle,
|
||||
] {
|
||||
if response.clicked_by(button) {
|
||||
new_info += &format!("Clicked by {:?}\n", button);
|
||||
}
|
||||
if response.double_clicked_by(button) {
|
||||
new_info += &format!("Double-clicked by {:?}\n", button);
|
||||
}
|
||||
if response.dragged() && ui.input().pointer.button_down(button) {
|
||||
new_info += &format!("Dragged by {:?}\n", button);
|
||||
}
|
||||
}
|
||||
if !new_info.is_empty() {
|
||||
self.info = new_info;
|
||||
}
|
||||
|
||||
ui.label(&self.info);
|
||||
}
|
||||
}
|
|
@ -29,7 +29,7 @@ impl Default for LayoutTest {
|
|||
|
||||
impl super::Demo for LayoutTest {
|
||||
fn name(&self) -> &str {
|
||||
"🗺 Layout Test"
|
||||
"Layout Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
|
|
|
@ -12,7 +12,6 @@ pub mod drag_and_drop;
|
|||
pub mod font_book;
|
||||
pub mod font_contents_emoji;
|
||||
pub mod font_contents_ubuntu;
|
||||
pub mod input_test;
|
||||
pub mod layout_test;
|
||||
pub mod painting;
|
||||
pub mod scrolling;
|
||||
|
|
|
@ -3,7 +3,7 @@ pub struct IdTest {}
|
|||
|
||||
impl super::Demo for IdTest {
|
||||
fn name(&self) -> &str {
|
||||
"📋 ID Test"
|
||||
"ID Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
|
@ -49,3 +49,135 @@ impl super::View for IdTest {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
pub struct TableTest {
|
||||
num_cols: usize,
|
||||
num_rows: usize,
|
||||
min_col_width: f32,
|
||||
max_col_width: f32,
|
||||
}
|
||||
|
||||
impl Default for TableTest {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
num_cols: 4,
|
||||
num_rows: 4,
|
||||
min_col_width: 10.0,
|
||||
max_col_width: 200.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl super::Demo for TableTest {
|
||||
fn name(&self) -> &str {
|
||||
"Table Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
egui::Window::new(self.name()).open(open).show(ctx, |ui| {
|
||||
use super::View;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for TableTest {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.add(
|
||||
egui::Slider::f32(&mut self.min_col_width, 0.0..=400.0).text("Minimum column width"),
|
||||
);
|
||||
ui.add(
|
||||
egui::Slider::f32(&mut self.max_col_width, 0.0..=400.0).text("Maximum column width"),
|
||||
);
|
||||
ui.add(egui::Slider::usize(&mut self.num_cols, 0..=5).text("Columns"));
|
||||
ui.add(egui::Slider::usize(&mut self.num_rows, 0..=20).text("Rows"));
|
||||
|
||||
ui.separator();
|
||||
|
||||
let words = [
|
||||
"random", "words", "in", "a", "random", "order", "that", "just", "keeps", "going",
|
||||
"with", "some", "more",
|
||||
];
|
||||
|
||||
egui::Grid::new("my_grid")
|
||||
.striped(true)
|
||||
.min_col_width(self.min_col_width)
|
||||
.max_col_width(self.max_col_width)
|
||||
.show(ui, |ui| {
|
||||
for row in 0..self.num_rows {
|
||||
for col in 0..self.num_cols {
|
||||
if col == 0 {
|
||||
ui.label(format!("row {}", row));
|
||||
} else {
|
||||
let word_idx = row * 3 + col * 5;
|
||||
let word_count = (row * 5 + col * 75) % 13;
|
||||
let mut string = String::new();
|
||||
for word in words.iter().cycle().skip(word_idx).take(word_count) {
|
||||
string += word;
|
||||
string += " ";
|
||||
}
|
||||
ui.label(string);
|
||||
}
|
||||
}
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#[cfg_attr(feature = "persistence", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[derive(Default)]
|
||||
pub struct InputTest {
|
||||
info: String,
|
||||
}
|
||||
|
||||
impl super::Demo for InputTest {
|
||||
fn name(&self) -> &str {
|
||||
"Input Test"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::CtxRef, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.open(open)
|
||||
.resizable(false)
|
||||
.show(ctx, |ui| {
|
||||
use super::View;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for InputTest {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
let response = ui.add(
|
||||
egui::Button::new("Click, double-click or drag me with any mouse button")
|
||||
.sense(egui::Sense::click_and_drag()),
|
||||
);
|
||||
|
||||
let mut new_info = String::new();
|
||||
for &button in &[
|
||||
egui::PointerButton::Primary,
|
||||
egui::PointerButton::Secondary,
|
||||
egui::PointerButton::Middle,
|
||||
] {
|
||||
if response.clicked_by(button) {
|
||||
new_info += &format!("Clicked by {:?}\n", button);
|
||||
}
|
||||
if response.double_clicked_by(button) {
|
||||
new_info += &format!("Double-clicked by {:?}\n", button);
|
||||
}
|
||||
if response.dragged() && ui.input().pointer.button_down(button) {
|
||||
new_info += &format!("Dragged by {:?}\n", button);
|
||||
}
|
||||
}
|
||||
if !new_info.is_empty() {
|
||||
self.info = new_info;
|
||||
}
|
||||
|
||||
ui.label(&self.info);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue