yuno 0.1.0

A declarative UI layout and rendering framework powered by Skia.
use crate::drawing::{Drawable, Plan};
use crate::layouts::{HasPlanner, Layout};
use skia_safe::{Canvas, Font, Paint, Point};
use std::cell::RefCell;
use std::rc::Rc;

pub struct TruncatedText {
    text: RefCell<String>,
    font: Font,
    pub p: RefCell<Paint>,
    dot_w: f32,
    planner: RefCell<Option<Rc<dyn Layout>>>,
}

impl HasPlanner for TruncatedText {
    fn planner(&self) -> &RefCell<Option<Rc<dyn Layout>>> {
        &self.planner
    }
    fn set_planner(&self, p: Option<Rc<dyn Layout>>) {
        *self.planner.borrow_mut() = p;
    }
}

impl Drawable for TruncatedText {
    fn draw(&self, canvas: &Canvas) -> anyhow::Result<()> {
        let area = self.get_planned_drawing_area(canvas);
        if area.is_empty() {
            return Ok(());
        }

        let display_text = self.text.borrow();
        let max_w = area.width();

        let mut glyphs = vec![skia_safe::GlyphId::default(); display_text.len()];
        let actual_glyph_count = self
            .font
            .text_to_glyphs(display_text.to_owned(), &mut glyphs);
        glyphs.truncate(actual_glyph_count);

        let mut widths = vec![0.0f32; glyphs.len()];
        self.font.get_widths(&glyphs, &mut widths);

        let total_text_w: f32 = widths.iter().sum();
        let mut text_to_draw = display_text.clone();

        if total_text_w > max_w {
            let mut current_w = 0.0;
            let mut char_count = 0;
            let allowed_w = max_w - self.dot_w;

            for &w in &widths {
                if current_w + w > allowed_w {
                    break;
                }
                current_w += w;
                char_count += 1;
            }
            if char_count > 0 {
                text_to_draw = format!(
                    "{}...",
                    &display_text.chars().take(char_count).collect::<String>()
                );
            }
        }

        let (_, metrics) = self.font.metrics();
        let center_y = area.top + area.height() / 2.0;
        let baseline_y = center_y - (metrics.ascent + metrics.descent) / 2.0;

        canvas.draw_str(
            &text_to_draw,
            Point::new(area.left, baseline_y),
            &self.font,
            &self.p.borrow(),
        );
        Ok(())
    }

    fn get_horizontal_plan(&self) -> Plan {
        Plan::Fill
    }
    fn get_vertical_plan(&self) -> Plan {
        Plan::Fill
    }
}

impl TruncatedText {
    pub fn new(text: &str, font: Font, paint: Paint, dot_w: f32) -> Rc<Self> {
        Rc::new(Self {
            text: RefCell::new(text.to_string()),
            font,
            p: RefCell::new(paint),
            dot_w,
            planner: RefCell::new(None),
        })
    }
}