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}