Add Widget and BoxedWidget

This commit is contained in:
Joscha 2024-03-03 23:33:42 +01:00
parent d6b9aea273
commit ba68e34d0d
4 changed files with 112 additions and 1 deletions

9
Cargo.lock generated
View file

@ -2,9 +2,18 @@
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 3
[[package]]
name = "anyhow"
version = "1.0.80"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1"
[[package]] [[package]]
name = "showbits-common" name = "showbits-common"
version = "0.0.0" version = "0.0.0"
dependencies = [
"anyhow",
]
[[package]] [[package]]
name = "showbits-thermal-printer" name = "showbits-thermal-printer"

View file

@ -3,5 +3,8 @@ name = "showbits-common"
version.workspace = true version.workspace = true
edition.workspace = true edition.workspace = true
[dependencies]
anyhow = "1.0.80"
[lints] [lints]
workspace = true workspace = true

View file

@ -1,6 +1,7 @@
pub use crate::{buffer::Buffer, rect::Rect, vec2::Vec2, view::View}; pub use crate::{buffer::*, rect::*, vec2::*, view::*, widget::*};
mod buffer; mod buffer;
mod rect; mod rect;
mod vec2; mod vec2;
mod view; mod view;
mod widget;

View file

@ -0,0 +1,98 @@
use crate::{Rect, Vec2, View};
pub trait Widget<C> {
/// Size that the widget wants to be, given the width and height
/// constraints.
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2;
/// Recalculate the size of all inner widgets given the widget's own size.
///
/// # Implement if...
///
/// - There are inner widgets
fn resize(&mut self, _area: Rect) {}
/// Perform any updates (e.g. fetching map tiles) that require the widget's
/// size and may fail.
///
/// # Implement if...
///
/// - There are inner widgets
/// - This widget needs to perform updates
fn update(&mut self, _area: Rect) -> anyhow::Result<()> {
Ok(())
}
fn draw(self, view: &mut View<'_, C>);
}
/// Wrapper trait around [`Widget`] that turns `Box<Self>` into a `Self` to get
/// around the "size cannot be statically determined" error with the naïve
/// approach of `Box<Widget>`.
trait WidgetWrapper<C> {
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2;
fn resize(&mut self, _area: Rect);
fn update(&mut self, _area: Rect) -> anyhow::Result<()>;
fn draw(self: Box<Self>, view: &mut View<'_, C>);
}
impl<C, W: Widget<C>> WidgetWrapper<C> for W {
// These implementations explicitly use `Widget::*` to call the widget
// methods even though they have priority over the `WidgetWrapper::*`
// methods for some reason. Just a bit of rustc magic, I guess.
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2 {
Widget::size(self, max_width, max_height)
}
fn resize(&mut self, area: Rect) {
Widget::resize(self, area);
}
fn update(&mut self, area: Rect) -> anyhow::Result<()> {
Widget::update(self, area)
}
fn draw(self: Box<Self>, view: &mut View<'_, C>) {
Widget::draw(*self, view);
}
}
pub struct BoxedWidget<C> {
area: Rect,
widget: Box<dyn WidgetWrapper<C>>,
}
impl<C> BoxedWidget<C> {
pub fn new<W>(widget: W) -> Self
where
W: Widget<C> + 'static,
{
Self {
area: Rect::ZERO,
widget: Box::new(widget),
}
}
pub fn area(&self) -> Rect {
self.area
}
}
impl<C> Widget<C> for BoxedWidget<C> {
fn size(&self, max_width: Option<i32>, max_height: Option<i32>) -> Vec2 {
self.widget.size(max_width, max_height)
}
fn resize(&mut self, area: Rect) {
self.widget.resize(area);
}
fn update(&mut self, area: Rect) -> anyhow::Result<()> {
self.widget.update(area)
}
fn draw(self, view: &mut View<'_, C>) {
self.widget.draw(view);
}
}