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