use std::{
cell::{Ref, RefCell, RefMut},
rc::Rc,
};
use anyhow::Result;
use fontdue::layout::{CoordinateSystem, Layout, LayoutSettings, TextStyle};
use serde::Deserialize;
use thiserror::Error;
use crate::{
root::Environment,
util::{fonts, Color},
widgets::Widget,
};
use super::{Style, WidgetData, WidgetError, WidgetList, WidgetNew, WidgetStyled};
#[derive(Deserialize, Debug, Clone, Default)]
pub struct TextSettings {
#[serde(default, flatten)]
pub default_data: WidgetData,
#[serde(default)]
pub text: String,
#[serde(default)]
pub font_color: Color,
#[serde(default)]
pub size: f32,
#[serde(default)]
pub fontid: usize,
#[serde(default)]
pub style: Style,
}
#[derive(Debug, Error)]
pub enum TextError {}
pub struct Text {
layout: Layout,
settings: TextSettings,
data: RefCell<WidgetData>,
env: Option<Rc<Environment>>,
is_ready: RefCell<bool>,
}
impl Text {
pub fn get_text(&self) -> String {
let mut text = String::new();
for glyph in self.layout.glyphs() {
text.push(glyph.parent);
}
text
}
pub fn change_text(&mut self, text: &str) {
self.layout.clear();
if let Some(ref mut _env) = self.env {
self.layout.append(
&fonts::fonts_vec(),
&TextStyle::new(text, self.settings.size, self.settings.fontid),
);
}
self.update_width();
self.data.borrow_mut().height = self.layout.height() as usize;
}
fn update_width(&self) {
let mut data = self.data.borrow_mut();
data.width = 0;
if let Some(lines) = self.layout.lines() {
for line in lines {
let glyph = self.layout.glyphs()[line.glyph_end];
let width = glyph.width + glyph.x.ceil() as usize;
data.width = usize::max(data.width, width);
}
}
}
}
impl Widget for Text {
fn name(&self) -> WidgetList {
WidgetList::Text
}
fn as_styled(&self) -> Option<&dyn WidgetStyled> {
Some(self)
}
fn bind(&mut self, env: Rc<Environment>) -> Result<(), WidgetError> {
self.env = Some(env);
let _env = self.env.as_mut().unwrap();
self.layout.append(
&fonts::fonts_vec(),
&TextStyle::new(
&self.settings.text,
self.settings.size,
self.settings.fontid,
),
);
Ok(())
}
fn env(&self) -> Option<Rc<Environment>> {
self.env.clone()
}
fn init(&self) -> Result<(), WidgetError> {
self.update_width();
self.data.borrow_mut().height = self.layout.height() as usize;
Ok(())
}
fn prepare(&self) -> Result<(), WidgetError> {
self.update_width();
self.apply_style()?;
*self.is_ready.borrow_mut() = true;
self.data.borrow_mut().height = self.layout.height() as usize;
Ok(())
}
fn draw(&self) -> Result<(), WidgetError> {
if self.env.is_none() {
return Err(WidgetError::DrawWithNoEnv(WidgetList::Text));
}
if !*self.is_ready.borrow() {
self.prepare()?;
}
*self.is_ready.borrow_mut() = false;
self.draw_style()?;
let font = &fonts::fonts_vec()[self.settings.fontid];
let data = &self.data.borrow_mut();
let mut drawer = self.env.as_ref().unwrap().drawer.borrow_mut();
for glyph in self.layout.glyphs() {
drawer.draw_glyph(data, glyph, font, self.settings.font_color);
}
Ok(())
}
fn data(&self) -> Ref<'_, WidgetData> {
self.data.borrow()
}
fn data_mut(&self) -> RefMut<'_, WidgetData> {
self.data.borrow_mut()
}
}
impl WidgetNew for Text {
type Settings = TextSettings;
fn new(env: Option<Rc<Environment>>, settings: Self::Settings) -> Result<Self, WidgetError>
where
Self: Sized,
{
let mut layout = Layout::new(CoordinateSystem::PositiveYDown);
layout.reset(&LayoutSettings {
max_width: match settings.default_data.width {
0 => None,
width => Some(width as f32),
},
..LayoutSettings::default()
});
let mut text = Text {
layout,
data: RefCell::new(settings.default_data),
settings,
env: None,
is_ready: RefCell::new(false),
};
if let Some(e) = env {
text.bind(e)?;
}
Ok(text)
}
}
impl WidgetStyled for Text {
fn style(&self) -> &Style {
&self.settings.style
}
}