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 {
|
||||
ctx: CtxRef,
|
||||
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,
|
||||
|
||||
striped: bool,
|
||||
initial_x: f32,
|
||||
default_row_height: f32,
|
||||
|
@ -62,10 +69,13 @@ pub(crate) struct GridLayout {
|
|||
|
||||
impl GridLayout {
|
||||
pub(crate) fn new(ui: &Ui, id: Id) -> Self {
|
||||
let prev_state = ui.memory().grid.get(&id).cloned().unwrap_or_default();
|
||||
|
||||
Self {
|
||||
ctx: ui.ctx().clone(),
|
||||
id,
|
||||
state: ui.memory().grid.get(&id).cloned().unwrap_or_default(),
|
||||
prev_state,
|
||||
curr_state: State::default(),
|
||||
spacing: ui.style().spacing.item_spacing,
|
||||
striped: false,
|
||||
initial_x: ui.cursor().x,
|
||||
|
@ -74,20 +84,11 @@ impl GridLayout {
|
|||
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 {
|
||||
fn row_height(&self, row: usize) -> f32 {
|
||||
self.state
|
||||
fn prev_row_height(&self, row: usize) -> f32 {
|
||||
self.prev_state
|
||||
.row_height(row)
|
||||
.unwrap_or(self.default_row_height)
|
||||
}
|
||||
|
@ -101,25 +102,33 @@ impl GridLayout {
|
|||
}
|
||||
|
||||
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 height = self.row_height(self.row);
|
||||
let width = self.prev_state.col_width(self.col).unwrap_or(0.0);
|
||||
let height = self.prev_row_height(self.row);
|
||||
let size = child_size.max(vec2(width, height));
|
||||
Rect::from_min_size(cursor, size)
|
||||
}
|
||||
|
||||
pub(crate) fn advance(&mut self, cursor: &mut Pos2, rect: Rect) {
|
||||
let dirty = self.state.set_min_col_width(self.col, rect.width());
|
||||
let dirty = self.state.set_min_row_height(self.row, rect.height()) || dirty;
|
||||
pub(crate) fn advance(&mut self, cursor: &mut Pos2, frame_rect: Rect, widget_rect: Rect) {
|
||||
let dirty = self
|
||||
.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 {
|
||||
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.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) {
|
||||
let row_height = self.row_height(self.row);
|
||||
let row_height = self.prev_row_height(self.row);
|
||||
|
||||
cursor.x = self.initial_x;
|
||||
cursor.y += row_height + self.spacing.y;
|
||||
|
@ -127,9 +136,9 @@ impl GridLayout {
|
|||
self.row += 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:
|
||||
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 color = Rgba::from_white_alpha(0.0075);
|
||||
// let color = Rgba::from_black_alpha(0.2);
|
||||
|
@ -184,7 +193,10 @@ impl Grid {
|
|||
|
||||
ui.wrap(|ui| {
|
||||
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);
|
||||
add_contents(ui)
|
||||
})
|
||||
|
|
|
@ -357,9 +357,9 @@ impl Layout {
|
|||
}
|
||||
|
||||
/// 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.
|
||||
/// This is what you then pass to `advance_after_outer_rect`.
|
||||
/// Use `justify_or_align` to get the inner `Rect`.
|
||||
/// The returned `frame_rect` `Rect` will always be justified along the cross axis.
|
||||
/// This is what you then pass to `advance_after_rects`.
|
||||
/// Use `justify_or_align` to get the inner `widget_rect`.
|
||||
#[allow(clippy::collapsible_if)]
|
||||
pub(crate) fn next_space(
|
||||
&self,
|
||||
|
@ -456,20 +456,21 @@ impl Layout {
|
|||
}
|
||||
|
||||
/// 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.
|
||||
pub(crate) fn advance_after_outer_rect(
|
||||
///
|
||||
/// * `frame_rect`: the frame inside which a widget was e.g. centered
|
||||
/// * `widget_rect`: the actual rect used by the widget
|
||||
pub(crate) fn advance_after_rects(
|
||||
&self,
|
||||
region: &mut Region,
|
||||
outer_rect: Rect,
|
||||
inner_rect: Rect,
|
||||
frame_rect: Rect,
|
||||
widget_rect: Rect,
|
||||
item_spacing: Vec2,
|
||||
) {
|
||||
region.cursor = match self.main_dir {
|
||||
Direction::LeftToRight => pos2(inner_rect.right() + item_spacing.x, outer_rect.top()),
|
||||
Direction::RightToLeft => pos2(inner_rect.left() - item_spacing.x, outer_rect.top()),
|
||||
Direction::TopDown => pos2(outer_rect.left(), inner_rect.bottom() + item_spacing.y),
|
||||
Direction::BottomUp => pos2(outer_rect.left(), inner_rect.top() - item_spacing.y),
|
||||
Direction::LeftToRight => pos2(widget_rect.right() + item_spacing.x, frame_rect.top()),
|
||||
Direction::RightToLeft => pos2(widget_rect.left() - item_spacing.x, frame_rect.top()),
|
||||
Direction::TopDown => pos2(frame_rect.left(), widget_rect.bottom() + 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.
|
||||
/// The returned "outer" `Rect` will always be justified along the cross axis.
|
||||
/// This is what you then pass to `advance_after_outer_rect`.
|
||||
/// Use `justify_or_align` to get the inner `Rect`.
|
||||
/// The returned `frame_rect` will always be justified along the cross axis.
|
||||
/// This is what you then pass to `advance_after_rects`.
|
||||
/// Use `justify_or_align` to get the inner `widget_rect`.
|
||||
pub(crate) fn next_space(&self, child_size: Vec2, item_spacing: Vec2) -> Rect {
|
||||
if let Some(grid) = &self.grid {
|
||||
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.
|
||||
/// `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.
|
||||
pub(crate) fn advance_after_outer_rect(
|
||||
///
|
||||
/// * `frame_rect`: the frame inside which a widget was e.g. centered
|
||||
/// * `widget_rect`: the actual rect used by the widget
|
||||
pub(crate) fn advance_after_rects(
|
||||
&mut self,
|
||||
outer_rect: Rect,
|
||||
inner_rect: Rect,
|
||||
frame_rect: Rect,
|
||||
widget_rect: Rect,
|
||||
item_spacing: Vec2,
|
||||
) {
|
||||
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 {
|
||||
self.layout.advance_after_outer_rect(
|
||||
&mut self.region,
|
||||
outer_rect,
|
||||
inner_rect,
|
||||
item_spacing,
|
||||
)
|
||||
self.layout
|
||||
.advance_after_rects(&mut self.region, frame_rect, widget_rect, item_spacing)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -507,20 +507,19 @@ impl Ui {
|
|||
/// Returns where to put the widget.
|
||||
fn allocate_space_impl(&mut self, desired_size: Vec2) -> Rect {
|
||||
let item_spacing = self.style().spacing.item_spacing;
|
||||
let outer_child_rect = self.placer.next_space(desired_size, item_spacing);
|
||||
let inner_child_rect = self.placer.justify_or_align(outer_child_rect, desired_size);
|
||||
let frame_rect = self.placer.next_space(desired_size, item_spacing);
|
||||
let widget_rect = self.placer.justify_or_align(frame_rect, desired_size);
|
||||
|
||||
self.placer
|
||||
.advance_after_outer_rect(outer_child_rect, inner_child_rect, item_spacing);
|
||||
self.expand_to_include_rect(inner_child_rect);
|
||||
.advance_after_rects(frame_rect, widget_rect, item_spacing);
|
||||
self.expand_to_include_rect(widget_rect);
|
||||
|
||||
inner_child_rect
|
||||
widget_rect
|
||||
}
|
||||
|
||||
pub(crate) fn advance_cursor_after_rect(&mut self, rect: Rect) -> Id {
|
||||
let item_spacing = self.style().spacing.item_spacing;
|
||||
self.placer
|
||||
.advance_after_outer_rect(rect, rect, item_spacing);
|
||||
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||
self.expand_to_include_rect(rect);
|
||||
|
||||
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 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),
|
||||
final_child_rect,
|
||||
item_spacing,
|
||||
|
@ -1075,15 +1074,16 @@ impl Ui {
|
|||
}
|
||||
|
||||
/// Start a ui with vertical layout.
|
||||
/// Widgets will be centered.
|
||||
/// Widgets will be horizontally centered.
|
||||
pub fn vertical_centered<R>(
|
||||
&mut self,
|
||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||
) -> (R, Response) {
|
||||
self.with_layout(Layout::top_down(Align::Center), add_contents)
|
||||
}
|
||||
|
||||
/// 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>(
|
||||
&mut self,
|
||||
add_contents: impl FnOnce(&mut Ui) -> R,
|
||||
|
@ -1103,8 +1103,7 @@ impl Ui {
|
|||
let ret = add_contents(&mut child_ui);
|
||||
let rect = child_ui.min_rect();
|
||||
let item_spacing = self.style().spacing.item_spacing;
|
||||
self.placer
|
||||
.advance_after_outer_rect(rect, rect, item_spacing);
|
||||
self.placer.advance_after_rects(rect, rect, item_spacing);
|
||||
self.expand_to_include_rect(rect);
|
||||
(ret, self.interact(rect, child_ui.id, Sense::hover()))
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue