matrix_gui/ui.rs
1//! User Interface (UI) module for the matrix_gui framework.
2//!
3//! This module provides the core UI infrastructure including:
4//! - Error handling for GUI operations
5//! - Widget rendering and state management
6//! - Interaction handling (touch/mouse input)
7//! - Focus management for keyboard navigation
8//! - Drawing primitives and utilities
9//! - Alignment and layout helpers
10//!
11//! # Core Components
12//!
13//! - [`Ui`]: Main UI context that manages drawing, state, and interactions
14//! - [`Widget`]: Trait for drawable UI components
15//! - [`Response`]: Result type for widget operations
16//! - [`Interaction`]: Input event types (pressed, drag, release)
17//!
18//! # Example
19//!
20//! ```ignore
21//! use matrix_gui::prelude::*;
22//! use embedded_graphics::primitives::Rectangle;
23//!
24//! // Create UI context
25//! let mut ui = Ui::new_fullscreen(&mut display, &widget_states, &style);
26//!
27//! // Add widgets
28//! ui.add(my_widget);
29//!
30//! // Handle interactions
31//! ui.interact(Interaction::Pressed(point));
32//! ```
33
34#[cfg(feature = "interaction")]
35use crate::region::Region;
36use crate::style::Style;
37use crate::widget_state::{RenderState, WidgetId, WidgetStates};
38use core::fmt::Debug;
39use embedded_graphics::draw_target::DrawTarget;
40use embedded_graphics::geometry::Dimensions;
41use embedded_graphics::pixelcolor::PixelColor;
42#[cfg(feature = "interaction")]
43use embedded_graphics::prelude::Point;
44use embedded_graphics::primitives::Rectangle;
45use embedded_graphics::{Drawable, Pixel};
46
47/// Error types that can occur during GUI operations.
48///
49/// This enum represents the various error conditions that can arise
50/// when performing GUI operations such as drawing or widget management.
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
52#[non_exhaustive]
53pub enum GuiError {
54 /// An error occurred during a drawing operation.
55 DrawError,
56 /// An invalid widget ID was provided.
57 InvalidWidgetId,
58 /// An invalid animation ID was provided.
59 InvalidAnimId,
60 /// A modal widget is already active.
61 ModalActive,
62}
63
64/// Result type alias for GUI operations.
65///
66/// This type alias provides a convenient way to handle results from
67/// GUI operations that can fail with [`GuiError`].
68pub type GuiResult<T> = Result<T, GuiError>;
69
70/// User interaction events.
71///
72/// This enum represents various types of user input events that can be
73/// processed by the UI system, such as touch presses, drags, and releases.
74#[cfg(feature = "interaction")]
75#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
76#[non_exhaustive]
77pub enum Interaction {
78 /// A press event at the given point (e.g., finger down or mouse click).
79 Pressed(Point),
80 /// A drag event at the given point (e.g., finger movement while pressed).
81 Drag(Point),
82 /// A release event at the given point (e.g., finger up or mouse release).
83 Release(Point),
84 /// A click event at the given point
85 Clicked(Point),
86 /// No interaction event.
87 #[default]
88 None,
89}
90
91#[cfg(feature = "interaction")]
92impl Interaction {
93 /// Returns the point associated with this interaction, if any.
94 ///
95 /// # Returns
96 ///
97 /// `Some(point)` if the interaction has an associated point,
98 /// `None` for `Interaction::None`.
99 fn get_point(&self) -> Option<Point> {
100 match self {
101 Interaction::Pressed(p) => Some(*p),
102 Interaction::Drag(p) => Some(*p),
103 Interaction::Release(p) => Some(*p),
104 Interaction::Clicked(p) => Some(*p),
105 Interaction::None => None,
106 }
107 }
108}
109
110/// Response from a widget operation.
111///
112/// This enum represents the various responses that a widget can return
113/// after processing an operation, such as drawing or handling interactions.
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
115#[non_exhaustive]
116pub enum Response {
117 /// No significant response, widget is idle.
118 Idle,
119 /// The widget was clicked (e.g., checkbox toggled).
120 Clicked,
121 /// The widget is currently being pressed down.
122 Pressed,
123 /// The widget was released.
124 Released,
125 /// The widget gained focus.
126 Focused,
127 /// The widget lost focus.
128 Defocused,
129 /// The widget was selected (e.g., radio button, list item).
130 Selected,
131 /// The widget was triggered (e.g., button activated).
132 Trigger,
133 /// The widget's content was edited.
134 Edited,
135 /// The widget's value changed.
136 ValueChanged,
137 /// The widget is being hovered over.
138 Hovered,
139 /// The widget was scrolled.
140 Scrolled,
141 /// The widget was disabled.
142 Disabled,
143 /// User-defined response with custom code.
144 User(u8),
145 /// An error occurred during the operation.
146 Error(GuiError),
147}
148
149// builder pattern
150impl Response {
151 /// Returns `true` if the response is idle.
152 pub const fn is_idle(&self) -> bool {
153 matches!(self, Response::Idle)
154 }
155
156 /// Returns `true` if the response is clicked.
157 pub const fn is_clicked(&self) -> bool {
158 matches!(self, Response::Clicked)
159 }
160
161 /// Returns `true` if the response is pressed.
162 pub const fn is_pressed(&self) -> bool {
163 matches!(self, Response::Pressed)
164 }
165
166 pub const fn is_released(&self) -> bool {
167 matches!(self, Response::Released)
168 }
169
170 /// Returns `true` if the response is focused.
171 pub const fn is_focused(&self) -> bool {
172 matches!(self, Response::Focused)
173 }
174
175 /// Returns `true` if the response is defocused.
176 pub const fn is_defocused(&self) -> bool {
177 matches!(self, Response::Defocused)
178 }
179
180 /// Returns `true` if the response is selected.
181 pub const fn is_selected(&self) -> bool {
182 matches!(self, Response::Selected)
183 }
184
185 /// Returns `true` if the response is trigger.
186 pub const fn is_trigger(&self) -> bool {
187 matches!(self, Response::Trigger)
188 }
189
190 /// Returns `true` if the response is edited.
191 pub const fn is_edited(&self) -> bool {
192 matches!(self, Response::Edited)
193 }
194
195 /// Returns `true` if the response indicates a value change.
196 pub const fn is_value_changed(&self) -> bool {
197 matches!(self, Response::ValueChanged)
198 }
199
200 /// Returns `true` if the response is hovered.
201 pub const fn is_hovered(&self) -> bool {
202 matches!(self, Response::Hovered)
203 }
204
205 /// Returns `true` if the response is scrolled.
206 pub const fn is_scrolled(&self) -> bool {
207 matches!(self, Response::Scrolled)
208 }
209
210 /// Returns `true` if the response is disabled.
211 pub const fn is_disabled(&self) -> bool {
212 matches!(self, Response::Disabled)
213 }
214
215 /// Returns `true` if the response is a user-defined response.
216 pub const fn is_user(&self) -> bool {
217 matches!(self, Response::User(_))
218 }
219
220 /// Returns the user code if the response is a user-defined response.
221 pub fn user_code(&self) -> Option<u8> {
222 match self {
223 Response::User(code) => Some(*code),
224 _ => None,
225 }
226 }
227
228 /// Returns the error if the response is an error.
229 pub fn error(&self) -> Option<GuiError> {
230 match self {
231 Response::Error(e) => Some(*e),
232 _ => None,
233 }
234 }
235}
236
237impl Response {
238 /// Creates a response from a GUI error.
239 ///
240 /// # Arguments
241 ///
242 /// * `error` - The error to convert into a response.
243 ///
244 /// # Returns
245 ///
246 /// `Response::Error(error)`.
247 pub fn from_error(error: GuiError) -> Response {
248 Response::Error(error)
249 }
250
251 /// Creates a response based on a change flag.
252 ///
253 /// # Arguments
254 ///
255 /// * `change` - `true` if a change occurred, `false` otherwise.
256 ///
257 /// # Returns
258 ///
259 /// `Response::ValueChanged` if `change` is `true`, `Response::Idle` otherwise.
260 pub fn from_change(change: bool) -> Self {
261 if change {
262 Response::ValueChanged
263 } else {
264 Response::Idle
265 }
266 }
267}
268
269/// Converts an interaction event into a response.
270///
271/// This implementation maps interaction events to widget responses:
272/// - `Interaction::None` → `Response::Idle`
273/// - `Interaction::Pressed` or `Interaction::Drag` → `Response::Pressed`
274/// - `Interaction::Release` → `Response::Trigger`
275#[cfg(feature = "interaction")]
276impl From<Interaction> for Response {
277 fn from(interaction: Interaction) -> Self {
278 match interaction {
279 Interaction::None => Response::Idle,
280 Interaction::Pressed(_) | Interaction::Drag(_) => Response::Pressed,
281 Interaction::Release(_) => Response::Released,
282 Interaction::Clicked(_) => Response::Clicked,
283 }
284 }
285}
286
287/// Trait for drawable UI widgets.
288///
289/// This trait defines the interface that all widgets must implement to be
290/// rendered within the UI system. Widgets are responsible for their own
291/// rendering and can return responses to indicate the result of operations.
292///
293/// # Type Parameters
294///
295/// * `DRAW` - The draw target type that implements [`DrawTarget`]
296/// * `COL` - The pixel color type that implements [`PixelColor`]
297///
298/// # Example
299///
300/// ```rust
301/// use matrix_gui::prelude::*;
302///
303/// struct MyWidget;
304///
305/// impl<DRAW, COL> Widget<DRAW, COL> for MyWidget
306/// where
307/// DRAW: DrawTarget<Color = COL>,
308/// COL: PixelColor,
309/// {
310/// fn draw(&mut self, ui: &mut Ui<DRAW, COL>) -> GuiResult<Response> {
311/// // Widget rendering logic here
312/// Ok(Response::Idle)
313/// }
314/// }
315/// ```
316pub trait Widget<DRAW: DrawTarget<Color = COL>, COL: PixelColor> {
317 /// Draws the widget to the UI context.
318 ///
319 /// This method is called by the UI system to render the widget.
320 /// The widget can use the provided UI context to access drawing
321 /// primitives, style information, and handle interactions.
322 ///
323 /// # Arguments
324 ///
325 /// * `ui` - Mutable reference to the UI context.
326 ///
327 /// # Returns
328 ///
329 /// A [`GuiResult<Response>`] indicating the result of the draw operation.
330 ///
331 /// # Errors
332 ///
333 /// Returns [`GuiError::DrawError`] if drawing fails.
334 fn draw(&mut self, ui: &mut Ui<DRAW, COL>) -> GuiResult<Response>;
335}
336
337/// Horizontal alignment options for UI elements.
338///
339/// This enum defines how content should be aligned horizontally within
340/// a container or bounding box.
341#[derive(Clone, Copy, Debug, PartialEq)]
342pub enum HorizontalAlign {
343 /// Align content to the left edge.
344 Left,
345 /// Align content to the center.
346 Center,
347 /// Align content to the right edge.
348 Right,
349 /// Justify content to fill the available width.
350 Justify,
351}
352
353/// Vertical alignment options for UI elements.
354///
355/// This enum defines how content should be aligned vertically within
356/// a container or bounding box.
357#[derive(Clone, Copy, Debug)]
358pub enum VerticalAlign {
359 /// Align content to the top edge.
360 Top,
361 /// Align content to the center.
362 Center,
363 /// Align content to the bottom edge.
364 Bottom,
365}
366
367/// Combined horizontal and vertical alignment.
368///
369/// This struct combines horizontal and vertical alignment into a single
370/// convenient type for positioning UI elements.
371///
372/// # Example
373///
374/// ```rust
375/// use matrix_gui::ui::{Align, HorizontalAlign, VerticalAlign};
376///
377/// // Center alignment
378/// let centered = Align(HorizontalAlign::Center, VerticalAlign::Center);
379///
380/// // Top-left alignment (default)
381/// let top_left = Align::default();
382/// ```
383#[derive(Clone, Copy, Debug)]
384pub struct Align(pub HorizontalAlign, pub VerticalAlign);
385
386/// Default alignment is top-left.
387impl Default for Align {
388 fn default() -> Self {
389 Align(HorizontalAlign::Left, VerticalAlign::Top)
390 }
391}
392
393/// Internal drawing helper that wraps a draw target.
394///
395/// This struct provides a thin wrapper around a draw target to simplify
396/// drawing operations within the UI system. It implements both `Drawable`
397/// and `DrawTarget` traits for flexibility.
398struct Painter<'a, COL: PixelColor, DRAW: DrawTarget<Color = COL>> {
399 /// The underlying draw target.
400 target: &'a mut DRAW,
401}
402
403impl<'a, COL: PixelColor, DRAW: DrawTarget<Color = COL>> Painter<'a, COL, DRAW> {
404 /// Creates a new painter wrapping the given draw target.
405 ///
406 /// # Arguments
407 ///
408 /// * `target` - Mutable reference to the draw target to wrap.
409 ///
410 /// # Returns
411 ///
412 /// A new `Painter` instance.
413 fn new(target: &'a mut DRAW) -> Self {
414 Self { target }
415 }
416
417 /// Draws a drawable item to the target.
418 ///
419 /// # Arguments
420 ///
421 /// * `item` - Reference to an item implementing `Drawable`.
422 ///
423 /// # Returns
424 ///
425 /// `Ok(())` if drawing succeeds, `Err(GuiError::DrawError)` otherwise.
426 fn draw(&mut self, item: &impl Drawable<Color = COL>) -> GuiResult<()> {
427 item.draw(self.target).map_err(|_| GuiError::DrawError)?;
428 Ok(())
429 }
430}
431
432/// Provides dimensions information for the painter.
433impl<COL: PixelColor, DRAW: DrawTarget<Color = COL, Error = ERR>, ERR> Dimensions
434 for Painter<'_, COL, DRAW>
435{
436 /// Returns the bounding box of the underlying draw target.
437 ///
438 /// # Returns
439 ///
440 /// A `Rectangle` representing the bounds of the draw target.
441 fn bounding_box(&self) -> Rectangle {
442 self.target.bounding_box()
443 }
444}
445
446/// Allows the painter to act as a draw target.
447impl<COL: PixelColor, DRAW: DrawTarget<Color = COL, Error = ERR>, ERR> DrawTarget
448 for Painter<'_, COL, DRAW>
449{
450 type Color = COL;
451 type Error = ERR;
452
453 /// Draws an iterator of pixels to the target.
454 ///
455 /// # Arguments
456 ///
457 /// * `pixels` - An iterator of pixels to draw.
458 ///
459 /// # Returns
460 ///
461 /// `Ok(())` if drawing succeeds, `Err(Self::Error)` otherwise.
462 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
463 where
464 I: IntoIterator<Item = Pixel<Self::Color>>,
465 {
466 self.target.draw_iter(pixels)
467 }
468}
469
470/// Main UI context for managing widgets, drawing, and interactions.
471///
472/// This struct is the central hub for all UI operations. It manages the
473/// draw target, widget states, styling, interactions, and focus management.
474/// All widgets are rendered through this context.
475///
476/// # Type Parameters
477///
478/// * `'a` - Lifetime of the borrowed resources
479/// * `DRAW` - The draw target type implementing [`DrawTarget`]
480/// * `COL` - The pixel color type implementing [`PixelColor`]
481///
482/// # Example
483///
484/// ```ignore
485/// use matrix_gui::prelude::*;
486/// use embedded_graphics::primitives::Rectangle;
487///
488/// // Create UI context with custom bounds
489/// let bounds = Rectangle::new(Point::new(0, 0), Size::new(320, 240));
490/// let mut ui = Ui::new(&mut display, bounds, &widget_states, &style);
491///
492/// // Or create fullscreen UI
493/// let mut ui = Ui::new_fullscreen(&mut display, &widget_states, &style);
494/// ```
495pub struct Ui<'a, DRAW, COL>
496where
497 DRAW: DrawTarget<Color = COL>,
498 COL: PixelColor,
499{
500 /// The bounding rectangle for the UI area.
501 bounds: Rectangle,
502 /// Internal painter for drawing operations.
503 painter: Painter<'a, COL, DRAW>,
504 /// Shared widget state storage.
505 widget_states: &'a WidgetStates<'a>,
506 /// Current styling configuration.
507 style: &'a Style<COL>,
508 /// Flag indicating if the background has been cleared.
509 cleared: bool,
510 /// Current interaction event (if interaction feature is enabled).
511 #[cfg(feature = "interaction")]
512 interact: Interaction,
513 /// Focus management state (if focus feature is enabled).
514 #[cfg(feature = "focus")]
515 focus: Option<crate::focus::Focus<'a>>,
516 /// Debug color for drawing widget bounds (if debug-color feature is enabled).
517 #[cfg(feature = "debug-color")]
518 debug_color: Option<COL>,
519}
520
521impl<DRAW, COL> Ui<'_, DRAW, COL>
522where
523 DRAW: DrawTarget<Color = COL>,
524 COL: PixelColor,
525{
526 /// Returns the screen width in pixels.
527 ///
528 /// # Returns
529 ///
530 /// The width of the UI bounds in pixels.
531 pub const fn get_screen_width(&self) -> u32 {
532 self.bounds.size.width
533 }
534
535 /// Returns the screen height in pixels.
536 ///
537 /// # Returns
538 ///
539 /// The height of the UI bounds in pixels.
540 pub const fn get_screen_height(&self) -> u32 {
541 self.bounds.size.height
542 }
543
544 /// Returns the screen bounding rectangle.
545 ///
546 /// # Returns
547 ///
548 /// A `Rectangle` representing the UI bounds.
549 pub const fn get_screen_bounds(&self) -> Rectangle {
550 self.bounds
551 }
552}
553
554impl<'a, COL, DRAW> Ui<'a, DRAW, COL>
555where
556 DRAW: DrawTarget<Color = COL>,
557 COL: PixelColor,
558{
559 /// Creates a new UI context with the specified bounds.
560 ///
561 /// # Arguments
562 ///
563 /// * `drawable` - Mutable reference to the draw target.
564 /// * `bounds` - The bounding rectangle for the UI area.
565 /// * `widget_states` - Reference to the widget state storage.
566 /// * `style` - Reference to the styling configuration.
567 ///
568 /// # Returns
569 ///
570 /// A new `Ui` instance.
571 pub fn new(
572 drawable: &'a mut DRAW,
573 bounds: Rectangle,
574 widget_states: &'a WidgetStates<'a>,
575 style: &'a Style<COL>,
576 ) -> Self {
577 Self {
578 bounds,
579 painter: Painter::new(drawable),
580 widget_states,
581 style,
582 cleared: false,
583 #[cfg(feature = "interaction")]
584 interact: Interaction::None,
585 #[cfg(feature = "focus")]
586 focus: None,
587 #[cfg(feature = "debug-color")]
588 debug_color: None,
589 }
590 }
591
592 /// Creates a new UI context covering the entire draw target.
593 ///
594 /// This is a convenience method that automatically determines the bounds
595 /// from the draw target's bounding box.
596 ///
597 /// # Arguments
598 ///
599 /// * `drawable` - Mutable reference to the draw target.
600 /// * `widget_states` - Reference to the widget state storage.
601 /// * `style` - Reference to the styling configuration.
602 ///
603 /// # Returns
604 ///
605 /// A new `Ui` instance with bounds covering the entire draw target.
606 pub fn new_fullscreen(
607 drawable: &'a mut DRAW,
608 widget_states: &'a WidgetStates<'a>,
609 style: &'a Style<COL>,
610 ) -> Self {
611 let bounds = drawable.bounding_box();
612 Ui::new(drawable, bounds, widget_states, style)
613 }
614
615 /// Adds a widget to the UI and draws it.
616 ///
617 /// This method calls the widget's `draw` method and returns the response.
618 /// If drawing fails, it returns an error response.
619 ///
620 /// # Arguments
621 ///
622 /// * `widget` - The widget to add and draw.
623 ///
624 /// # Returns
625 ///
626 /// The widget's response, or an error response if drawing fails.
627 pub fn add(&mut self, mut widget: impl Widget<DRAW, COL>) -> Response {
628 widget.draw(self).unwrap_or_else(Response::from_error)
629 }
630
631 /// Conditionally draws a widget based on its redraw state.
632 ///
633 /// This method only calls the drawing closure if the widget needs to be
634 /// redrawn (based on its state). This is useful for optimizing rendering
635 /// performance by avoiding unnecessary redraws.
636 ///
637 /// # Type Parameters
638 ///
639 /// * `ID` - The widget ID type implementing [`WidgetId`]
640 /// * `F` - The closure type that performs the drawing
641 ///
642 /// # Arguments
643 ///
644 /// * `id` - The widget's identifier.
645 /// * `f` - A closure that performs the drawing operation.
646 ///
647 /// # Returns
648 ///
649 /// The response from the drawing closure, or `Response::Idle` if no redraw was needed.
650 pub fn lazy_draw<F, ID: WidgetId>(&mut self, id: ID, f: F) -> Response
651 where
652 F: FnOnce(&mut Ui<DRAW, COL>) -> Response,
653 {
654 if self.widget_states.should_redraw(id) {
655 f(self)
656 } else {
657 Response::Idle
658 }
659 }
660
661 /// Returns a reference to the current style configuration.
662 ///
663 /// # Returns
664 ///
665 /// A reference to the [`Style`] configuration.
666 pub fn style(&self) -> &Style<COL> {
667 self.style
668 }
669
670 /// Sets the current interaction event.
671 ///
672 /// This method updates the interaction state that widgets can query
673 /// to handle user input events.
674 ///
675 /// # Arguments
676 ///
677 /// * `interaction` - The interaction event to set.
678 #[cfg(feature = "interaction")]
679 pub fn interact(&mut self, interaction: Interaction) {
680 self.interact = interaction;
681 }
682
683 /// Checks if the current interaction is within a widget's region.
684 ///
685 /// This method determines if the current interaction point (if any) is
686 /// contained within the specified region. It also marks the widget as
687 /// having received interaction.
688 ///
689 /// # Type Parameters
690 ///
691 /// * `ID` - The widget ID type implementing [`WidgetId`]
692 ///
693 /// # Arguments
694 ///
695 /// * `region` - The region to check for interaction.
696 ///
697 /// # Returns
698 ///
699 /// The interaction if it's within the region, `Interaction::None` otherwise.
700 #[cfg(feature = "interaction")]
701 pub fn check_interact<ID: WidgetId>(&mut self, region: &Region<ID>) -> Interaction {
702 self.widget_states.mark_as_interact(region.id());
703
704 #[cfg(feature = "popup")]
705 if self.is_modal_active() {
706 return Interaction::None;
707 }
708
709 let area = ®ion.rectangle();
710 if self
711 .interact
712 .get_point()
713 .map(|pt| area.contains(pt))
714 .unwrap_or(false)
715 {
716 self.interact
717 } else {
718 Interaction::None
719 }
720 }
721}
722
723impl<COL, DRAW> Ui<'_, DRAW, COL>
724where
725 DRAW: DrawTarget<Color = COL>,
726 COL: PixelColor,
727{
728 /// Returns whether the background has been cleared.
729 ///
730 /// # Returns
731 ///
732 /// `true` if the background has been cleared, `false` otherwise.
733 pub const fn cleared(&self) -> bool {
734 self.cleared
735 }
736
737 /// Clears the specified area with the background color.
738 ///
739 /// This method fills the given rectangle with the background color from
740 /// the current style. If the background has already been cleared and the
741 /// debug-color feature is not enabled, this method returns early.
742 ///
743 /// # Arguments
744 ///
745 /// * `area` - The rectangle to clear.
746 ///
747 /// # Returns
748 ///
749 /// `Ok(())` if clearing succeeds, `Err(GuiError)` otherwise.
750 pub fn clear_area(&mut self, area: &Rectangle) -> GuiResult<()> {
751 #[cfg(not(feature = "debug-color"))]
752 if self.cleared {
753 return Ok(());
754 }
755
756 self.clear_area_raw(area, self.style.background_color)?;
757
758 #[cfg(feature = "debug-color")]
759 self.draw_widget_bound_box(area);
760
761 Ok(())
762 }
763
764 /// Clears the specified area with a custom color.
765 ///
766 /// This method fills the given rectangle with the specified color.
767 /// It uses either the standard `StyledDrawable` trait or the optimized
768 /// `fill-rect` feature if enabled.
769 ///
770 /// # Arguments
771 ///
772 /// * `area` - The rectangle to clear.
773 /// * `color` - The color to fill the area with.
774 ///
775 /// # Returns
776 ///
777 /// `Ok(())` if clearing succeeds, `Err(GuiError::DrawError)` otherwise.
778 pub fn clear_area_raw(&mut self, area: &Rectangle, color: COL) -> GuiResult<()> {
779 #[cfg(not(feature = "fill-rect"))]
780 {
781 use embedded_graphics::primitives::{PrimitiveStyle, StyledDrawable};
782
783 area.draw_styled(&PrimitiveStyle::with_fill(color), self.painter.target)
784 .map_err(|_| GuiError::DrawError)
785 }
786 #[cfg(feature = "fill-rect")]
787 {
788 crate::fill_rect::fill_with_color(area, color);
789 Ok(())
790 }
791 }
792
793 /// Clears the entire background area.
794 ///
795 /// This method fills the entire UI bounds with the background color
796 /// from the current style and sets the cleared flag to `true`.
797 ///
798 /// # Returns
799 ///
800 /// `Ok(())` if clearing succeeds, `Err(GuiError)` otherwise.
801 pub fn clear_background(&mut self) -> GuiResult<()> {
802 self.cleared = true;
803 self.clear_area_raw(&self.bounds.clone(), self.style.background_color)
804 }
805}
806
807impl<'a, COL, DRAW> Ui<'a, DRAW, COL>
808where
809 DRAW: DrawTarget<Color = COL>,
810 COL: PixelColor,
811{
812 /// Draws a drawable item to the UI.
813 ///
814 /// This method provides direct access to draw any item that implements
815 /// the `Drawable` trait to the UI's draw target.
816 ///
817 /// # Arguments
818 ///
819 /// * `item` - Reference to the item to draw.
820 ///
821 /// # Returns
822 ///
823 /// `Ok(())` if drawing succeeds, `Err(GuiError::DrawError)` otherwise.
824 pub fn draw(&mut self, item: &impl Drawable<Color = COL>) -> GuiResult<()> {
825 self.painter.draw(item)
826 }
827}
828
829impl<'a, COL, DRAW> Ui<'a, DRAW, COL>
830where
831 DRAW: DrawTarget<Color = COL>,
832 COL: PixelColor,
833{
834 /// Retrieves the render state for a widget.
835 ///
836 /// This method returns a reference to the render state for the widget
837 /// with the specified ID. The render state contains information about
838 /// whether the widget needs to be redrawn.
839 ///
840 /// # Type Parameters
841 ///
842 /// * `ID` - The widget ID type implementing [`WidgetId`]
843 ///
844 /// # Arguments
845 ///
846 /// * `widget_id` - The identifier of the widget.
847 ///
848 /// # Returns
849 ///
850 /// A reference to the [`RenderState`] if the widget exists,
851 /// `Err(GuiError::InvalidWidgetId)` otherwise.
852 pub fn get_widget_state<ID: WidgetId>(&self, widget_id: ID) -> GuiResult<&RenderState> {
853 self.widget_states.get_state(widget_id)
854 }
855
856 /// Retrieves the animation status for a widget.
857 ///
858 /// This method returns the current animation status for the widget with the
859 /// specified ID. The animation status is a value that tracks the progress of
860 /// the animation.
861 ///
862 /// # Arguments
863 ///
864 /// * `anim_id` - The identifier of the animation.
865 ///
866 /// # Returns
867 ///
868 /// The current animation status as an `Option<i32>` value.
869 #[cfg(feature = "animation")]
870 pub fn take_anim_status(&self, anim_id: crate::prelude::AnimId) -> GuiResult<Option<i32>> {
871 self.widget_states.take_anim_status(anim_id)
872 }
873
874 /// Retrieves the animation status for a widget.
875 ///
876 /// This method returns the current animation status for the widget with the
877 /// specified ID. The animation status is a value that tracks the progress of
878 /// the animation.
879 ///
880 /// # Arguments
881 ///
882 /// * `anim_id` - The identifier of the animation.
883 ///
884 /// # Returns
885 ///
886 /// The current animation status as an `Option<i32>` value.
887 #[cfg(feature = "animation")]
888 pub fn get_anim_status(&self, anim_id: crate::prelude::AnimId) -> GuiResult<Option<i32>> {
889 self.widget_states.get_anim_status(anim_id)
890 }
891
892 /// Forces a widget to be redrawn on the next frame.
893 ///
894 /// This method marks the widget with the specified ID as needing to be
895 /// redrawn, regardless of its current state.
896 ///
897 /// # Type Parameters
898 ///
899 /// * `ID` - The widget ID type implementing [`WidgetId`]
900 ///
901 /// # Arguments
902 ///
903 /// * `widget_id` - The identifier of the widget to force redraw.
904 pub fn force_redraw<ID: WidgetId>(&self, widget_id: ID) {
905 self.widget_states.force_redraw(widget_id);
906 }
907
908 /// Forces all widgets to be redrawn on the next frame.
909 ///
910 /// This method marks all widgets as needing to be redrawn, regardless of
911 /// their current state. This is useful for refreshing the entire UI.
912 pub fn force_redraw_all(&self) {
913 self.widget_states.force_redraw_all();
914 }
915}
916
917/// Focus management implementation (requires "focus" feature).
918///
919/// This implementation provides methods for managing keyboard focus
920/// and navigation between widgets.
921#[cfg(feature = "focus")]
922impl<'a, COL, DRAW> Ui<'a, DRAW, COL>
923where
924 DRAW: DrawTarget<Color = COL>,
925 COL: PixelColor,
926{
927 /// Sets the focus state for the UI.
928 ///
929 /// This method associates a focus state tracker with the UI, enabling
930 /// keyboard navigation and focus management.
931 ///
932 /// # Type Parameters
933 ///
934 /// * `N` - The maximum number of focusable widgets
935 ///
936 /// # Arguments
937 ///
938 /// * `focus_state` - Mutable reference to the focus state tracker.
939 pub fn set_focus_state<const N: usize>(
940 &mut self,
941 focus_state: &'a mut crate::focus::FocusState<N>,
942 ) {
943 self.focus = Some(crate::focus::Focus::new(focus_state));
944 }
945
946 /// Checks if a widget is focused.
947 ///
948 /// This method determines if the widget with the specified ID is currently
949 /// focused. It also handles trigger events and updates the focus state.
950 ///
951 /// # Type Parameters
952 ///
953 /// * `ID` - The widget ID type implementing [`WidgetId`]
954 ///
955 /// # Arguments
956 ///
957 /// * `id` - The identifier of the widget to check.
958 ///
959 /// # Returns
960 ///
961 /// A [`crate::focus::Focused`] enum indicating the focus status.
962 pub fn check_focused<ID: WidgetId>(&mut self, id: ID) -> crate::focus::Focused {
963 use crate::focus::Focused;
964
965 #[cfg(feature = "popup")]
966 if self.is_modal_active() {
967 return Focused::No;
968 }
969
970 if let Some(focus) = self.focus.as_mut() {
971 self.widget_states.mark_as_interact(id);
972
973 if let Some(trigger_id) = focus.tracker.trigger
974 && trigger_id == id.id() as u16
975 {
976 focus.tracker.trigger = None;
977 return Focused::Trigger;
978 } else if focus.register_focus(id.id()) && focus.is_focused(id.id()) {
979 return Focused::Yes;
980 }
981 }
982
983 Focused::No
984 }
985}
986
987#[cfg(feature = "popup")]
988impl<'a, COL, DRAW> Ui<'a, DRAW, COL>
989where
990 DRAW: DrawTarget<Color = COL>,
991 COL: PixelColor,
992{
993 pub const fn is_modal_active(&self) -> bool {
994 self.widget_states.is_modal_active()
995 }
996
997 pub fn set_modal_active(&self, active: bool) {
998 self.widget_states.set_modal_active(active);
999 }
1000}
1001
1002/// Debug drawing implementation (requires "debug-color" feature).
1003///
1004/// This implementation provides methods for drawing debug visualizations
1005/// of widget bounds and UI areas.
1006#[cfg(feature = "debug-color")]
1007impl<COL, DRAW> Ui<'_, DRAW, COL>
1008where
1009 DRAW: DrawTarget<Color = COL>,
1010 COL: PixelColor,
1011{
1012 /// Draws a debug border around the UI bounds.
1013 ///
1014 /// This method draws a 1-pixel wide border around the entire UI area
1015 /// using the specified color. This is useful for debugging UI layout.
1016 ///
1017 /// # Arguments
1018 ///
1019 /// * `color` - The color to use for the border.
1020 ///
1021 /// # Returns
1022 ///
1023 /// `Ok(())` if drawing succeeds, `Err(GuiError::DrawError)` otherwise.
1024 pub fn draw_bounds_debug(&mut self, color: COL) -> GuiResult<()> {
1025 use embedded_graphics::primitives::{PrimitiveStyleBuilder, StyledDrawable};
1026
1027 let bounds = self.bounds;
1028 bounds
1029 .draw_styled(
1030 &PrimitiveStyleBuilder::new()
1031 .stroke_color(color)
1032 .stroke_width(1)
1033 .build(),
1034 &mut self.painter,
1035 )
1036 .map_err(|_| GuiError::DrawError)
1037 }
1038
1039 /// Enables debug drawing for widget bounds.
1040 ///
1041 /// This method sets a color that will be used to draw borders around
1042 /// widget areas when they are cleared. This is useful for visualizing
1043 /// widget boundaries during development.
1044 ///
1045 /// # Arguments
1046 ///
1047 /// * `color` - The color to use for widget bound boxes.
1048 pub fn draw_widget_bounds_debug(&mut self, color: COL) {
1049 self.debug_color = Some(color);
1050 }
1051
1052 /// Draws a debug border around a widget area.
1053 ///
1054 /// This method draws a 1-pixel wide border around the specified area
1055 /// if debug drawing is enabled. This is called automatically when
1056 /// clearing widget areas.
1057 ///
1058 /// # Arguments
1059 ///
1060 /// * `area` - The rectangle to draw a border around.
1061 pub fn draw_widget_bound_box(&mut self, area: &Rectangle) {
1062 if let Some(debug_color) = self.debug_color {
1063 use embedded_graphics::primitives::{PrimitiveStyleBuilder, StyledDrawable};
1064
1065 area.draw_styled(
1066 &PrimitiveStyleBuilder::new()
1067 .stroke_color(debug_color)
1068 .stroke_width(1)
1069 .build(),
1070 &mut self.painter,
1071 )
1072 .ok();
1073 }
1074 }
1075}