pixels_graphics_lib/ui/
tooltip.rs

1use crate::ui::prelude::*;
2use crate::ui::styles::TooltipStyle;
3use crate::Timing;
4use buffer_graphics_lib::prelude::DrawType::Fill;
5use buffer_graphics_lib::prelude::*;
6
7#[derive(Debug)]
8pub struct Tooltip {
9    label: String,
10    text: Text,
11    background: Drawable<Rect>,
12    border: Polyline,
13    shadow: Polyline,
14    style: TooltipStyle,
15}
16
17impl Tooltip {
18    pub fn new<P: Into<Coord>>(
19        anchor: P,
20        text: &str,
21        positioning: Positioning,
22        style: &TooltipStyle,
23    ) -> Self {
24        let bounds = Self::calc_bounds(anchor.into(), positioning, text, style.font);
25        let bounds = Rect::new_with_size(
26            bounds.top_left(),
27            bounds.width() + (style.padding),
28            bounds.height() + (style.padding),
29        );
30        let label = text;
31        let (border, shadow, background, text) = Self::layout(&bounds, label, style);
32        Self {
33            label: label.to_string(),
34            text,
35            background,
36            border,
37            shadow,
38            style: style.clone(),
39        }
40    }
41
42    fn layout(
43        bounds: &Rect,
44        label: &str,
45        style: &TooltipStyle,
46    ) -> (Polyline, Polyline, Drawable<Rect>, Text) {
47        let border = Polyline::rounded_rect(
48            bounds.left(),
49            bounds.top(),
50            bounds.right(),
51            bounds.bottom(),
52            0,
53            WHITE,
54        )
55        .unwrap();
56        let shadow = Polyline::rounded_rect(
57            bounds.left() + 1,
58            bounds.top() + 1,
59            bounds.right() + 1,
60            bounds.bottom() + 1,
61            0,
62            WHITE,
63        )
64        .unwrap();
65        let background = Drawable::from_obj(
66            Rect::new(bounds.top_left(), bounds.bottom_right()),
67            Fill(BLACK),
68        );
69        let text = Text::new(
70            label,
71            TextPos::px(bounds.top_left() + (style.padding, style.padding)),
72            (WHITE, style.font, WrappingStrategy::SpaceBeforeCol(20)),
73        );
74        (border, shadow, background, text)
75    }
76}
77
78impl Tooltip {
79    pub fn calc_bounds(xy: Coord, positioning: Positioning, text: &str, font: PixelFont) -> Rect {
80        let (w, h) = font.measure(&WrappingStrategy::SpaceBeforeCol(20).wrap(text).join("\n"));
81        let anchor = positioning.calc((xy.x, xy.y), w, h);
82        Rect::new_with_size(Coord::new(anchor.0, anchor.1), w, h)
83    }
84}
85
86impl PixelView for Tooltip {
87    fn set_position(&mut self, top_left: Coord) {
88        self.background = self.background.with_move(top_left);
89        let (border, shadow, _, text) =
90            Self::layout(self.background.obj(), &self.label, &self.style);
91        self.border = border;
92        self.shadow = shadow;
93        self.text = text;
94    }
95
96    fn bounds(&self) -> &Rect {
97        self.background.obj()
98    }
99
100    fn render(&self, graphics: &mut Graphics, _: &MouseData) {
101        if let Some(color) = self.style.shadow.get(false, false, false) {
102            self.shadow.with_color(color).render(graphics);
103        }
104        if let Some(color) = self.style.background.get(false, false, false) {
105            self.background.with_draw_type(Fill(color)).render(graphics);
106        }
107        if let Some(color) = self.style.border.get(false, false, false) {
108            self.border.with_color(color).render(graphics);
109        }
110        if let Some(color) = self.style.text.get(false, false, false) {
111            self.text.with_color(color).render(graphics);
112        }
113    }
114
115    fn update(&mut self, _: &Timing) {}
116
117    fn set_state(&mut self, _: ViewState) {
118        unimplemented!("Tooltip doesn't have state")
119    }
120
121    fn get_state(&self) -> ViewState {
122        unimplemented!("Tooltip doesn't have state")
123    }
124}