2022-03-31 19:13:25 +00:00
|
|
|
use egui::{Pos2, Rect, Response, Sense, Ui};
|
|
|
|
|
|
|
|
#[derive(Clone, Copy)]
|
|
|
|
pub(crate) enum CellSize {
|
|
|
|
/// Absolute size in points
|
|
|
|
Absolute(f32),
|
|
|
|
/// Take all available space
|
|
|
|
Remainder,
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Cells are positioned in two dimensions, cells go in one direction and form lines.
|
|
|
|
///
|
|
|
|
/// In a strip there's only one line which goes in the direction of the strip:
|
|
|
|
///
|
2022-05-23 15:25:31 +00:00
|
|
|
/// In a horizontal strip, a [`StripLayout`] with horizontal [`CellDirection`] is used.
|
|
|
|
/// Its cells go from left to right inside this [`StripLayout`].
|
2022-03-31 19:13:25 +00:00
|
|
|
///
|
2022-05-23 15:25:31 +00:00
|
|
|
/// In a table there's a [`StripLayout`] for each table row with a horizontal [`CellDirection`].
|
2022-03-31 19:13:25 +00:00
|
|
|
/// Its cells go from left to right. And the lines go from top to bottom.
|
|
|
|
pub(crate) enum CellDirection {
|
2022-04-01 12:43:43 +00:00
|
|
|
/// Cells go from left to right.
|
2022-03-31 19:13:25 +00:00
|
|
|
Horizontal,
|
2022-04-01 12:43:43 +00:00
|
|
|
/// Cells go from top to bottom.
|
2022-03-31 19:13:25 +00:00
|
|
|
Vertical,
|
|
|
|
}
|
|
|
|
|
2022-05-23 15:25:31 +00:00
|
|
|
/// Positions cells in [`CellDirection`] and starts a new line on [`StripLayout::end_line`]
|
2022-03-31 19:13:25 +00:00
|
|
|
pub struct StripLayout<'l> {
|
2022-04-11 15:54:57 +00:00
|
|
|
pub(crate) ui: &'l mut Ui,
|
2022-03-31 19:13:25 +00:00
|
|
|
direction: CellDirection,
|
2022-04-11 15:54:57 +00:00
|
|
|
pub(crate) rect: Rect,
|
2022-04-01 12:43:43 +00:00
|
|
|
cursor: Pos2,
|
2022-03-31 19:13:25 +00:00
|
|
|
max: Pos2,
|
2022-04-01 13:27:42 +00:00
|
|
|
pub(crate) clip: bool,
|
2022-04-11 08:29:34 +00:00
|
|
|
cell_layout: egui::Layout,
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'l> StripLayout<'l> {
|
2022-04-11 08:29:34 +00:00
|
|
|
pub(crate) fn new(
|
|
|
|
ui: &'l mut Ui,
|
|
|
|
direction: CellDirection,
|
|
|
|
clip: bool,
|
|
|
|
cell_layout: egui::Layout,
|
|
|
|
) -> Self {
|
2022-03-31 19:13:25 +00:00
|
|
|
let rect = ui.available_rect_before_wrap();
|
|
|
|
let pos = rect.left_top();
|
|
|
|
|
|
|
|
Self {
|
|
|
|
ui,
|
2022-04-11 08:29:34 +00:00
|
|
|
direction,
|
2022-03-31 19:13:25 +00:00
|
|
|
rect,
|
2022-04-01 12:43:43 +00:00
|
|
|
cursor: pos,
|
2022-03-31 19:13:25 +00:00
|
|
|
max: pos,
|
2022-04-01 13:27:42 +00:00
|
|
|
clip,
|
2022-04-11 08:29:34 +00:00
|
|
|
cell_layout,
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
fn cell_rect(&self, width: &CellSize, height: &CellSize) -> Rect {
|
|
|
|
Rect {
|
2022-04-01 12:43:43 +00:00
|
|
|
min: self.cursor,
|
2022-03-31 19:13:25 +00:00
|
|
|
max: Pos2 {
|
|
|
|
x: match width {
|
2022-04-01 12:43:43 +00:00
|
|
|
CellSize::Absolute(width) => self.cursor.x + width,
|
|
|
|
CellSize::Remainder => self.rect.right(),
|
2022-03-31 19:13:25 +00:00
|
|
|
},
|
|
|
|
y: match height {
|
2022-04-01 12:43:43 +00:00
|
|
|
CellSize::Absolute(height) => self.cursor.y + height,
|
|
|
|
CellSize::Remainder => self.rect.bottom(),
|
2022-03-31 19:13:25 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_pos(&mut self, rect: Rect) {
|
2022-04-01 12:43:43 +00:00
|
|
|
self.max.x = self.max.x.max(rect.right());
|
|
|
|
self.max.y = self.max.y.max(rect.bottom());
|
|
|
|
|
2022-03-31 19:13:25 +00:00
|
|
|
match self.direction {
|
|
|
|
CellDirection::Horizontal => {
|
2022-04-01 12:43:43 +00:00
|
|
|
self.cursor.x = rect.right() + self.ui.spacing().item_spacing.x;
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
CellDirection::Vertical => {
|
2022-04-01 12:43:43 +00:00
|
|
|
self.cursor.y = rect.bottom() + self.ui.spacing().item_spacing.y;
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn empty(&mut self, width: CellSize, height: CellSize) {
|
|
|
|
self.set_pos(self.cell_rect(&width, &height));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn add(
|
|
|
|
&mut self,
|
|
|
|
width: CellSize,
|
|
|
|
height: CellSize,
|
|
|
|
add_contents: impl FnOnce(&mut Ui),
|
|
|
|
) -> Response {
|
|
|
|
let rect = self.cell_rect(&width, &height);
|
2022-04-01 13:27:42 +00:00
|
|
|
let used_rect = self.cell(rect, add_contents);
|
2022-03-31 19:13:25 +00:00
|
|
|
self.set_pos(rect);
|
2022-04-01 10:01:00 +00:00
|
|
|
self.ui.allocate_rect(rect.union(used_rect), Sense::hover())
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn add_striped(
|
|
|
|
&mut self,
|
|
|
|
width: CellSize,
|
|
|
|
height: CellSize,
|
|
|
|
add_contents: impl FnOnce(&mut Ui),
|
|
|
|
) -> Response {
|
2022-04-01 12:43:43 +00:00
|
|
|
let rect = self.cell_rect(&width, &height);
|
|
|
|
|
|
|
|
// Make sure we don't have a gap in the stripe background:
|
2022-06-22 10:33:51 +00:00
|
|
|
let rect = rect.expand2(0.5 * self.ui.spacing().item_spacing);
|
2022-03-31 19:13:25 +00:00
|
|
|
|
|
|
|
self.ui
|
|
|
|
.painter()
|
|
|
|
.rect_filled(rect, 0.0, self.ui.visuals().faint_bg_color);
|
|
|
|
|
2022-04-01 13:27:42 +00:00
|
|
|
self.add(width, height, add_contents)
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
|
2022-04-03 16:18:35 +00:00
|
|
|
/// only needed for layouts with multiple lines, like [`Table`](crate::Table).
|
2022-03-31 19:13:25 +00:00
|
|
|
pub fn end_line(&mut self) {
|
|
|
|
match self.direction {
|
|
|
|
CellDirection::Horizontal => {
|
2022-04-11 08:19:07 +00:00
|
|
|
self.cursor.y = self.max.y + self.ui.spacing().item_spacing.y;
|
2022-04-01 12:43:43 +00:00
|
|
|
self.cursor.x = self.rect.left();
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
CellDirection::Vertical => {
|
2022-04-11 08:19:07 +00:00
|
|
|
self.cursor.x = self.max.x + self.ui.spacing().item_spacing.x;
|
2022-04-01 12:43:43 +00:00
|
|
|
self.cursor.y = self.rect.top();
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-11 15:54:57 +00:00
|
|
|
/// Skip a lot of space.
|
|
|
|
pub(crate) fn skip_space(&mut self, delta: egui::Vec2) {
|
|
|
|
let before = self.cursor;
|
|
|
|
self.cursor += delta;
|
|
|
|
let rect = Rect::from_two_pos(before, self.cursor);
|
|
|
|
self.ui.allocate_rect(rect, Sense::hover());
|
|
|
|
}
|
|
|
|
|
2022-04-01 13:27:42 +00:00
|
|
|
fn cell(&mut self, rect: Rect, add_contents: impl FnOnce(&mut Ui)) -> Rect {
|
2022-04-11 08:29:34 +00:00
|
|
|
let mut child_ui = self.ui.child_ui(rect, self.cell_layout);
|
2022-03-31 19:13:25 +00:00
|
|
|
|
2022-04-01 13:27:42 +00:00
|
|
|
if self.clip {
|
2022-04-11 08:55:52 +00:00
|
|
|
let margin = egui::Vec2::splat(self.ui.visuals().clip_rect_margin);
|
|
|
|
let margin = margin.min(0.5 * self.ui.spacing().item_spacing);
|
|
|
|
let clip_rect = rect.expand2(margin);
|
|
|
|
child_ui.set_clip_rect(clip_rect.intersect(child_ui.clip_rect()));
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
add_contents(&mut child_ui);
|
2022-04-01 10:01:00 +00:00
|
|
|
child_ui.min_rect()
|
2022-03-31 19:13:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Allocate the rect in [`Self::ui`] so that the scrollview knows about our size
|
|
|
|
pub fn allocate_rect(&mut self) -> Response {
|
|
|
|
let mut rect = self.rect;
|
|
|
|
rect.set_right(self.max.x);
|
|
|
|
rect.set_bottom(self.max.y);
|
|
|
|
|
|
|
|
self.ui.allocate_rect(rect, Sense::hover())
|
|
|
|
}
|
|
|
|
}
|