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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
use crate::ui::prelude::*;
use crate::ui::styles::TooltipStyle;
use crate::Timing;
use buffer_graphics_lib::prelude::DrawType::Fill;
use buffer_graphics_lib::prelude::*;

#[derive(Debug)]
pub struct Tooltip {
    label: String,
    text: Text,
    background: Drawable<Rect>,
    border: Polyline,
    shadow: Polyline,
    style: TooltipStyle,
}

impl Tooltip {
    pub fn new<P: Into<Coord>>(
        anchor: P,
        text: &str,
        positioning: Positioning,
        style: &TooltipStyle,
    ) -> Self {
        let bounds = Self::calc_bounds(anchor.into(), positioning, text, style.size);
        let bounds = Rect::new_with_size(
            bounds.top_left(),
            bounds.width() + (style.padding),
            bounds.height() + (style.padding),
        );
        let label = text;
        let (border, shadow, background, text) = Self::layout(&bounds, label, style);
        Self {
            label: label.to_string(),
            text,
            background,
            border,
            shadow,
            style: style.clone(),
        }
    }

    fn layout(
        bounds: &Rect,
        label: &str,
        style: &TooltipStyle,
    ) -> (Polyline, Polyline, Drawable<Rect>, Text) {
        let border = Polyline::rounded_rect(
            bounds.left(),
            bounds.top(),
            bounds.right(),
            bounds.bottom(),
            0,
            WHITE,
        )
        .unwrap();
        let shadow = Polyline::rounded_rect(
            bounds.left() + 1,
            bounds.top() + 1,
            bounds.right() + 1,
            bounds.bottom() + 1,
            0,
            WHITE,
        )
        .unwrap();
        let background = Drawable::from_obj(
            Rect::new(bounds.top_left(), bounds.bottom_right()),
            Fill(BLACK),
        );
        let text = Text::new(
            label,
            TextPos::px(bounds.top_left() + (style.padding, style.padding)),
            (WHITE, style.size, WrappingStrategy::SpaceBeforeCol(20)),
        );
        (border, shadow, background, text)
    }
}

impl Tooltip {
    pub fn calc_bounds(
        xy: Coord,
        positioning: Positioning,
        text: &str,
        text_size: TextSize,
    ) -> Rect {
        let (w, h) = text_size.measure(text, WrappingStrategy::SpaceBeforeCol(20));
        let anchor = positioning.calc((xy.x, xy.y), w, h);
        Rect::new_with_size(Coord::new(anchor.0, anchor.1), w, h)
    }
}

impl UiElement for Tooltip {
    fn set_position(&mut self, top_left: Coord) {
        self.background = self.background.with_move(top_left);
        let (border, shadow, _, text) =
            Self::layout(self.background.obj(), &self.label, &self.style);
        self.border = border;
        self.shadow = shadow;
        self.text = text;
    }

    fn bounds(&self) -> &Rect {
        self.background.obj()
    }

    fn render(&self, graphics: &mut Graphics, _: Coord) {
        if let Some(color) = self.style.shadow.get(false, false, false) {
            self.shadow.with_color(color).render(graphics);
        }
        if let Some(color) = self.style.background.get(false, false, false) {
            self.background.with_draw_type(Fill(color)).render(graphics);
        }
        if let Some(color) = self.style.border.get(false, false, false) {
            self.border.with_color(color).render(graphics);
        }
        if let Some(color) = self.style.text.get(false, false, false) {
            self.text.with_color(color).render(graphics);
        }
    }

    fn update(&mut self, _: &Timing) {}

    fn set_state(&mut self, _: ElementState) {
        unimplemented!("Tooltip doesn't have state")
    }

    fn get_state(&self) -> ElementState {
        unimplemented!("Tooltip doesn't have state")
    }
}