1use crate::{
2 geometry::Rect,
3 render::RenderCtx,
4 style::{VisualState, lerp_style},
5};
6use embedded_graphics_core::pixelcolor::Rgb565;
7
8use super::*;
9
10impl<'a, const NODES: usize, const EVENTS: usize, const DIRTY: usize>
11 GuiContext<'a, NODES, EVENTS, DIRTY>
12{
13 pub fn render<D>(&self, target: &mut D) -> Result<(), D::Error>
14 where
15 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>,
16 {
17 let mut ctx = RenderCtx::new(target, self.viewport);
18 ctx.set_quality(self.render_quality);
19 self.render_into(&mut ctx, 0, 0, 255)
20 }
21
22 pub fn render_composited<D>(&self, target: &mut D) -> Result<(), D::Error>
27 where
28 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>
29 + crate::render::PixelRead,
30 {
31 let mut ctx = RenderCtx::compositing(target, self.viewport);
32 ctx.set_quality(self.render_quality);
33 self.render_into::<D, crate::render::Blend>(&mut ctx, 0, 0, 255)
34 }
35
36 pub fn render_dirty<D>(&self, target: &mut D) -> Result<(), D::Error>
37 where
38 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>,
39 {
40 let slice = self.dirty.as_slice();
41 if slice.is_empty() {
42 return Ok(());
43 }
44
45 if slice.len() == 1 {
46 let mut ctx = RenderCtx::with_dirty(target, self.viewport, slice[0]);
47 ctx.set_quality(self.render_quality);
48 return self.render_into(&mut ctx, 0, 0, 255);
49 }
50
51 if let Some(bound) = self.dirty.bounding_rect() {
52 let area_bound = bound.w as u64 * bound.h as u64;
53 let sum_area: u64 = slice.iter().map(|r| r.w as u64 * r.h as u64).sum();
54 if area_bound <= sum_area + (sum_area / 2) {
57 let mut ctx = RenderCtx::with_dirty(target, self.viewport, bound);
58 ctx.set_quality(self.render_quality);
59 return self.render_into(&mut ctx, 0, 0, 255);
60 }
61 }
62
63 for dirty in slice {
64 let mut ctx = RenderCtx::with_dirty(target, self.viewport, *dirty);
65 ctx.set_quality(self.render_quality);
66 self.render_into(&mut ctx, 0, 0, 255)?;
67 }
68 Ok(())
69 }
70
71 pub fn render_dirty_windowed<D>(&self, target: &mut D) -> Result<(), D::Error>
75 where
76 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>
77 + crate::render::WindowedDrawTarget,
78 {
79 let slice = self.dirty.as_slice();
80 if slice.is_empty() {
81 return Ok(());
82 }
83
84 for dirty in slice {
85 let eg_rect = embedded_graphics_core::primitives::Rectangle::new(
86 embedded_graphics_core::geometry::Point::new(dirty.x, dirty.y),
87 embedded_graphics_core::geometry::Size::new(dirty.w, dirty.h),
88 );
89 target.set_window(&eg_rect)?;
90 let mut ctx = RenderCtx::with_dirty(target, self.viewport, *dirty);
91 ctx.set_quality(self.render_quality);
92 self.render_into(&mut ctx, 0, 0, 255)?;
93 }
94 Ok(())
95 }
96
97 pub fn render_with_offset<D>(
98 &self,
99 target: &mut D,
100 offset_x: i32,
101 offset_y: i32,
102 ) -> Result<(), D::Error>
103 where
104 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>,
105 {
106 self.render_with_offset_and_opacity(target, offset_x, offset_y, 255)
107 }
108
109 pub fn render_with_offset_and_opacity<D>(
110 &self,
111 target: &mut D,
112 offset_x: i32,
113 offset_y: i32,
114 opacity: u8,
115 ) -> Result<(), D::Error>
116 where
117 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>,
118 {
119 let mut ctx = RenderCtx::new(target, self.viewport);
120 ctx.set_quality(self.render_quality);
121 self.render_into(&mut ctx, offset_x, offset_y, opacity)
122 }
123
124 pub fn render_with_offset_opacity_and_clip<D>(
125 &self,
126 target: &mut D,
127 offset_x: i32,
128 offset_y: i32,
129 opacity: u8,
130 clip: Rect,
131 ) -> Result<(), D::Error>
132 where
133 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>,
134 {
135 let mut ctx = RenderCtx::new(target, self.viewport);
136 ctx.set_quality(self.render_quality);
137 let old_clip = ctx.clip();
138 ctx.set_clip(old_clip.intersection(clip));
139 self.render_into(&mut ctx, offset_x, offset_y, opacity)
140 }
141
142 pub(crate) fn render_into<D, C>(
143 &self,
144 ctx: &mut RenderCtx<'_, D, C>,
145 offset_x: i32,
146 offset_y: i32,
147 opacity: u8,
148 ) -> Result<(), D::Error>
149 where
150 D: embedded_graphics_core::draw_target::DrawTarget<Color = Rgb565>,
151 C: crate::render::Compositor<D>,
152 {
153 for node in &self.widgets {
154 if !self.effective_visible(node.id) {
155 continue;
156 }
157 let Some(base_rect) = self.absolute_rect(node.id) else {
158 continue;
159 };
160 let rect = Rect::new(
161 base_rect.x + offset_x,
162 base_rect.y + offset_y,
163 base_rect.w,
164 base_rect.h,
165 );
166 let base_clip = self.inherited_clip(node.id).unwrap_or(self.viewport);
167 let clip = Rect::new(
168 base_clip.x + offset_x,
169 base_clip.y + offset_y,
170 base_clip.w,
171 base_clip.h,
172 );
173 if rect.intersection(clip).is_empty() {
174 continue;
175 }
176 let old_clip = ctx.clip();
177 ctx.set_clip(old_clip.intersection(clip));
178 let state = if self.pressed.is_some_and(|pressed| pressed.id == node.id) {
179 VisualState::Pressed
180 } else if Some(node.id) == self.focus {
181 VisualState::Focused
182 } else if !self.effective_enabled(node.id) {
183 VisualState::Disabled
184 } else {
185 VisualState::Normal
186 };
187 let mut render_node = *node;
188 let class_style = node.style_class.and_then(|class| {
189 self.class_styles
190 .iter()
191 .find(|(id, _)| *id == class)
192 .map(|(_, style)| *style)
193 });
194 let resolve_state_style = |vs: VisualState| {
195 class_style
196 .map(|style| style.resolve(vs))
197 .unwrap_or_else(|| render_node.style.resolve(vs))
198 };
199 let active_style = if let Some((from, to, t)) = self.state_transition_progress(node.id)
200 {
201 lerp_style(resolve_state_style(from), resolve_state_style(to), t)
202 } else {
203 resolve_state_style(state)
204 };
205 render_node.style = render_node.style.with_state_override(state, active_style);
206 if opacity < 255 {
207 let apply = |v: u8| -> u8 { ((v as u16 * opacity as u16) / 255) as u8 };
208 render_node.style.normal.opacity = apply(render_node.style.normal.opacity);
209 render_node.style.focused.opacity = apply(render_node.style.focused.opacity);
210 render_node.style.pressed.opacity = apply(render_node.style.pressed.opacity);
211 render_node.style.disabled.opacity = apply(render_node.style.disabled.opacity);
212 }
213 render_node.render_at(ctx, rect, state)?;
214 ctx.set_clip(old_clip);
215 }
216 Ok(())
217 }
218}