Auto-shrink grid
This commit is contained in:
parent
4ebaa53fea
commit
63d3e9e70b
4 changed files with 72 additions and 63 deletions
|
@ -51,8 +51,15 @@ impl State {
|
||||||
pub(crate) struct GridLayout {
|
pub(crate) struct GridLayout {
|
||||||
ctx: CtxRef,
|
ctx: CtxRef,
|
||||||
id: Id,
|
id: Id,
|
||||||
state: State,
|
|
||||||
|
/// State previous frame (if any).
|
||||||
|
/// This can be used to predict future sizes of cells.
|
||||||
|
prev_state: State,
|
||||||
|
/// State accumulated during the current frame.
|
||||||
|
curr_state: State,
|
||||||
|
|
||||||
spacing: Vec2,
|
spacing: Vec2,
|
||||||
|
|
||||||
striped: bool,
|
striped: bool,
|
||||||
initial_x: f32,
|
initial_x: f32,
|
||||||
default_row_height: f32,
|
default_row_height: f32,
|
||||||
|
@ -62,10 +69,13 @@ pub(crate) struct GridLayout {
|
||||||
|
|
||||||
impl GridLayout {
|
impl GridLayout {
|
||||||
pub(crate) fn new(ui: &Ui, id: Id) -> Self {
|
pub(crate) fn new(ui: &Ui, id: Id) -> Self {
|
||||||
|
let prev_state = ui.memory().grid.get(&id).cloned().unwrap_or_default();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
ctx: ui.ctx().clone(),
|
ctx: ui.ctx().clone(),
|
||||||
id,
|
id,
|
||||||
state: ui.memory().grid.get(&id).cloned().unwrap_or_default(),
|
prev_state,
|
||||||
|
curr_state: State::default(),
|
||||||
spacing: ui.style().spacing.item_spacing,
|
spacing: ui.style().spacing.item_spacing,
|
||||||
striped: false,
|
striped: false,
|
||||||
initial_x: ui.cursor().x,
|
initial_x: ui.cursor().x,
|
||||||
|
@ -74,20 +84,11 @@ impl GridLayout {
|
||||||
row: 0,
|
row: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `true`, add a subtle background color to every other row.
|
|
||||||
///
|
|
||||||
/// This can make a table easier to read.
|
|
||||||
/// Default: `false`.
|
|
||||||
pub(crate) fn striped(mut self, striped: bool) -> Self {
|
|
||||||
self.striped = striped;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GridLayout {
|
impl GridLayout {
|
||||||
fn row_height(&self, row: usize) -> f32 {
|
fn prev_row_height(&self, row: usize) -> f32 {
|
||||||
self.state
|
self.prev_state
|
||||||
.row_height(row)
|
.row_height(row)
|
||||||
.unwrap_or(self.default_row_height)
|
.unwrap_or(self.default_row_height)
|
||||||
}
|
}
|
||||||
|
@ -101,25 +102,33 @@ impl GridLayout {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn next_cell(&self, cursor: Pos2, child_size: Vec2) -> Rect {
|
pub(crate) fn next_cell(&self, cursor: Pos2, child_size: Vec2) -> Rect {
|
||||||
let width = self.state.col_width(self.col).unwrap_or(0.0);
|
let width = self.prev_state.col_width(self.col).unwrap_or(0.0);
|
||||||
let height = self.row_height(self.row);
|
let height = self.prev_row_height(self.row);
|
||||||
let size = child_size.max(vec2(width, height));
|
let size = child_size.max(vec2(width, height));
|
||||||
Rect::from_min_size(cursor, size)
|
Rect::from_min_size(cursor, size)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn advance(&mut self, cursor: &mut Pos2, rect: Rect) {
|
pub(crate) fn advance(&mut self, cursor: &mut Pos2, frame_rect: Rect, widget_rect: Rect) {
|
||||||
let dirty = self.state.set_min_col_width(self.col, rect.width());
|
let dirty = self
|
||||||
let dirty = self.state.set_min_row_height(self.row, rect.height()) || dirty;
|
.curr_state
|
||||||
|
.set_min_col_width(self.col, widget_rect.width());
|
||||||
|
let dirty = self
|
||||||
|
.curr_state
|
||||||
|
.set_min_row_height(self.row, widget_rect.height())
|
||||||
|
|| dirty;
|
||||||
if dirty {
|
if dirty {
|
||||||
self.ctx.memory().grid.insert(self.id, self.state.clone());
|
self.ctx
|
||||||
|
.memory()
|
||||||
|
.grid
|
||||||
|
.insert(self.id, self.curr_state.clone());
|
||||||
self.ctx.request_repaint();
|
self.ctx.request_repaint();
|
||||||
}
|
}
|
||||||
self.col += 1;
|
self.col += 1;
|
||||||
cursor.x += rect.width() + self.spacing.x;
|
cursor.x += frame_rect.width() + self.spacing.x;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn end_row(&mut self, cursor: &mut Pos2, painter: &Painter) {
|
pub(crate) fn end_row(&mut self, cursor: &mut Pos2, painter: &Painter) {
|
||||||
let row_height = self.row_height(self.row);
|
let row_height = self.prev_row_height(self.row);
|
||||||
|
|
||||||
cursor.x = self.initial_x;
|
cursor.x = self.initial_x;
|
||||||
cursor.y += row_height + self.spacing.y;
|
cursor.y += row_height + self.spacing.y;
|
||||||
|
@ -127,9 +136,9 @@ impl GridLayout {
|
||||||
self.row += 1;
|
self.row += 1;
|
||||||
|
|
||||||
if self.striped && self.row % 2 == 1 {
|
if self.striped && self.row % 2 == 1 {
|
||||||
if let Some(height) = self.state.row_height(self.row) {
|
if let Some(height) = self.prev_state.row_height(self.row) {
|
||||||
// Paint background for coming row:
|
// Paint background for coming row:
|
||||||
let size = Vec2::new(self.state.full_width(self.spacing.x), height);
|
let size = Vec2::new(self.prev_state.full_width(self.spacing.x), height);
|
||||||
let rect = Rect::from_min_size(*cursor, size);
|
let rect = Rect::from_min_size(*cursor, size);
|
||||||
let color = Rgba::from_white_alpha(0.0075);
|
let color = Rgba::from_white_alpha(0.0075);
|
||||||
// let color = Rgba::from_black_alpha(0.2);
|
// let color = Rgba::from_black_alpha(0.2);
|
||||||
|
@ -184,7 +193,10 @@ impl Grid {
|
||||||
|
|
||||||
ui.wrap(|ui| {
|
ui.wrap(|ui| {
|
||||||
let id = ui.make_persistent_id(id_source);
|
let id = ui.make_persistent_id(id_source);
|
||||||
let grid = GridLayout::new(ui, id).striped(striped);
|
let grid = GridLayout {
|
||||||
|
striped,
|
||||||
|
..GridLayout::new(ui, id)
|
||||||
|
};
|
||||||
ui.set_grid(grid);
|
ui.set_grid(grid);
|
||||||
add_contents(ui)
|
add_contents(ui)
|
||||||
})
|
})
|
||||||
|
|
|
@ -357,9 +357,9 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns where to put the next widget that is of the given size.
|
/// Returns where to put the next widget that is of the given size.
|
||||||
/// The returned "outer" `Rect` will always be justified along the cross axis.
|
/// The returned `frame_rect` `Rect` will always be justified along the cross axis.
|
||||||
/// This is what you then pass to `advance_after_outer_rect`.
|
/// This is what you then pass to `advance_after_rects`.
|
||||||
/// Use `justify_or_align` to get the inner `Rect`.
|
/// Use `justify_or_align` to get the inner `widget_rect`.
|
||||||
#[allow(clippy::collapsible_if)]
|
#[allow(clippy::collapsible_if)]
|
||||||
pub(crate) fn next_space(
|
pub(crate) fn next_space(
|
||||||
&self,
|
&self,
|
||||||
|
@ -456,20 +456,21 @@ impl Layout {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance cursor after a widget was added to a specific rectangle.
|
/// Advance cursor after a widget was added to a specific rectangle.
|
||||||
/// `outer_rect` is a hack needed because the Vec2 cursor is not quite sufficient to keep track
|
///
|
||||||
/// of what is happening when we are doing wrapping layouts.
|
/// * `frame_rect`: the frame inside which a widget was e.g. centered
|
||||||
pub(crate) fn advance_after_outer_rect(
|
/// * `widget_rect`: the actual rect used by the widget
|
||||||
|
pub(crate) fn advance_after_rects(
|
||||||
&self,
|
&self,
|
||||||
region: &mut Region,
|
region: &mut Region,
|
||||||
outer_rect: Rect,
|
frame_rect: Rect,
|
||||||
inner_rect: Rect,
|
widget_rect: Rect,
|
||||||
item_spacing: Vec2,
|
item_spacing: Vec2,
|
||||||
) {
|
) {
|
||||||
region.cursor = match self.main_dir {
|
region.cursor = match self.main_dir {
|
||||||
Direction::LeftToRight => pos2(inner_rect.right() + item_spacing.x, outer_rect.top()),
|
Direction::LeftToRight => pos2(widget_rect.right() + item_spacing.x, frame_rect.top()),
|
||||||
Direction::RightToLeft => pos2(inner_rect.left() - item_spacing.x, outer_rect.top()),
|
Direction::RightToLeft => pos2(widget_rect.left() - item_spacing.x, frame_rect.top()),
|
||||||
Direction::TopDown => pos2(outer_rect.left(), inner_rect.bottom() + item_spacing.y),
|
Direction::TopDown => pos2(frame_rect.left(), widget_rect.bottom() + item_spacing.y),
|
||||||
Direction::BottomUp => pos2(outer_rect.left(), inner_rect.top() - item_spacing.y),
|
Direction::BottomUp => pos2(frame_rect.left(), widget_rect.top() - item_spacing.y),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -82,9 +82,9 @@ impl Placer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns where to put the next widget that is of the given size.
|
/// Returns where to put the next widget that is of the given size.
|
||||||
/// The returned "outer" `Rect` will always be justified along the cross axis.
|
/// The returned `frame_rect` will always be justified along the cross axis.
|
||||||
/// This is what you then pass to `advance_after_outer_rect`.
|
/// This is what you then pass to `advance_after_rects`.
|
||||||
/// Use `justify_or_align` to get the inner `Rect`.
|
/// Use `justify_or_align` to get the inner `widget_rect`.
|
||||||
pub(crate) fn next_space(&self, child_size: Vec2, item_spacing: Vec2) -> Rect {
|
pub(crate) fn next_space(&self, child_size: Vec2, item_spacing: Vec2) -> Rect {
|
||||||
if let Some(grid) = &self.grid {
|
if let Some(grid) = &self.grid {
|
||||||
grid.next_cell(self.region.cursor, child_size)
|
grid.next_cell(self.region.cursor, child_size)
|
||||||
|
@ -109,23 +109,20 @@ impl Placer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Advance cursor after a widget was added to a specific rectangle.
|
/// Advance cursor after a widget was added to a specific rectangle.
|
||||||
/// `outer_rect` is a hack needed because the Vec2 cursor is not quite sufficient to keep track
|
///
|
||||||
/// of what is happening when we are doing wrapping layouts.
|
/// * `frame_rect`: the frame inside which a widget was e.g. centered
|
||||||
pub(crate) fn advance_after_outer_rect(
|
/// * `widget_rect`: the actual rect used by the widget
|
||||||
|
pub(crate) fn advance_after_rects(
|
||||||
&mut self,
|
&mut self,
|
||||||
outer_rect: Rect,
|
frame_rect: Rect,
|
||||||
inner_rect: Rect,
|
widget_rect: Rect,
|
||||||
item_spacing: Vec2,
|
item_spacing: Vec2,
|
||||||
) {
|
) {
|
||||||
if let Some(grid) = &mut self.grid {
|
if let Some(grid) = &mut self.grid {
|
||||||
grid.advance(&mut self.region.cursor, outer_rect)
|
grid.advance(&mut self.region.cursor, frame_rect, widget_rect)
|
||||||
} else {
|
} else {
|
||||||
self.layout.advance_after_outer_rect(
|
self.layout
|
||||||
&mut self.region,
|
.advance_after_rects(&mut self.region, frame_rect, widget_rect, item_spacing)
|
||||||
outer_rect,
|
|
||||||
inner_rect,
|
|
||||||
item_spacing,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -507,20 +507,19 @@ impl Ui {
|
||||||
/// Returns where to put the widget.
|
/// Returns where to put the widget.
|
||||||
fn allocate_space_impl(&mut self, desired_size: Vec2) -> Rect {
|
fn allocate_space_impl(&mut self, desired_size: Vec2) -> Rect {
|
||||||
let item_spacing = self.style().spacing.item_spacing;
|
let item_spacing = self.style().spacing.item_spacing;
|
||||||
let outer_child_rect = self.placer.next_space(desired_size, item_spacing);
|
let frame_rect = self.placer.next_space(desired_size, item_spacing);
|
||||||
let inner_child_rect = self.placer.justify_or_align(outer_child_rect, desired_size);
|
let widget_rect = self.placer.justify_or_align(frame_rect, desired_size);
|
||||||
|
|
||||||
self.placer
|
self.placer
|
||||||
.advance_after_outer_rect(outer_child_rect, inner_child_rect, item_spacing);
|
.advance_after_rects(frame_rect, widget_rect, item_spacing);
|
||||||
self.expand_to_include_rect(inner_child_rect);
|
self.expand_to_include_rect(widget_rect);
|
||||||
|
|
||||||
inner_child_rect
|
widget_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id {
|
pub(crate) fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id {
|
||||||
let item_spacing = self.style().spacing.item_spacing;
|
let item_spacing = self.style().spacing.item_spacing;
|
||||||
self.placer
|
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||||
.advance_after_outer_rect(rect, rect, item_spacing);
|
|
||||||
self.expand_to_include_rect(rect);
|
self.expand_to_include_rect(rect);
|
||||||
|
|
||||||
self.next_auto_id = self.next_auto_id.wrapping_add(1);
|
self.next_auto_id = self.next_auto_id.wrapping_add(1);
|
||||||
|
@ -548,7 +547,7 @@ impl Ui {
|
||||||
let ret = add_contents(&mut child_ui);
|
let ret = add_contents(&mut child_ui);
|
||||||
let final_child_rect = child_ui.min_rect();
|
let final_child_rect = child_ui.min_rect();
|
||||||
|
|
||||||
self.placer.advance_after_outer_rect(
|
self.placer.advance_after_rects(
|
||||||
outer_child_rect.union(final_child_rect),
|
outer_child_rect.union(final_child_rect),
|
||||||
final_child_rect,
|
final_child_rect,
|
||||||
item_spacing,
|
item_spacing,
|
||||||
|
@ -1075,15 +1074,16 @@ impl Ui {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a ui with vertical layout.
|
/// Start a ui with vertical layout.
|
||||||
/// Widgets will be centered.
|
/// Widgets will be horizontally centered.
|
||||||
pub fn vertical_centered<R>(
|
pub fn vertical_centered<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
) -> (R, Response) {
|
) -> (R, Response) {
|
||||||
self.with_layout(Layout::top_down(Align::Center), add_contents)
|
self.with_layout(Layout::top_down(Align::Center), add_contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a ui with vertical layout.
|
/// Start a ui with vertical layout.
|
||||||
/// Widgets will be centered and justified (fill full width).
|
/// Widgets will be horizontally centered and justified (fill full width).
|
||||||
pub fn vertical_centered_justified<R>(
|
pub fn vertical_centered_justified<R>(
|
||||||
&mut self,
|
&mut self,
|
||||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||||
|
@ -1103,8 +1103,7 @@ impl Ui {
|
||||||
let ret = add_contents(&mut child_ui);
|
let ret = add_contents(&mut child_ui);
|
||||||
let rect = child_ui.min_rect();
|
let rect = child_ui.min_rect();
|
||||||
let item_spacing = self.style().spacing.item_spacing;
|
let item_spacing = self.style().spacing.item_spacing;
|
||||||
self.placer
|
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||||
.advance_after_outer_rect(rect, rect, item_spacing);
|
|
||||||
self.expand_to_include_rect(rect);
|
self.expand_to_include_rect(rect);
|
||||||
(ret, self.interact(rect, child_ui.id, Sense::hover()))
|
(ret, self.interact(rect, child_ui.id, Sense::hover()))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue