From ba68e34d0d61936404d27a61add4c0f19a0ea0c9 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sun, 3 Mar 2024 23:33:42 +0100 Subject: [PATCH] Add Widget and BoxedWidget --- Cargo.lock | 9 ++++ showbits-common/Cargo.toml | 3 ++ showbits-common/src/lib.rs | 3 +- showbits-common/src/widget.rs | 98 +++++++++++++++++++++++++++++++++++ 4 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 showbits-common/src/widget.rs diff --git a/Cargo.lock b/Cargo.lock index 44847ac..3c7da6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,9 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + [[package]] name = "showbits-common" version = "0.0.0" +dependencies = [ + "anyhow", +] [[package]] name = "showbits-thermal-printer" diff --git a/showbits-common/Cargo.toml b/showbits-common/Cargo.toml index ec7ce3b..ecc8221 100644 --- a/showbits-common/Cargo.toml +++ b/showbits-common/Cargo.toml @@ -3,5 +3,8 @@ name = "showbits-common" version.workspace = true edition.workspace = true +[dependencies] +anyhow = "1.0.80" + [lints] workspace = true diff --git a/showbits-common/src/lib.rs b/showbits-common/src/lib.rs index 605350f..4b1f0b5 100644 --- a/showbits-common/src/lib.rs +++ b/showbits-common/src/lib.rs @@ -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 rect; mod vec2; mod view; +mod widget; diff --git a/showbits-common/src/widget.rs b/showbits-common/src/widget.rs new file mode 100644 index 0000000..942754b --- /dev/null +++ b/showbits-common/src/widget.rs @@ -0,0 +1,98 @@ +use crate::{Rect, Vec2, View}; + +pub trait Widget { + /// Size that the widget wants to be, given the width and height + /// constraints. + fn size(&self, max_width: Option, max_height: Option) -> 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` into a `Self` to get +/// around the "size cannot be statically determined" error with the naïve +/// approach of `Box`. +trait WidgetWrapper { + fn size(&self, max_width: Option, max_height: Option) -> Vec2; + fn resize(&mut self, _area: Rect); + fn update(&mut self, _area: Rect) -> anyhow::Result<()>; + fn draw(self: Box, view: &mut View<'_, C>); +} + +impl> WidgetWrapper 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, max_height: Option) -> 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, view: &mut View<'_, C>) { + Widget::draw(*self, view); + } +} + +pub struct BoxedWidget { + area: Rect, + widget: Box>, +} + +impl BoxedWidget { + pub fn new(widget: W) -> Self + where + W: Widget + 'static, + { + Self { + area: Rect::ZERO, + widget: Box::new(widget), + } + } + + pub fn area(&self) -> Rect { + self.area + } +} + +impl Widget for BoxedWidget { + fn size(&self, max_width: Option, max_height: Option) -> 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); + } +}