ove/
lvgl.rs

1// Copyright (C) 2026 Kamil Lulko <kamil.lulko@gmail.com>
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4//
5// This file is part of oveRTOS.
6
7//! Safe LVGL v9 wrappers for the oveRTOS Rust SDK.
8//!
9//! Provides idiomatic Rust bindings matching the C++ `ove::lvgl` wrapper:
10//!
11//! - **Zero-cost abstractions** — every widget is `Copy` + pointer-sized
12//! - **Fluent API** — method chaining via `self -> Self` on `Copy` types
13//! - **Trait composition** — `Layout`, `Styleable`, `EventTarget` blanket-impl
14//!   on anything implementing `Widget`, replacing C++ CRTP mixins
15//! - **RAII** — `LvglGuard` for lock/unlock, `Style` with `Drop`
16//! - **`no_std` compatible** — no allocator needed
17
18use crate::bindings;
19use crate::error::{Error, Result};
20
21// =========================================================================
22//  Constants
23// =========================================================================
24
25/// LVGL alignment constant: center the widget relative to its parent.
26pub const ALIGN_CENTER: u8 = 9;
27/// LVGL alignment constant: align to the top-center of the parent.
28pub const ALIGN_TOP_MID: u8 = 2;
29/// LVGL alignment constant: align to the top-left of the parent.
30pub const ALIGN_TOP_LEFT: u8 = 1;
31
32/// LVGL style selector for the main (background) part of a widget.
33pub const PART_MAIN: u32 = 0x000000;
34/// LVGL style selector for the indicator part (e.g. bar fill, checkbox mark).
35pub const PART_INDICATOR: u32 = 0x010000;
36
37/// LVGL v9 `LV_SIZE_CONTENT` — sets widget to size-to-content mode.
38/// Computed from: `LV_COORD_SET_SPEC(LV_COORD_MAX)` where
39/// `LV_COORD_TYPE_SHIFT = 29`, giving `((1<<29)-1) | (1<<29)`.
40pub const SIZE_CONTENT: i32 = 0x3FFF_FFFF;
41
42// =========================================================================
43//  Color
44// =========================================================================
45
46/// RGB888 color matching `lv_color_t { blue, green, red }` layout.
47#[repr(C)]
48#[derive(Clone, Copy)]
49pub struct Color {
50    pub blue: u8,
51    pub green: u8,
52    pub red: u8,
53}
54
55impl Color {
56    /// Construct a color from red, green, blue components (0–255 each).
57    pub const fn make(r: u8, g: u8, b: u8) -> Self {
58        Self {
59            blue: b,
60            green: g,
61            red: r,
62        }
63    }
64
65    /// Return pure white (R=255, G=255, B=255).
66    pub const fn white() -> Self {
67        Self::make(255, 255, 255)
68    }
69
70    /// Return pure black (R=0, G=0, B=0).
71    pub const fn black() -> Self {
72        Self::make(0, 0, 0)
73    }
74
75    /// Construct a color from a packed 24-bit RGB hex value (e.g. `0xFF8800`).
76    pub fn hex(hex: u32) -> Self {
77        Self::make(
78            ((hex >> 16) & 0xFF) as u8,
79            ((hex >> 8) & 0xFF) as u8,
80            (hex & 0xFF) as u8,
81        )
82    }
83
84    /// Return the main (500-shade) color of an LVGL palette family.
85    ///
86    /// `p` must be one of the `PALETTE_*` constants (e.g. [`PALETTE_BLUE`]).
87    pub fn palette_main(p: u32) -> Self {
88        unsafe {
89            let c = bindings::lv_palette_main(p as _);
90            core::mem::transmute(c)
91        }
92    }
93
94    fn to_raw(self) -> bindings::lv_color_t {
95        unsafe { core::mem::transmute(self) }
96    }
97}
98
99/// LVGL palette index for the blue palette family (`LV_PALETTE_BLUE`).
100pub const PALETTE_BLUE: u32 = 6;
101
102// =========================================================================
103//  Font accessors
104// =========================================================================
105
106/// Safe pointer to `lv_font_montserrat_32`.
107pub fn font_montserrat_32() -> *const bindings::lv_font_t {
108    unsafe { &bindings::lv_font_montserrat_32 }
109}
110
111/// Safe pointer to `lv_font_montserrat_14`.
112pub fn font_montserrat_14() -> *const bindings::lv_font_t {
113    unsafe { &bindings::lv_font_montserrat_14 }
114}
115
116// =========================================================================
117//  Widget trait — core abstraction replacing C++ ObjectView
118// =========================================================================
119
120/// Core trait for all LVGL widget wrappers. Provides access to the raw
121/// `lv_obj_t` pointer. All higher-level traits (`Layout`, `Styleable`,
122/// `EventTarget`) are blanket-implemented for any type implementing `Widget`.
123///
124/// # Safety contract
125/// Implementors must ensure `raw()` returns a pointer obtained from LVGL.
126/// All access must happen under the LVGL lock.
127pub trait Widget: Copy {
128    /// Return the raw `lv_obj_t` pointer for this widget.
129    ///
130    /// # Safety
131    /// Must only be used while holding the LVGL lock (see [`lock`]).
132    fn raw(self) -> *mut bindings::lv_obj_t;
133}
134
135// =========================================================================
136//  Layout trait — fluent positioning/sizing (blanket impl on Widget)
137// =========================================================================
138
139/// Fluent positioning, sizing, and flag manipulation.
140/// Blanket-implemented for all `Widget` types.
141pub trait Layout: Widget + Sized {
142    /// Set both width and height of the widget in pixels.
143    fn size(self, w: i32, h: i32) -> Self {
144        unsafe { bindings::lv_obj_set_size(self.raw(), w, h) };
145        self
146    }
147
148    /// Set the width of the widget in pixels.
149    fn width(self, w: i32) -> Self {
150        unsafe { bindings::lv_obj_set_width(self.raw(), w) };
151        self
152    }
153
154    /// Set the height of the widget in pixels.
155    fn height(self, h: i32) -> Self {
156        unsafe { bindings::lv_obj_set_height(self.raw(), h) };
157        self
158    }
159
160    /// Set the position of the widget relative to its parent's top-left corner.
161    fn pos(self, x: i32, y: i32) -> Self {
162        unsafe { bindings::lv_obj_set_pos(self.raw(), x, y) };
163        self
164    }
165
166    /// Center the widget within its parent.
167    fn center(self) -> Self {
168        unsafe { bindings::lv_obj_center(self.raw()) };
169        self
170    }
171
172    /// Align the widget using an LVGL alignment constant and pixel offsets.
173    ///
174    /// `a` must be one of the `ALIGN_*` constants (e.g. [`ALIGN_CENTER`]).
175    fn align(self, a: u8, x_ofs: i32, y_ofs: i32) -> Self {
176        unsafe { bindings::lv_obj_align(self.raw(), a as _, x_ofs, y_ofs) };
177        self
178    }
179
180    /// Hide the widget by setting `LV_OBJ_FLAG_HIDDEN`.
181    fn hide(self) -> Self {
182        unsafe { bindings::lv_obj_add_flag(self.raw(), bindings::LV_OBJ_FLAG_HIDDEN) };
183        self
184    }
185
186    /// Make the widget visible by removing `LV_OBJ_FLAG_HIDDEN`.
187    fn show(self) -> Self {
188        unsafe { bindings::lv_obj_remove_flag(self.raw(), bindings::LV_OBJ_FLAG_HIDDEN) };
189        self
190    }
191
192    /// Show or hide the widget. Equivalent to calling [`show`](Layout::show) or [`hide`](Layout::hide).
193    fn visible(self, v: bool) -> Self {
194        if v { self.show() } else { self.hide() }
195    }
196
197    /// Add one or more LVGL object flags (bitwise OR of `LV_OBJ_FLAG_*` values).
198    fn add_flag(self, f: u32) -> Self {
199        unsafe { bindings::lv_obj_add_flag(self.raw(), f) };
200        self
201    }
202
203    /// Remove one or more LVGL object flags.
204    fn remove_flag(self, f: u32) -> Self {
205        unsafe { bindings::lv_obj_remove_flag(self.raw(), f) };
206        self
207    }
208
209    /// Add one or more LVGL object state bits (e.g. `LV_STATE_CHECKED`).
210    fn add_state(self, s: u32) -> Self {
211        unsafe { bindings::lv_obj_add_state(self.raw(), s as _) };
212        self
213    }
214
215    /// Remove one or more LVGL object state bits.
216    fn remove_state(self, s: u32) -> Self {
217        unsafe { bindings::lv_obj_remove_state(self.raw(), s as _) };
218        self
219    }
220
221    /// Enable or disable click events on the widget.
222    fn clickable(self, on: bool) -> Self {
223        if on {
224            self.add_flag(bindings::LV_OBJ_FLAG_CLICKABLE)
225        } else {
226            self.remove_flag(bindings::LV_OBJ_FLAG_CLICKABLE)
227        }
228    }
229}
230
231impl<T: Widget> Layout for T {}
232
233// =========================================================================
234//  Styleable trait — fluent inline style setters (blanket impl on Widget)
235// =========================================================================
236
237/// Fluent inline style setters. Applied to `LV_PART_MAIN` by default.
238/// Blanket-implemented for all `Widget` types.
239pub trait Styleable: Widget + Sized {
240    /// Set the background color of the widget's main part.
241    fn bg_color(self, c: Color) -> Self {
242        unsafe { bindings::lv_obj_set_style_bg_color(self.raw(), c.to_raw(), PART_MAIN) };
243        self
244    }
245
246    /// Set the background opacity of the widget's main part (0 = transparent, 255 = opaque).
247    fn bg_opa(self, opa: u8) -> Self {
248        unsafe { bindings::lv_obj_set_style_bg_opa(self.raw(), opa, PART_MAIN) };
249        self
250    }
251
252    /// Set the border color of the widget's main part.
253    fn border_color(self, c: Color) -> Self {
254        unsafe { bindings::lv_obj_set_style_border_color(self.raw(), c.to_raw(), PART_MAIN) };
255        self
256    }
257
258    /// Set the border width in pixels.
259    fn border_width(self, w: i32) -> Self {
260        unsafe { bindings::lv_obj_set_style_border_width(self.raw(), w, PART_MAIN) };
261        self
262    }
263
264    /// Set the corner radius in pixels (use `LV_RADIUS_CIRCLE` for a pill shape).
265    fn radius(self, r: i32) -> Self {
266        unsafe { bindings::lv_obj_set_style_radius(self.raw(), r, PART_MAIN) };
267        self
268    }
269
270    /// Set equal padding on all four sides.
271    fn pad_all(self, p: i32) -> Self {
272        unsafe {
273            let r = self.raw();
274            bindings::lv_obj_set_style_pad_left(r, p, PART_MAIN);
275            bindings::lv_obj_set_style_pad_right(r, p, PART_MAIN);
276            bindings::lv_obj_set_style_pad_top(r, p, PART_MAIN);
277            bindings::lv_obj_set_style_pad_bottom(r, p, PART_MAIN);
278        }
279        self
280    }
281
282    /// Set equal left and right (horizontal) padding.
283    fn pad_hor(self, p: i32) -> Self {
284        unsafe {
285            let r = self.raw();
286            bindings::lv_obj_set_style_pad_left(r, p, PART_MAIN);
287            bindings::lv_obj_set_style_pad_right(r, p, PART_MAIN);
288        }
289        self
290    }
291
292    /// Set equal top and bottom (vertical) padding.
293    fn pad_ver(self, p: i32) -> Self {
294        unsafe {
295            let r = self.raw();
296            bindings::lv_obj_set_style_pad_top(r, p, PART_MAIN);
297            bindings::lv_obj_set_style_pad_bottom(r, p, PART_MAIN);
298        }
299        self
300    }
301
302    /// Set the row and column gap between flex children.
303    fn pad_gap(self, g: i32) -> Self {
304        unsafe {
305            let r = self.raw();
306            bindings::lv_obj_set_style_pad_row(r, g, PART_MAIN);
307            bindings::lv_obj_set_style_pad_column(r, g, PART_MAIN);
308        }
309        self
310    }
311
312    /// Set the text color for label-like widgets.
313    fn text_color(self, c: Color) -> Self {
314        unsafe { bindings::lv_obj_set_style_text_color(self.raw(), c.to_raw(), PART_MAIN) };
315        self
316    }
317
318    /// Set the text font. Use [`font_montserrat_32`] or [`font_montserrat_14`] for built-ins.
319    fn text_font(self, f: *const bindings::lv_font_t) -> Self {
320        unsafe { bindings::lv_obj_set_style_text_font(self.raw(), f, PART_MAIN) };
321        self
322    }
323
324    /// Apply a reusable [`Style`] to the given style selector (part + state bitmask).
325    fn add_style(self, style: &mut Style, selector: u32) -> Self {
326        unsafe { bindings::lv_obj_add_style(self.raw(), style.as_mut_ptr(), selector) };
327        self
328    }
329}
330
331impl<T: Widget> Styleable for T {}
332
333// =========================================================================
334//  EventTarget trait — type-safe event callbacks (blanket impl on Widget)
335// =========================================================================
336
337/// Event callback registration. Uses `fn` pointers for `no_std` compatibility.
338/// Blanket-implemented for all `Widget` types.
339pub trait EventTarget: Widget + Sized {
340    /// Register a callback for an arbitrary LVGL event code.
341    ///
342    /// `code` is any `LV_EVENT_*` constant. `user_data` is passed through to `cb` unchanged.
343    fn on(self, code: bindings::lv_event_code_t, cb: bindings::lv_event_cb_t, user_data: *mut core::ffi::c_void) -> Self {
344        unsafe { bindings::lv_obj_add_event_cb(self.raw(), cb, code, user_data) };
345        self
346    }
347
348    /// Register a callback for click (`LV_EVENT_CLICKED`) events.
349    fn on_click(self, cb: bindings::lv_event_cb_t, user_data: *mut core::ffi::c_void) -> Self {
350        self.on(bindings::LV_EVENT_CLICKED, cb, user_data)
351    }
352
353    /// Register a callback for value-changed (`LV_EVENT_VALUE_CHANGED`) events.
354    fn on_value_changed(self, cb: bindings::lv_event_cb_t, user_data: *mut core::ffi::c_void) -> Self {
355        self.on(bindings::LV_EVENT_VALUE_CHANGED, cb, user_data)
356    }
357}
358
359impl<T: Widget> EventTarget for T {}
360
361// =========================================================================
362//  Obj — base widget wrapper
363// =========================================================================
364
365/// Non-owning handle to an LVGL object (`lv_obj_t *`).
366///
367/// LVGL manages object lifetimes (parent owns children). No `Drop`.
368/// `Copy` enables fluent method chaining (`self -> Self`).
369#[derive(Clone, Copy)]
370pub struct Obj {
371    raw: *mut bindings::lv_obj_t,
372}
373
374impl Obj {
375    /// Wrap a raw LVGL object pointer.
376    ///
377    /// # Safety
378    /// The pointer must be valid and obtained from an LVGL function.
379    pub unsafe fn from_raw(raw: *mut bindings::lv_obj_t) -> Self {
380        Self { raw }
381    }
382
383    /// Get the raw pointer (for passing to FFI).
384    pub fn as_raw(self) -> *mut bindings::lv_obj_t {
385        self.raw
386    }
387
388    /// Create a plain container object.
389    pub fn create(parent: Obj) -> Self {
390        let raw = unsafe { bindings::lv_obj_create(parent.raw) };
391        Self { raw }
392    }
393
394    /// Delete this object and all its children.
395    pub fn del(self) {
396        unsafe { bindings::lv_obj_delete(self.raw) };
397    }
398
399    /// Delete all children of this object.
400    pub fn clean(self) {
401        unsafe { bindings::lv_obj_clean(self.raw) };
402    }
403
404    /// Get parent object.
405    pub fn parent(self) -> Self {
406        Self {
407            raw: unsafe { bindings::lv_obj_get_parent(self.raw) },
408        }
409    }
410
411    /// Get number of children.
412    pub fn child_count(self) -> u32 {
413        unsafe { bindings::lv_obj_get_child_count(self.raw) }
414    }
415
416    /// Get current width.
417    pub fn get_width(self) -> i32 {
418        unsafe { bindings::lv_obj_get_width(self.raw) }
419    }
420
421    /// Get current height.
422    pub fn get_height(self) -> i32 {
423        unsafe { bindings::lv_obj_get_height(self.raw) }
424    }
425
426    /// Set user data pointer.
427    pub fn set_user_data(self, data: *mut core::ffi::c_void) -> Self {
428        unsafe { bindings::lv_obj_set_user_data(self.raw, data) };
429        self
430    }
431
432    /// Get user data pointer.
433    pub fn get_user_data(self) -> *mut core::ffi::c_void {
434        unsafe { bindings::lv_obj_get_user_data(self.raw) }
435    }
436
437    // Legacy API preserved for backward compatibility
438    /// Set the size of the widget (legacy, non-fluent variant).
439    pub fn set_size(self, w: i32, h: i32) {
440        unsafe { bindings::lv_obj_set_size(self.raw, w, h) };
441    }
442
443    /// Set the position of the widget (legacy, non-fluent variant).
444    pub fn set_pos(self, x: i32, y: i32) {
445        unsafe { bindings::lv_obj_set_pos(self.raw, x, y) };
446    }
447
448    /// Set the background color for the given selector (legacy, non-fluent variant).
449    pub fn set_style_bg_color(self, color: Color, selector: u32) {
450        unsafe { bindings::lv_obj_set_style_bg_color(self.raw, color.to_raw(), selector) };
451    }
452
453    /// Set the text color for the given selector (legacy, non-fluent variant).
454    pub fn set_style_text_color(self, color: Color, selector: u32) {
455        unsafe { bindings::lv_obj_set_style_text_color(self.raw, color.to_raw(), selector) };
456    }
457
458    /// Set the corner radius for the given selector (legacy, non-fluent variant).
459    pub fn set_style_radius(self, radius: i32, selector: u32) {
460        unsafe { bindings::lv_obj_set_style_radius(self.raw, radius, selector) };
461    }
462
463    /// Set the text font for the given selector (legacy, non-fluent variant).
464    pub fn set_style_text_font(self, font: *const bindings::lv_font_t, selector: u32) {
465        unsafe { bindings::lv_obj_set_style_text_font(self.raw, font, selector) };
466    }
467}
468
469impl Widget for Obj {
470    fn raw(self) -> *mut bindings::lv_obj_t {
471        self.raw
472    }
473}
474
475// =========================================================================
476//  Label
477// =========================================================================
478
479/// LVGL label widget.
480#[derive(Clone, Copy)]
481pub struct Label {
482    raw: *mut bindings::lv_obj_t,
483}
484
485impl Label {
486    /// Create a new label as a child of `parent`.
487    pub fn create(parent: impl Widget) -> Self {
488        let raw = unsafe { bindings::lv_label_create(parent.raw()) };
489        Self { raw }
490    }
491
492    /// Set text from a null-terminated byte slice.
493    pub fn set_text(self, text: &[u8]) {
494        unsafe { bindings::lv_label_set_text(self.raw, text.as_ptr() as *const _) };
495    }
496
497    /// Fluent: set text (copies string).
498    pub fn text(self, txt: &[u8]) -> Self {
499        unsafe { bindings::lv_label_set_text(self.raw, txt.as_ptr() as *const _) };
500        self
501    }
502
503    /// Fluent: set static text (pointer must remain valid).
504    pub fn text_static(self, txt: &'static [u8]) -> Self {
505        unsafe { bindings::lv_label_set_text_static(self.raw, txt.as_ptr() as *const _) };
506        self
507    }
508
509    /// Fluent: set font shorthand.
510    pub fn font(self, f: *const bindings::lv_font_t) -> Self {
511        unsafe { bindings::lv_obj_set_style_text_font(self.raw, f, PART_MAIN) };
512        self
513    }
514
515    /// Fluent: set text color shorthand.
516    pub fn color(self, c: Color) -> Self {
517        unsafe { bindings::lv_obj_set_style_text_color(self.raw, c.to_raw(), PART_MAIN) };
518        self
519    }
520}
521
522impl Widget for Label {
523    fn raw(self) -> *mut bindings::lv_obj_t {
524        self.raw
525    }
526}
527
528impl core::ops::Deref for Label {
529    type Target = Obj;
530    fn deref(&self) -> &Obj {
531        // SAFETY: Label and Obj have identical layout (single *mut lv_obj_t)
532        unsafe { &*(self as *const Label as *const Obj) }
533    }
534}
535
536// =========================================================================
537//  Bar
538// =========================================================================
539
540/// LVGL bar widget.
541#[derive(Clone, Copy)]
542pub struct Bar {
543    raw: *mut bindings::lv_obj_t,
544}
545
546impl Bar {
547    /// Create a new bar widget as a child of `parent`.
548    pub fn create(parent: impl Widget) -> Self {
549        let raw = unsafe { bindings::lv_bar_create(parent.raw()) };
550        Self { raw }
551    }
552
553    /// Set bar value. `anim`: false = instant, true = animate.
554    pub fn set_value(self, value: i32, anim: bool) {
555        unsafe {
556            #[allow(clippy::unnecessary_cast)]
557            bindings::lv_bar_set_value(self.raw, value, anim as _);
558        }
559    }
560
561    /// Set bar range.
562    pub fn set_range(self, min: i32, max: i32) {
563        unsafe { bindings::lv_bar_set_range(self.raw, min, max) };
564    }
565
566    /// Fluent: set value with animation.
567    pub fn value(self, val: i32) -> Self {
568        unsafe {
569            #[allow(clippy::unnecessary_cast)]
570            bindings::lv_bar_set_value(self.raw, val, true as _);
571        }
572        self
573    }
574
575    /// Fluent: set value with animation control.
576    pub fn value_anim(self, val: i32, anim: bool) -> Self {
577        unsafe {
578            #[allow(clippy::unnecessary_cast)]
579            bindings::lv_bar_set_value(self.raw, val, anim as _);
580        }
581        self
582    }
583
584    /// Fluent: set range.
585    pub fn range(self, min: i32, max: i32) -> Self {
586        unsafe { bindings::lv_bar_set_range(self.raw, min, max) };
587        self
588    }
589
590    /// Fluent: set indicator color.
591    pub fn indicator_color(self, c: Color) -> Self {
592        unsafe { bindings::lv_obj_set_style_bg_color(self.raw, c.to_raw(), PART_INDICATOR) };
593        self
594    }
595
596    /// Fluent: set bar background color.
597    pub fn bar_color(self, c: Color) -> Self {
598        unsafe { bindings::lv_obj_set_style_bg_color(self.raw, c.to_raw(), PART_MAIN) };
599        self
600    }
601}
602
603impl Widget for Bar {
604    fn raw(self) -> *mut bindings::lv_obj_t {
605        self.raw
606    }
607}
608
609impl core::ops::Deref for Bar {
610    type Target = Obj;
611    fn deref(&self) -> &Obj {
612        unsafe { &*(self as *const Bar as *const Obj) }
613    }
614}
615
616// =========================================================================
617//  Box — container without scrollbar
618// =========================================================================
619
620/// Plain container object with scrolling disabled.
621#[derive(Clone, Copy)]
622pub struct Box {
623    raw: *mut bindings::lv_obj_t,
624}
625
626impl Box {
627    /// Create a plain container widget (scrolling disabled) as a child of `parent`.
628    pub fn create(parent: impl Widget) -> Self {
629        let raw = unsafe {
630            let obj = bindings::lv_obj_create(parent.raw());
631            bindings::lv_obj_remove_flag(obj, bindings::LV_OBJ_FLAG_SCROLLABLE);
632            obj
633        };
634        Self { raw }
635    }
636}
637
638impl Widget for Box {
639    fn raw(self) -> *mut bindings::lv_obj_t {
640        self.raw
641    }
642}
643
644impl core::ops::Deref for Box {
645    type Target = Obj;
646    fn deref(&self) -> &Obj {
647        unsafe { &*(self as *const Box as *const Obj) }
648    }
649}
650
651// =========================================================================
652//  Layout helpers — vbox / hbox
653// =========================================================================
654
655/// Create a vertical flex container.
656pub fn vbox(parent: impl Widget) -> Box {
657    let b = Box::create(parent);
658    unsafe {
659        bindings::lv_obj_set_flex_flow(b.raw, bindings::LV_FLEX_FLOW_COLUMN);
660        bindings::lv_obj_set_size(b.raw, SIZE_CONTENT, SIZE_CONTENT);
661    }
662    b
663}
664
665/// Create a horizontal flex container.
666pub fn hbox(parent: impl Widget) -> Box {
667    let b = Box::create(parent);
668    unsafe {
669        bindings::lv_obj_set_flex_flow(b.raw, bindings::LV_FLEX_FLOW_ROW);
670        bindings::lv_obj_set_size(b.raw, SIZE_CONTENT, SIZE_CONTENT);
671    }
672    b
673}
674
675// =========================================================================
676//  Style — RAII style object
677// =========================================================================
678
679/// RAII wrapper around `lv_style_t`. Calls `lv_style_reset` on drop.
680pub struct Style {
681    inner: bindings::lv_style_t,
682}
683
684impl Style {
685    /// Create and initialize a new LVGL style object.
686    pub fn new() -> Self {
687        let mut s = Self {
688            inner: unsafe { core::mem::zeroed() },
689        };
690        unsafe { bindings::lv_style_init(&mut s.inner) };
691        s
692    }
693
694    /// Return a raw mutable pointer for use with [`Styleable::add_style`].
695    pub fn as_mut_ptr(&mut self) -> *mut bindings::lv_style_t {
696        &mut self.inner
697    }
698
699    /// Set the background color in this style.
700    pub fn bg_color(mut self, c: Color) -> Self {
701        unsafe { bindings::lv_style_set_bg_color(&mut self.inner, c.to_raw()) };
702        self
703    }
704
705    /// Set the background opacity in this style (0 = transparent, 255 = opaque).
706    pub fn bg_opa(mut self, opa: u8) -> Self {
707        unsafe { bindings::lv_style_set_bg_opa(&mut self.inner, opa) };
708        self
709    }
710
711    /// Set the corner radius in this style.
712    pub fn radius(mut self, r: i32) -> Self {
713        unsafe { bindings::lv_style_set_radius(&mut self.inner, r) };
714        self
715    }
716
717    /// Set the border color in this style.
718    pub fn border_color(mut self, c: Color) -> Self {
719        unsafe { bindings::lv_style_set_border_color(&mut self.inner, c.to_raw()) };
720        self
721    }
722
723    /// Set the border width in this style.
724    pub fn border_width(mut self, w: i32) -> Self {
725        unsafe { bindings::lv_style_set_border_width(&mut self.inner, w) };
726        self
727    }
728
729    /// Set equal padding on all four sides in this style.
730    pub fn pad_all(mut self, p: i32) -> Self {
731        unsafe {
732            let s = &mut self.inner;
733            bindings::lv_style_set_pad_left(s, p);
734            bindings::lv_style_set_pad_right(s, p);
735            bindings::lv_style_set_pad_top(s, p);
736            bindings::lv_style_set_pad_bottom(s, p);
737        }
738        self
739    }
740
741    /// Set the text color in this style.
742    pub fn text_color(mut self, c: Color) -> Self {
743        unsafe { bindings::lv_style_set_text_color(&mut self.inner, c.to_raw()) };
744        self
745    }
746
747    /// Set the text font in this style.
748    pub fn text_font(mut self, f: *const bindings::lv_font_t) -> Self {
749        unsafe { bindings::lv_style_set_text_font(&mut self.inner, f) };
750        self
751    }
752}
753
754impl Drop for Style {
755    fn drop(&mut self) {
756        unsafe { bindings::lv_style_reset(&mut self.inner) };
757    }
758}
759
760// =========================================================================
761//  Send + Sync (same contract as C/C++: all access under LVGL lock)
762// =========================================================================
763
764unsafe impl Send for Obj {}
765unsafe impl Sync for Obj {}
766unsafe impl Send for Label {}
767unsafe impl Sync for Label {}
768unsafe impl Send for Bar {}
769unsafe impl Sync for Bar {}
770unsafe impl Send for Box {}
771unsafe impl Sync for Box {}
772
773// =========================================================================
774//  LvglGuard — RAII lock/unlock
775// =========================================================================
776
777/// RAII guard for the LVGL mutex. `Drop` calls `ove_lvgl_unlock()`.
778pub struct LvglGuard(());
779
780impl Drop for LvglGuard {
781    fn drop(&mut self) {
782        unsafe { bindings::ove_lvgl_unlock() };
783    }
784}
785
786// =========================================================================
787//  Module-level functions
788// =========================================================================
789
790/// Initialize LVGL via `ove_lvgl_init()`.
791pub fn init() -> Result<()> {
792    let ret = unsafe { bindings::ove_lvgl_init() };
793    Error::from_code(ret)
794}
795
796/// Feed LVGL tick counter.
797pub fn tick(ms: u32) {
798    unsafe { bindings::ove_lvgl_tick(ms) };
799}
800
801/// Run the LVGL task handler.
802pub fn handler() {
803    unsafe { bindings::ove_lvgl_handler() };
804}
805
806/// Acquire the LVGL mutex and return an RAII guard.
807pub fn lock() -> LvglGuard {
808    unsafe { bindings::ove_lvgl_lock() };
809    LvglGuard(())
810}
811
812/// Get the currently active screen.
813pub fn screen_active() -> Obj {
814    let raw = unsafe { bindings::lv_screen_active() };
815    unsafe { Obj::from_raw(raw) }
816}