1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
use std::borrow::{Borrow, Cow};
use std::fmt::Debug;
use std::num::NonZeroI16;
use tuifw_screen_base::{Vector, Point, Rect};
use tuifw_window::{RenderPort};
use dep_obj::{dep_type, DepObjBuilderCore};
use dyn_context::{Context, ContextExt};
use crate::view::base::*;
use unicode_width::UnicodeWidthChar;
use unicode_segmentation::UnicodeSegmentation;

pub trait ViewBuilderLabelDecoratorExt {
    fn label_decorator(
        self,
        f: impl for<'a> FnOnce(LabelDecoratorBuilder<'a>) -> LabelDecoratorBuilder<'a>
    ) -> Self;
}

impl<'a> ViewBuilderLabelDecoratorExt for ViewBuilder<'a> {
    fn label_decorator(
        mut self,
        f: impl for<'b> FnOnce(LabelDecoratorBuilder<'b>) -> LabelDecoratorBuilder<'b>
    ) -> Self {
        let view = self.id();
        let tree: &mut ViewTree = self.context_mut().get_mut();
        LabelDecorator::new(tree, view);
        f(LabelDecoratorBuilder::new_priv(self)).core_priv()
    }
}

dep_type! {
    #[derive(Debug)]
    pub struct LabelDecorator become decorator in View {
        text: Cow<'static, str> = Cow::Borrowed(""),
    }

    type BuilderCore<'a> = ViewBuilder<'a>;
}

impl LabelDecorator {
    const BEHAVIOR: LabelDecoratorBehavior = LabelDecoratorBehavior;

    #[allow(clippy::new_ret_no_self)]
    pub fn new(
        tree: &mut ViewTree,
        view: View,
    ) {
        view.set_decorator(tree, LabelDecorator::new_priv());
        view.decorator_on_changed(tree, LabelDecorator::TEXT, Self::invalidate_measure);
    }

    fn invalidate_measure<T>(context: &mut dyn Context, view: View, _old: &T) {
        let tree: &mut ViewTree = context.get_mut();
        view.invalidate_measure(tree);
    }
}

impl Decorator for LabelDecorator {
    fn behavior(&self) -> &'static dyn DecoratorBehavior { &Self::BEHAVIOR }
}

struct LabelDecoratorBehavior;

impl DecoratorBehavior for LabelDecoratorBehavior {
    fn children_measure_size(
        &self,
        _view: View,
        _tree: &mut ViewTree,
        _measure_size: (Option<i16>, Option<i16>)
    ) -> (Option<i16>, Option<i16>) {
        (Some(0), Some(0))
    }

    fn desired_size(&self, view: View, tree: &mut ViewTree, _children_desired_size: Vector) -> Vector {
        let text: &str = view.decorator_get(tree, LabelDecorator::TEXT).borrow();
        let width = text
            .graphemes(true)
            .map(|g| g
                .chars()
                .find_map(|c| NonZeroI16::new(c.width().unwrap_or(1) as u16 as i16))
                .map_or(0, |x| x.get())
            )
            .sum();
        Vector { x: width, y: 1 }
    }

    fn children_arrange_bounds(&self, _view: View, _tree: &mut ViewTree, _arrange_size: Vector) -> Rect {
        Rect { tl: Point { x: 0, y: 0}, size: Vector::null() }
    }

    fn render_bounds(&self, view: View, tree: &mut ViewTree, _children_render_bounds: Rect) -> Rect {
        let text: &str = view.decorator_get(tree, LabelDecorator::TEXT).borrow();
        let width = text
            .graphemes(true)
            .map(|g| g
                .chars()
                .find_map(|c| NonZeroI16::new(c.width().unwrap_or(1) as u16 as i16))
                .map_or(0, |x| x.get())
            )
            .sum();
        Rect { tl: Point { x: 0, y: 0 }, size: Vector { x: width, y: 1 } }
    }

    fn render(&self, view: View, tree: &ViewTree, port: &mut RenderPort) {
        let text: &str = view.decorator_get(tree, LabelDecorator::TEXT).borrow();
        let fg = view.actual_fg(tree);
        let bg = view.actual_bg(tree);
        let attr = view.actual_attr(tree);
        port.out(Point { y: 0, x: 0 }, fg, bg, attr, text);
    }
}