Skip to main content

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 = &region.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}