diff --git a/all-is-cubes-ui/src/vui/widgets/text.rs b/all-is-cubes-ui/src/vui/widgets/text.rs index 080babcd4..fbcd86a7b 100644 --- a/all-is-cubes-ui/src/vui/widgets/text.rs +++ b/all-is-cubes-ui/src/vui/widgets/text.rs @@ -1,17 +1,18 @@ use alloc::sync::Arc; use all_is_cubes::arcstr::ArcStr; +use all_is_cubes::block::text::{self, Text as BlockText}; use all_is_cubes::drawing::embedded_graphics::{ mono_font::{MonoFont, MonoTextStyle}, prelude::{Dimensions, Point}, - text::{Text, TextStyle}, + text::{Text as EgText, TextStyle}, Drawable, }; use all_is_cubes::drawing::{rectangle_to_aab, VoxelBrush}; use all_is_cubes::math::{GridAab, Gridgid}; use all_is_cubes::space::SpaceTransaction; -use crate::vui::{widgets, LayoutGrant, LayoutRequest, Layoutable, Widget, WidgetController}; +use crate::vui::{self, widgets, LayoutGrant, LayoutRequest, Layoutable, Widget, WidgetController}; /// Widget which draws text using a block per font pixel. /// @@ -33,8 +34,8 @@ pub struct LargeText { } impl LargeText { - fn drawable(&self) -> Text<'_, MonoTextStyle<'_, &VoxelBrush<'_>>> { - Text::with_text_style( + fn drawable(&self) -> EgText<'_, MonoTextStyle<'_, &VoxelBrush<'_>>> { + EgText::with_text_style( &self.text, Point::new(0, 0), MonoTextStyle::new((self.font)(), &self.brush), @@ -83,15 +84,90 @@ impl Widget for LargeText { } } +/// Widget which draws a static [`Text`](BlockText) for use as a text label in UI. +/// +/// This cannot be used for dynamic text. +#[derive(Clone, Debug)] +pub struct Label { + text: ArcStr, + font: text::Font, +} + +impl Label { + /// Constructs a [`Label`] that draws the given text. + pub fn new(text: ArcStr) -> Self { + Self { + text, + font: text::Font::System16, + } + } +} + +impl Layoutable for Label { + fn requirements(&self) -> LayoutRequest { + // TODO: memoize + LayoutRequest { + minimum: text_for_widget( + self.text.clone(), + self.font.clone(), + vui::Gravity::splat(vui::Align::Center), + ) + .bounding_blocks() + .size(), + } + } +} + +impl Widget for Label { + fn controller(self: Arc, grant: &LayoutGrant) -> Box { + // TODO: memoize `Text` construction for slightly more efficient reuse of widget + // (this will only matter once `Text` memoizes glyph layout) + + widgets::OneshotController::new(draw_text_txn( + &text_for_widget(self.text.clone(), self.font.clone(), grant.gravity), + grant, + )) + } +} + +fn text_for_widget(text: ArcStr, font: text::Font, gravity: vui::Gravity) -> text::Text { + text::Text::new( + text, + font, + text::Positioning { + x: match gravity.x { + vui::Align::Low => text::PositioningX::Left, + vui::Align::Center => text::PositioningX::Center, + vui::Align::High => text::PositioningX::Right, + }, + // TODO: need to be able to set the anchor to produce middle-of-block + line_y: text::PositioningY::BodyBottom, + z: 0, + }, + ) +} + +fn draw_text_txn(text: &BlockText, grant: &LayoutGrant) -> SpaceTransaction { + let text_aabb = text.bounding_blocks(); + let grant = grant.shrink_to(text_aabb.size(), true); + text.installation( + Gridgid::from_translation(grant.bounds.lower_bounds() - text_aabb.lower_bounds()), + core::convert::identity, + ) +} + #[cfg(test)] mod tests { use super::*; + use crate::vui; + use all_is_cubes::arcstr::literal; use all_is_cubes::block::Block; use all_is_cubes::drawing::embedded_graphics::mono_font::iso_8859_1::FONT_9X15_BOLD; use all_is_cubes::math::{GridVector, Rgba}; + use all_is_cubes::space::{SpaceBuilder, SpacePhysics}; #[test] - fn text_size() { + fn large_text_size() { let text = "abc"; let widget = LargeText { text: text.into(), @@ -106,4 +182,16 @@ mod tests { } ); } + + #[test] + fn label_layout() { + let tree: vui::WidgetTree = vui::LayoutTree::leaf(Arc::new(Label::new(literal!("hi")))); + + // to_space() serves as a transaction sanity check. TODO: make a proper tester + tree.to_space( + SpaceBuilder::default().physics(SpacePhysics::DEFAULT_FOR_BLOCK), + vui::Gravity::new(vui::Align::Center, vui::Align::Center, vui::Align::Low), + ) + .unwrap(); + } }