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