use embedded_graphics::{
mono_font::{MonoFont, MonoTextStyle, ascii::FONT_10X20},
pixelcolor::{BinaryColor, Rgb565},
prelude::{Dimensions as _, PixelColor, Point, Size},
text::{Baseline, Text as GraphicsText},
};
use crate::{
Context, Style,
common::NodeIndex,
element::{BuildError, ElementBuilder},
layout::Layout,
style::StyledElement,
tree::{Node, NodeKind, TextNode},
};
#[derive(PartialEq, Eq)]
pub struct TextStyle<C = Rgb565> {
pub(crate) font: Font,
pub(crate) color: Option<C>,
}
impl<C> Default for TextStyle<C> {
fn default() -> Self {
Self { font: Font::mono(&FONT_10X20), color: None }
}
}
impl<'frame, C, CE> Context<'frame, C, CE>
where
C: PixelColor,
{
pub fn text<'cx, 't>(&'cx self, content: &'t str) -> TextBuilder<'cx, 'frame, 't, C, CE> {
TextBuilder::new(self, content)
}
}
pub struct TextBuilder<'cx, 'frame, 't, C, CE>
where
C: PixelColor,
{
content: &'t str,
pub(crate) style: Style<TextStyle<C>, C>,
cx: &'cx Context<'frame, C, CE>,
}
impl<'cx, 'frame, 't, C, CE> TextBuilder<'cx, 'frame, 't, C, CE>
where
C: PixelColor,
{
pub fn new(cx: &'cx Context<'frame, C, CE>, content: &'t str) -> Self {
Self { content, style: Style::default(), cx }
}
pub fn measure(&self) -> Size {
match self.style.font {
Font::Mono(font) => {
let character_style = MonoTextStyle::new(font, BinaryColor::On);
let bounds = GraphicsText::with_baseline(
self.content,
Point::new(0, 0),
character_style,
Baseline::Top,
)
.bounding_box();
bounds.size
}
}
}
}
impl<C, CE> StyledElement for TextBuilder<'_, '_, '_, C, CE>
where
C: PixelColor,
{
type Color = C;
type Specific = TextStyle<C>;
fn style(&self) -> &Style<Self::Specific, Self::Color> {
&self.style
}
fn style_mut(&mut self) -> &mut Style<Self::Specific, Self::Color> {
&mut self.style
}
}
impl<C, CE> ElementBuilder for TextBuilder<'_, '_, '_, C, CE>
where
C: PixelColor,
{
fn try_build(self) -> Result<NodeIndex, BuildError> {
let range = self.cx.store_text(self.content)?;
let size = self.measure();
self.cx.insert(Node {
kind: NodeKind::Text(TextNode { size, range, style: self.style }),
layout: Layout::empty(),
child: None,
sibling: None,
})
}
}
#[derive(PartialEq)]
pub enum Font {
Mono(&'static MonoFont<'static>),
}
impl Font {
#[inline]
pub const fn mono(font: &'static MonoFont<'static>) -> Self {
Self::Mono(font)
}
}
impl Eq for Font {}
impl Default for Font {
fn default() -> Self {
Self::mono(&FONT_10X20)
}
}
pub trait TextStyledElement<C>: StyledElement<Color = C, Specific = TextStyle<C>>
where
C: PixelColor,
{
fn font(mut self, font: Font) -> Self {
self.style_mut().specific.font = font;
self
}
fn text_color(mut self, color: C) -> Self {
self.style_mut().specific.color = Some(color);
self
}
}
impl<T, C> TextStyledElement<C> for T
where
T: StyledElement<Color = C, Specific = TextStyle<C>>,
C: PixelColor,
{
}