use embedded_graphics::{
Drawable,
draw_target::DrawTarget,
geometry::Point,
prelude::{PixelColor, Primitive},
primitives::{PrimitiveStyle, Rectangle},
text::{Alignment, Baseline, renderer::TextRenderer},
transform::Transform,
};
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[cfg_attr(feature = "defmt", derive(::defmt::Format))]
pub struct StaticText<'a, S> {
pub text: &'a str,
pub rectangle: Rectangle,
pub character_style: S,
pub alignment: Alignment,
pub baseline: Baseline,
}
impl<'a, S> StaticText<'a, S> {
pub const fn new(text: &'a str, rectangle: Rectangle, character_style: S) -> Self {
Self {
text,
rectangle,
character_style,
alignment: Alignment::Left,
baseline: Baseline::Alphabetic,
}
}
pub const fn with_style(
text: &'a str,
rectangle: Rectangle,
character_style: S,
alignment: Alignment,
baseline: Baseline,
) -> Self {
Self {
text,
rectangle,
character_style,
alignment,
baseline,
}
}
pub fn fill_with_style<C, D>(
&self,
style: PrimitiveStyle<C>,
target: &mut D,
) -> Result<Point, D::Error>
where
C: PixelColor,
D: DrawTarget<Color = C>,
{
let result = self.rectangle.into_styled(style).draw(target);
result.map(|_| {
Point::new(
self.rectangle.top_left.x + self.rectangle.size.width as i32,
self.rectangle.top_left.y,
)
})
}
}
impl<S: Clone> Transform for StaticText<'_, S> {
fn translate(&self, by: Point) -> Self {
Self {
rectangle: Rectangle::new(self.rectangle.top_left + by, self.rectangle.size),
..self.clone()
}
}
fn translate_mut(&mut self, by: Point) -> &mut Self {
self.rectangle.top_left += by;
self
}
}
impl<S: TextRenderer> StaticText<'_, S> {
fn lines(&self) -> impl Iterator<Item = (&str, Point)> {
let line_feed = self.text.matches('\n').count() as i32;
let offset_y = self.character_style.line_height() as i32 * line_feed;
let mut position = self.rectangle.top_left;
let height = self.rectangle.size.height as i32;
match self.baseline {
Baseline::Top => {}
Baseline::Bottom | Baseline::Alphabetic => position.y += height - 1 - offset_y,
Baseline::Middle => position.y += (height - 1 - offset_y) / 2,
}
self.text.split('\n').map(move |line| {
let p = match self.alignment {
Alignment::Left => position,
Alignment::Right => {
let metrics =
self.character_style
.measure_string(line, Point::zero(), self.baseline);
position + Point::new(self.rectangle.size.width as i32, 0)
- (metrics.next_position - Point::new(1, 0))
}
Alignment::Center => {
let metrics =
self.character_style
.measure_string(line, Point::zero(), self.baseline);
position + Point::new(self.rectangle.size.width as i32 / 2, 0)
- metrics.next_position / 2
}
};
position.y += self.character_style.line_height() as i32;
let len = line.len();
if len > 0 && line.as_bytes()[len - 1] == b'\r' {
(&line[0..len - 1], p)
} else {
(line, p)
}
})
}
pub fn clear<D>(&self, target: &mut D) -> Result<Point, D::Error>
where
D: DrawTarget<Color = S::Color>,
{
self.character_style.draw_whitespace(
self.rectangle.size.width,
self.rectangle.top_left,
self.baseline,
target,
)
}
pub fn clear_by_color<C, D>(&self, color: C, target: &mut D) -> Result<Point, D::Error>
where
C: PixelColor,
D: DrawTarget<Color = C>,
{
target.fill_solid(&self.rectangle, color).map(|_| {
Point::new(
self.rectangle.top_left.x + self.rectangle.size.width as i32,
self.rectangle.top_left.y,
)
})
}
}
impl<S: TextRenderer> Drawable for StaticText<'_, S> {
type Color = S::Color;
type Output = Point;
fn draw<D>(&self, target: &mut D) -> Result<Point, D::Error>
where
D: DrawTarget<Color = Self::Color>,
{
let mut next_position = Point::zero();
let size = &self.rectangle.size;
let left_x = self.rectangle.top_left.x;
let right_x = left_x + size.width as i32;
for (line, position) in self.lines() {
if position.x > left_x {
self.character_style.draw_whitespace(
(position.x - left_x) as u32,
Point::new(left_x, position.y),
self.baseline,
target,
)?;
}
next_position =
self.character_style
.draw_string(line, position, self.baseline, target)?;
if next_position.x < right_x {
self.character_style.draw_whitespace(
(right_x - next_position.x) as u32,
next_position,
self.baseline,
target,
)?;
next_position.x = right_x
}
}
Ok(next_position)
}
}