Skip to main content

coffee/ui/
renderer.rs

1mod button;
2mod checkbox;
3mod image;
4mod panel;
5mod progress_bar;
6mod radio;
7mod slider;
8mod text;
9
10use crate::graphics::{Batch, Color, Font, Frame, Image, Mesh, Shape};
11use crate::load::{Join, Task};
12use crate::ui::core;
13
14use std::cell::RefCell;
15use std::rc::Rc;
16
17/// A renderer capable of drawing all the [built-in widgets].
18///
19/// It can be configured using [`Configuration`] and
20/// [`UserInterface::configuration`].
21///
22/// [built-in widgets]: widget/index.html
23/// [`Configuration`]: struct.Configuration.html
24/// [`UserInterface::configuration`]: trait.UserInterface.html#method.configuration
25pub struct Renderer {
26    pub(crate) sprites: Batch,
27    pub(crate) images: Vec<Batch>,
28    pub(crate) font: Rc<RefCell<Font>>,
29    explain_mesh: Mesh,
30}
31
32impl std::fmt::Debug for Renderer {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        f.debug_struct("Renderer")
35            .field("sprites", &self.sprites)
36            .field("images", &self.images)
37            .finish()
38    }
39}
40
41impl core::Renderer for Renderer {
42    type Configuration = Configuration;
43
44    fn load(config: Configuration) -> Task<Renderer> {
45        (config.sprites, config.font)
46            .join()
47            .map(|(sprites, font)| Renderer {
48                sprites: Batch::new(sprites),
49                images: Vec::new(),
50                font: Rc::new(RefCell::new(font)),
51                explain_mesh: Mesh::new(),
52            })
53    }
54
55    fn explain(&mut self, layout: &core::Layout<'_>, color: Color) {
56        self.explain_mesh
57            .stroke(Shape::Rectangle(layout.bounds()), color, 1.0);
58
59        layout
60            .children()
61            .for_each(|layout| self.explain(&layout, color));
62    }
63
64    fn flush(&mut self, frame: &mut Frame<'_>) {
65        let target = &mut frame.as_target();
66
67        self.sprites.draw(target);
68        self.sprites.clear();
69
70        for image in &self.images {
71            image.draw(target);
72        }
73
74        self.images.clear();
75
76        self.font.borrow_mut().draw(target);
77
78        if !self.explain_mesh.is_empty() {
79            self.explain_mesh.draw(target);
80            self.explain_mesh = Mesh::new();
81        }
82    }
83}
84
85/// The [`Renderer`] configuration.
86///
87/// You can implement [`UserInterface::configuration`] and return your own
88/// [`Configuration`] to customize the built-in [`Renderer`].
89///
90/// [`Renderer`]: struct.Renderer.html
91/// [`UserInterface::configuration`]: trait.UserInterface.html#method.configuration
92/// [`Configuration`]: struct.Configuration.html
93///
94/// # Example
95/// ```no_run
96/// use coffee::graphics::Image;
97/// use coffee::ui::Configuration;
98///
99/// Configuration {
100///     sprites: Image::load("resources/my_ui_sprites.png"),
101///     ..Configuration::default()
102/// };
103/// ```
104#[derive(Debug)]
105pub struct Configuration {
106    /// The spritesheet used to render the [different widgets] of the user interface.
107    ///
108    /// The spritesheet needs to be structured like [the default spritesheet].
109    ///
110    /// [different widgets]: widget/index.html
111    /// [the default spritesheet]: https://raw.githubusercontent.com/hecrj/coffee/92aa6b64673116fdc49d8694a10ee5bf53afb1b5/resources/ui.png
112    pub sprites: Task<Image>,
113
114    /// The font used to render [`Text`].
115    ///
116    /// By default, it uses [Inconsolata Regular].
117    ///
118    /// [`Text`]: widget/text/struct.Text.html
119    /// [Inconsolata Regular]: https://fonts.google.com/specimen/Inconsolata
120    pub font: Task<Font>,
121}
122
123impl Default for Configuration {
124    fn default() -> Self {
125        Self {
126            sprites: Task::using_gpu(|gpu| {
127                Image::from_image(
128                    gpu,
129                    &::image::load_from_memory(include_bytes!(
130                        "../../resources/ui.png"
131                    ))?,
132                )
133            }),
134            font: Font::load_from_bytes(include_bytes!(
135                "../../resources/font/Inconsolata-Regular.ttf"
136            )),
137        }
138    }
139}