use crate::kurbo::{Point, Rect, Size};
use crate::piet::{
FontBuilder, PietText, PietTextLayout, RenderContext, Text, TextLayout, TextLayoutBuilder,
UnitPoint,
};
use crate::theme;
use crate::{
BoxConstraints, Data, Env, Event, EventCtx, LayoutCtx, LocalizedString, PaintCtx, UpdateCtx,
Widget,
};
pub enum LabelText<T> {
Localized(LocalizedString<T>),
Specific(String),
Dynamic(Box<dyn Fn(&T, &Env) -> String>),
}
pub struct Label<T> {
text: LabelText<T>,
align: UnitPoint,
}
impl<T: Data> Label<T> {
pub fn new(text: impl Into<LabelText<T>>) -> Self {
let text = text.into();
Self {
text,
align: UnitPoint::LEFT,
}
}
pub fn align(mut self, align: UnitPoint) -> Self {
self.align = align;
self
}
fn get_layout(&mut self, t: &mut PietText, env: &Env, data: &T) -> PietTextLayout {
let font_name = env.get(theme::FONT_NAME);
let font_size = env.get(theme::TEXT_SIZE_NORMAL);
let font = t.new_font_by_name(font_name, font_size).build().unwrap();
self.text.with_display_text(data, env, |text| {
t.new_text_layout(&font, &text).build().unwrap()
})
}
}
impl<T: Data> LabelText<T> {
pub fn with_display_text<V>(&self, data: &T, env: &Env, mut cb: impl FnMut(&str) -> V) -> V {
match self {
LabelText::Specific(s) => cb(s.as_str()),
LabelText::Localized(s) => cb(s.localized_str()),
LabelText::Dynamic(f) => cb((f)(data, env).as_str()),
}
}
pub fn resolve(&mut self, data: &T, env: &Env) -> bool {
match self {
LabelText::Specific(_) => false,
LabelText::Localized(s) => s.resolve(data, env),
LabelText::Dynamic(_s) => false,
}
}
}
impl<T: Data> Widget<T> for Label<T> {
fn event(&mut self, _ctx: &mut EventCtx, _event: &Event, _data: &mut T, _env: &Env) {}
fn update(&mut self, ctx: &mut UpdateCtx, _old_data: Option<&T>, data: &T, env: &Env) {
if self.text.resolve(data, env) {
ctx.invalidate();
}
}
fn layout(
&mut self,
layout_ctx: &mut LayoutCtx,
bc: &BoxConstraints,
data: &T,
env: &Env,
) -> Size {
bc.debug_check("Label");
let font_size = env.get(theme::TEXT_SIZE_NORMAL);
let text_layout = self.get_layout(layout_ctx.text(), env, data);
bc.constrain(Size::new(text_layout.width(), font_size * 1.2))
}
fn paint(&mut self, paint_ctx: &mut PaintCtx, data: &T, env: &Env) {
let font_size = env.get(theme::TEXT_SIZE_NORMAL);
let text_layout = self.get_layout(paint_ctx.text(), env, data);
let mut origin = self.align.resolve(Rect::from_origin_size(
Point::ORIGIN,
Size::new(
(paint_ctx.size().width - text_layout.width()).max(0.0),
paint_ctx.size().height + (font_size * 1.2) / 2.,
),
));
origin.y = origin.y.min(paint_ctx.size().height);
paint_ctx.draw_text(&text_layout, origin, &env.get(theme::LABEL_COLOR));
}
}
impl<T> From<String> for LabelText<T> {
fn from(src: String) -> LabelText<T> {
LabelText::Specific(src)
}
}
impl<T> From<&str> for LabelText<T> {
fn from(src: &str) -> LabelText<T> {
LabelText::Specific(src.to_string())
}
}
impl<T> From<LocalizedString<T>> for LabelText<T> {
fn from(src: LocalizedString<T>) -> LabelText<T> {
LabelText::Localized(src)
}
}
impl<T, F: Fn(&T, &Env) -> String + 'static> From<F> for LabelText<T> {
fn from(src: F) -> LabelText<T> {
LabelText::Dynamic(Box::new(src))
}
}