summaryrefslogtreecommitdiffstats
path: root/examples/tour/renderer/text.rs
blob: ecf1481ebf08b528947954c838d5025b6775925b (plain) (blame)
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
use super::Renderer;
use ggez::graphics::{self, mint, Align, Color, Scale, Text, TextFragment};

use iced::text;
use std::cell::RefCell;
use std::f32;

impl text::Renderer<Color> for Renderer<'_> {
    fn node(
        &self,
        style: iced::Style,
        content: &str,
        size: Option<u16>,
    ) -> iced::Node {
        let font = self.font;
        let font_cache = graphics::font_cache(self.context);
        let content = String::from(content);

        // TODO: Investigate why stretch tries to measure this MANY times
        // with every ancestor's bounds.
        // Bug? Using the library wrong? I should probably open an issue on
        // the stretch repository.
        // I noticed that the first measure is the one that matters in
        // practice. Here, we use a RefCell to store the cached measurement.
        let measure = RefCell::new(None);
        let size = size.map(f32::from).unwrap_or(self.font_size);

        iced::Node::with_measure(style, move |bounds| {
            let mut measure = measure.borrow_mut();

            if measure.is_none() {
                let bounds = (
                    match bounds.width {
                        iced::Number::Undefined => f32::INFINITY,
                        iced::Number::Defined(w) => w,
                    },
                    match bounds.height {
                        iced::Number::Undefined => f32::INFINITY,
                        iced::Number::Defined(h) => h,
                    },
                );

                let mut text = Text::new(TextFragment {
                    text: content.clone(),
                    font: Some(font),
                    scale: Some(Scale { x: size, y: size }),
                    ..Default::default()
                });

                text.set_bounds(
                    mint::Point2 {
                        x: bounds.0,
                        y: bounds.1,
                    },
                    Align::Left,
                );

                let (width, height) = font_cache.dimensions(&text);

                let size = iced::Size {
                    width: width as f32,
                    height: height as f32,
                };

                // If the text has no width boundary we avoid caching as the
                // layout engine may just be measuring text in a row.
                if bounds.0 == f32::INFINITY {
                    return size;
                } else {
                    *measure = Some(size);
                }
            }

            measure.unwrap()
        })
    }

    fn draw(
        &mut self,
        bounds: iced::Rectangle,
        content: &str,
        size: Option<u16>,
        color: Option<Color>,
        horizontal_alignment: text::HorizontalAlignment,
        _vertical_alignment: text::VerticalAlignment,
    ) {
        let size = size.map(f32::from).unwrap_or(self.font_size);

        let mut text = Text::new(TextFragment {
            text: String::from(content),
            font: Some(self.font),
            scale: Some(Scale { x: size, y: size }),
            ..Default::default()
        });

        text.set_bounds(
            mint::Point2 {
                x: bounds.width,
                y: bounds.height,
            },
            match horizontal_alignment {
                text::HorizontalAlignment::Left => graphics::Align::Left,
                text::HorizontalAlignment::Center => graphics::Align::Center,
                text::HorizontalAlignment::Right => graphics::Align::Right,
            },
        );

        graphics::queue_text(
            self.context,
            &text,
            mint::Point2 {
                x: bounds.x,
                y: bounds.y,
            },
            color.or(Some(graphics::BLACK)),
        );
    }
}