use retroglyph_core::grid::Rect;
use retroglyph_core::surface::Surface;
use crate::interact::{Interaction, Response, Sense};
use crate::widget::{InteractiveWidget, Measure, StatefulWidget, Widget};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum Axis {
Vertical,
Horizontal,
}
struct Cursor {
axis: Axis,
remaining: Rect,
}
impl Cursor {
fn allocate(&mut self, primary: u16) -> Rect {
match self.axis {
Axis::Vertical => {
let h = primary.min(self.remaining.height());
let rect = Rect::new(
self.remaining.left(),
self.remaining.top(),
self.remaining.width(),
h,
);
self.remaining = Rect::new(
self.remaining.left(),
self.remaining.top().saturating_add(h),
self.remaining.width(),
self.remaining.height() - h,
);
rect
}
Axis::Horizontal => {
let w = primary.min(self.remaining.width());
let rect = Rect::new(
self.remaining.left(),
self.remaining.top(),
w,
self.remaining.height(),
);
self.remaining = Rect::new(
self.remaining.left().saturating_add(w),
self.remaining.top(),
self.remaining.width() - w,
self.remaining.height(),
);
rect
}
}
}
fn allocate_rest(&mut self) -> Rect {
let primary = match self.axis {
Axis::Vertical => self.remaining.height(),
Axis::Horizontal => self.remaining.width(),
};
self.allocate(primary)
}
}
pub struct Ui<'s, 'g, Id> {
surface: &'s mut Surface<'g>,
interaction: &'s mut Interaction<Id>,
enabled: bool,
cursor: Option<&'s mut Cursor>,
}
impl<'s, 'g, Id> Ui<'s, 'g, Id> {
#[must_use]
pub const fn new(surface: &'s mut Surface<'g>, interaction: &'s mut Interaction<Id>) -> Self {
Self {
surface,
interaction,
enabled: true,
cursor: None,
}
}
#[must_use]
pub const fn is_enabled(&self) -> bool {
self.enabled
}
#[must_use]
pub const fn surface(&mut self) -> &mut Surface<'g> {
self.surface
}
#[must_use]
pub const fn interaction(&mut self) -> &mut Interaction<Id> {
self.interaction
}
#[must_use]
pub const fn area(&self) -> Rect {
self.surface.area()
}
}
impl<'g, Id: Copy + PartialEq> Ui<'_, 'g, Id> {
#[must_use]
pub const fn enabled(&mut self, enabled: bool) -> Ui<'_, 'g, Id> {
Ui {
surface: self.surface,
interaction: self.interaction,
enabled: self.enabled && enabled,
cursor: None,
}
}
#[must_use]
pub fn show(
&mut self,
area: Rect,
id: Id,
widget: &impl InteractiveWidget<Id, State = ()>,
) -> Response<Id> {
self.show_stateful(area, id, widget, &mut ())
}
#[must_use]
pub fn show_stateful<W: InteractiveWidget<Id> + ?Sized>(
&mut self,
area: Rect,
id: Id,
widget: &W,
state: &mut W::State,
) -> Response<Id> {
let sense = widget.sense().disabled_if(!self.enabled);
let response = self.interaction.interact(area, id, sense);
widget.render(&mut self.surface.scope(area), state, response);
response
}
pub fn draw(&mut self, area: Rect, widget: &impl Widget) {
widget.render(&mut self.surface.scope(area));
}
pub fn draw_stateful<W: StatefulWidget + ?Sized>(
&mut self,
area: Rect,
widget: &W,
state: &mut W::State,
) {
widget.render(&mut self.surface.scope(area), state);
}
#[must_use]
pub fn region(&mut self, area: Rect, id: Id, sense: Sense) -> (Response<Id>, Surface<'_>) {
let sense = sense.disabled_if(!self.enabled);
let response = self.interaction.interact(area, id, sense);
(response, self.surface.scope(area))
}
pub fn modal<R>(&mut self, area: Rect, f: impl FnOnce(&mut Ui<'_, 'g, Id>) -> R) -> R {
self.interaction.push_barrier(area);
let mut inner = Ui {
surface: self.surface,
interaction: self.interaction,
enabled: self.enabled,
cursor: None,
};
f(&mut inner)
}
fn allocate(&mut self, primary: u16) -> Rect {
self.cursor
.as_deref_mut()
.expect(
"Ui::show_sized/draw_sized/vertical_sized/horizontal_sized require an active \
cursor: call them on the `Ui` that `vertical`/`horizontal` hands to your \
closure, not on a `Ui` fresh from `new`/`enabled`/`modal`",
)
.allocate(primary)
}
fn allocate_rest(&mut self) -> Rect {
self.cursor
.as_deref_mut()
.map_or_else(|| self.surface.area(), Cursor::allocate_rest)
}
fn vertical_cursor_width(&self) -> u16 {
match &self.cursor {
Some(cursor) if cursor.axis == Axis::Vertical => cursor.remaining.width(),
Some(_) => panic!(
"Ui::show_auto/draw_auto require a vertical cursor: `Measure::height_for` takes \
a width, so a horizontal cursor has nothing to size a column by"
),
None => panic!(
"Ui::show_auto/draw_auto require an active cursor: call them on the `Ui` that \
`vertical`/`horizontal` hands to your closure, not on a `Ui` fresh from \
`new`/`enabled`/`modal`"
),
}
}
pub fn vertical<R>(&mut self, f: impl FnOnce(&mut Ui<'_, 'g, Id>) -> R) -> R {
let area = self.allocate_rest();
let mut cursor = Cursor {
axis: Axis::Vertical,
remaining: area,
};
let mut inner = Ui {
surface: self.surface,
interaction: self.interaction,
enabled: self.enabled,
cursor: Some(&mut cursor),
};
f(&mut inner)
}
pub fn horizontal<R>(&mut self, f: impl FnOnce(&mut Ui<'_, 'g, Id>) -> R) -> R {
let area = self.allocate_rest();
let mut cursor = Cursor {
axis: Axis::Horizontal,
remaining: area,
};
let mut inner = Ui {
surface: self.surface,
interaction: self.interaction,
enabled: self.enabled,
cursor: Some(&mut cursor),
};
f(&mut inner)
}
pub fn vertical_sized<R>(
&mut self,
height: u16,
f: impl FnOnce(&mut Ui<'_, 'g, Id>) -> R,
) -> R {
let area = self.allocate(height);
let mut cursor = Cursor {
axis: Axis::Vertical,
remaining: area,
};
let mut inner = Ui {
surface: self.surface,
interaction: self.interaction,
enabled: self.enabled,
cursor: Some(&mut cursor),
};
f(&mut inner)
}
pub fn horizontal_sized<R>(
&mut self,
width: u16,
f: impl FnOnce(&mut Ui<'_, 'g, Id>) -> R,
) -> R {
let area = self.allocate(width);
let mut cursor = Cursor {
axis: Axis::Horizontal,
remaining: area,
};
let mut inner = Ui {
surface: self.surface,
interaction: self.interaction,
enabled: self.enabled,
cursor: Some(&mut cursor),
};
f(&mut inner)
}
#[must_use]
pub fn show_sized(
&mut self,
id: Id,
widget: &impl InteractiveWidget<Id, State = ()>,
size: u16,
) -> Response<Id> {
let area = self.allocate(size);
self.show(area, id, widget)
}
pub fn draw_sized(&mut self, widget: &impl Widget, size: u16) {
let area = self.allocate(size);
self.draw(area, widget);
}
#[must_use]
pub fn show_auto(
&mut self,
id: Id,
widget: &(impl InteractiveWidget<Id, State = ()> + Measure),
) -> Response<Id> {
let height = widget.height_for(self.vertical_cursor_width());
self.show_sized(id, widget, height)
}
pub fn draw_auto(&mut self, widget: &(impl Widget + Measure)) {
let height = widget.height_for(self.vertical_cursor_width());
self.draw_sized(widget, height);
}
}
#[cfg(test)]
mod tests {
use alloc::string::String;
use retroglyph_core::color::Style;
use retroglyph_core::event::{Event, KeyModifiers, MouseEvent, MouseEventKind};
use retroglyph_core::grid::{Grid, Pos};
use super::*;
use crate::widget::Widget;
fn move_pointer(interaction: &mut Interaction<Id>, pos: Pos) {
let _ = interaction.handle_event(&Event::Mouse(MouseEvent::new(
MouseEventKind::Moved,
pos,
KeyModifiers::NONE,
)));
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Id {
Button,
Behind,
InModal,
}
struct Dot;
impl<Id> InteractiveWidget<Id> for Dot {
type State = ();
fn sense(&self) -> Sense {
Sense::click()
}
fn render(
&self,
surface: &mut Surface<'_>,
_state: &mut Self::State,
response: Response<Id>,
) {
let glyph = if response.hovered() { '*' } else { '.' };
surface.put((0, 0), glyph, Style::new());
}
}
struct Fill(char);
impl Widget for Fill {
fn render(&self, surface: &mut Surface<'_>) {
let area = surface.area();
for y in 0..area.height() {
for x in 0..area.width() {
surface.put((x, y), self.0, Style::new());
}
}
}
}
#[test]
fn show_registers_the_area_it_draws_into() {
let mut grid = Grid::new(10, 10);
let mut interaction = Interaction::<Id>::new();
let area = Rect::new(2, 2, 3, 3);
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| {
let _ = ui.show(area, Id::Button, &Dot);
},
);
move_pointer(&mut interaction, Pos::new(3, 3));
let response = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| ui.show(area, Id::Button, &Dot),
);
assert!(response.hovered());
move_pointer(&mut interaction, Pos::new(8, 8));
let response = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| ui.show(area, Id::Button, &Dot),
);
assert!(!response.hovered());
}
#[test]
fn region_registers_the_area_it_scopes_the_surface_to() {
let mut grid = Grid::new(10, 10);
let mut interaction = Interaction::<Id>::new();
let area = Rect::new(2, 2, 3, 3);
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| {
let (_response, mut surface) = ui.region(area, Id::Button, Sense::hover());
assert_eq!(surface.area(), area);
surface.put((0, 0), 'x', Style::new());
},
);
assert_eq!(grid[Pos::new(2, 2)].glyph(), 'x');
move_pointer(&mut interaction, Pos::new(3, 3));
let response = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| ui.region(area, Id::Button, Sense::hover()).0,
);
assert!(response.hovered());
}
#[test]
fn frame_calls_begin_and_end_exactly_once() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
let area = Rect::new(0, 0, 4, 4);
interaction.frame(&mut Surface::new(&mut grid, area, 0), |ui| {
let _ = ui.show(area, Id::Button, &Dot);
});
let _ = interaction.handle_event(&Event::Mouse(MouseEvent::new(
MouseEventKind::Down(retroglyph_core::event::MouseButton::Left),
Pos::new(1, 1),
KeyModifiers::NONE,
)));
let _ = interaction.handle_event(&Event::Mouse(MouseEvent::new(
MouseEventKind::Up(retroglyph_core::event::MouseButton::Left),
Pos::new(1, 1),
KeyModifiers::NONE,
)));
let clicked = interaction.frame(&mut Surface::new(&mut grid, area, 0), |ui| {
ui.show(area, Id::Button, &Dot).clicked()
});
assert!(clicked);
}
#[test]
fn enabled_nesting_only_tightens() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
let area = Rect::new(0, 0, 4, 4);
move_pointer(&mut interaction, Pos::new(0, 0));
interaction.frame(&mut Surface::new(&mut grid, area, 0), |ui| {
let mut disabled = ui.enabled(false);
let mut re_enabled = disabled.enabled(true);
assert!(!re_enabled.is_enabled());
let _ = re_enabled.show(area, Id::Button, &Dot);
});
let _ = interaction.handle_event(&Event::Mouse(MouseEvent::new(
MouseEventKind::Down(retroglyph_core::event::MouseButton::Left),
Pos::new(0, 0),
KeyModifiers::NONE,
)));
let _ = interaction.handle_event(&Event::Mouse(MouseEvent::new(
MouseEventKind::Up(retroglyph_core::event::MouseButton::Left),
Pos::new(0, 0),
KeyModifiers::NONE,
)));
let response = interaction.frame(&mut Surface::new(&mut grid, area, 0), |ui| {
let mut disabled = ui.enabled(false);
let mut re_enabled = disabled.enabled(true);
re_enabled.show(area, Id::Button, &Dot)
});
assert!(response.disabled());
assert!(!response.clicked());
assert!(response.hovered());
}
#[test]
fn surface_is_usable_after_frame_returns() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
let mut surface = Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0);
interaction.frame(&mut surface, |ui| {
ui.draw(Rect::new(0, 0, 4, 4), &Fill('.'));
});
surface.put((0, 0), 'x', Style::new());
assert_eq!(grid[Pos::new(0, 0)].glyph(), 'x');
}
#[test]
fn modal_wins_a_hit_over_an_earlier_widget_at_the_same_position() {
let mut grid = Grid::new(10, 10);
let mut interaction = Interaction::<Id>::new();
let area = Rect::new(2, 2, 3, 3);
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| {
let _ = ui.show(area, Id::Behind, &Dot);
ui.modal(area, |ui| {
let _ = ui.show(area, Id::InModal, &Dot);
});
},
);
move_pointer(&mut interaction, Pos::new(3, 3));
let (behind, in_modal) = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| {
let behind = ui.show(area, Id::Behind, &Dot);
let in_modal = ui.modal(area, |ui| ui.show(area, Id::InModal, &Dot));
(behind, in_modal)
},
);
assert!(!behind.hovered()); assert!(in_modal.hovered());
}
#[test]
fn a_widget_outside_the_modal_area_still_wins_hits_at_its_own_position() {
let mut grid = Grid::new(10, 10);
let mut interaction = Interaction::<Id>::new();
let modal_area = Rect::new(2, 2, 3, 3);
let outside_area = Rect::new(7, 7, 2, 2);
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| {
let _ = ui.show(outside_area, Id::Behind, &Dot);
ui.modal(modal_area, |ui| {
let _ = ui.show(modal_area, Id::InModal, &Dot);
});
},
);
move_pointer(&mut interaction, Pos::new(7, 7));
let behind = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 10, 10), 0),
|ui| {
let behind = ui.show(outside_area, Id::Behind, &Dot);
let _ = ui.modal(modal_area, |ui| ui.show(modal_area, Id::InModal, &Dot));
behind
},
);
assert!(behind.hovered()); }
struct FixedHeight(u16);
impl Widget for FixedHeight {
fn render(&self, surface: &mut Surface<'_>) {
let area = surface.area();
for y in 0..area.height() {
for x in 0..area.width() {
surface.put((x, y), '#', Style::new());
}
}
}
}
impl<Id> InteractiveWidget<Id> for FixedHeight {
type State = ();
fn sense(&self) -> Sense {
Sense::click()
}
fn render(
&self,
surface: &mut Surface<'_>,
_state: &mut Self::State,
_response: Response<Id>,
) {
Widget::render(self, surface);
}
}
impl Measure for FixedHeight {
fn height_for(&self, _width: u16) -> u16 {
self.0
}
}
fn glyphs_in_row(grid: &Grid, y: u16, width: u16) -> String {
(0..width).map(|x| grid[Pos::new(x, y)].glyph()).collect()
}
#[test]
fn vertical_stacks_draw_sized_rows_top_to_bottom() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.vertical(|ui| {
ui.draw_sized(&Fill('a'), 1);
ui.draw_sized(&Fill('b'), 1);
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "aaaa");
assert_eq!(glyphs_in_row(&grid, 1, 4), "bbbb");
assert_eq!(glyphs_in_row(&grid, 2, 4), " "); }
#[test]
fn horizontal_stacks_draw_sized_columns_left_to_right() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.horizontal(|ui| {
ui.draw_sized(&Fill('a'), 1);
ui.draw_sized(&Fill('b'), 1);
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "ab ");
assert_eq!(glyphs_in_row(&grid, 3, 4), "ab ");
}
#[test]
fn horizontal_sized_claims_only_its_own_width_from_the_outer_cursor() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.horizontal(|ui| {
ui.horizontal_sized(2, |ui| {
ui.draw_sized(&Fill('a'), 2);
});
ui.draw_sized(&Fill('b'), 1);
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "aab ");
}
#[test]
fn vertical_clips_when_rows_overflow_the_cursor_area() {
let mut grid = Grid::new(4, 2);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 2), 0),
|ui| {
ui.vertical(|ui| {
ui.draw_sized(&Fill('a'), 1);
ui.draw_sized(&Fill('b'), 1);
ui.draw_sized(&Fill('c'), 1);
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "aaaa");
assert_eq!(glyphs_in_row(&grid, 1, 4), "bbbb");
}
#[test]
fn show_sized_registers_the_area_it_allocates() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.vertical(|ui| {
let _ = ui.show_sized(Id::Button, &Dot, 1);
});
},
);
move_pointer(&mut interaction, Pos::new(1, 0));
let response = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| ui.vertical(|ui| ui.show_sized(Id::Button, &Dot, 1)),
);
assert!(response.hovered());
move_pointer(&mut interaction, Pos::new(1, 2));
let response = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| ui.vertical(|ui| ui.show_sized(Id::Button, &Dot, 1)),
);
assert!(!response.hovered());
}
#[test]
fn draw_auto_sizes_by_measure_height_for() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.vertical(|ui| {
ui.draw_auto(&FixedHeight(2));
ui.draw_sized(&Fill('x'), 1); });
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "####");
assert_eq!(glyphs_in_row(&grid, 1, 4), "####");
assert_eq!(glyphs_in_row(&grid, 2, 4), "xxxx");
}
#[test]
fn show_auto_sizes_by_measure_and_registers_the_area_it_allocates() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.vertical(|ui| {
let _ = ui.show_auto(Id::Button, &FixedHeight(2));
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "####");
assert_eq!(glyphs_in_row(&grid, 1, 4), "####");
assert_eq!(glyphs_in_row(&grid, 2, 4), " ");
move_pointer(&mut interaction, Pos::new(1, 1));
let response = interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| ui.vertical(|ui| ui.show_auto(Id::Button, &FixedHeight(2))),
);
assert!(response.hovered());
}
#[test]
#[should_panic(expected = "require an active cursor")]
fn draw_auto_without_an_active_cursor_panics() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.draw_auto(&FixedHeight(1));
},
);
}
#[test]
fn horizontal_nested_in_vertical_is_scoped_to_the_outer_cursors_remaining_area() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.vertical(|ui| {
ui.draw_sized(&Fill('t'), 1); ui.vertical_sized(1, |ui| {
ui.horizontal(|ui| {
ui.draw_sized(&Fill('a'), 1);
ui.draw_sized(&Fill('b'), 1);
});
});
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "tttt");
assert_eq!(glyphs_in_row(&grid, 1, 4), "ab ");
assert_eq!(glyphs_in_row(&grid, 2, 4), " "); }
#[test]
fn vertical_nested_in_horizontal_claims_the_outer_cursors_remaining_width() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.horizontal(|ui| {
ui.draw_sized(&Fill('l'), 1); ui.vertical(|ui| {
ui.draw_sized(&Fill('a'), 1);
ui.draw_sized(&Fill('b'), 1);
});
});
},
);
assert_eq!(glyphs_in_row(&grid, 0, 4), "laaa");
assert_eq!(glyphs_in_row(&grid, 1, 4), "lbbb");
}
#[test]
#[should_panic(expected = "require an active cursor")]
fn show_sized_without_an_active_cursor_panics() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
let _ = ui.show_sized(Id::Button, &Dot, 1);
},
);
}
#[test]
#[should_panic(expected = "require an active cursor")]
fn enabled_child_of_a_cursor_ui_has_no_active_cursor() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.vertical(|ui| {
let _ = ui.enabled(true).show_sized(Id::Button, &Dot, 1);
});
},
);
}
#[test]
#[should_panic(expected = "require a vertical cursor")]
fn draw_auto_on_a_horizontal_cursor_panics() {
let mut grid = Grid::new(4, 4);
let mut interaction = Interaction::<Id>::new();
interaction.frame(
&mut Surface::new(&mut grid, Rect::new(0, 0, 4, 4), 0),
|ui| {
ui.horizontal(|ui| {
ui.draw_auto(&FixedHeight(1));
});
},
);
}
}