### Run Host Example Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Run an example application on the host, which will open an interactive SDL window. ```sh ./run_host.sh getting_started1 ``` -------------------------------- ### Run Host Examples Source: https://github.com/emobotics-dev/oxivgl/blob/master/README.md Execute LVGL examples on the host machine using SDL2 for interactive display or headless screenshots. Use the `-s` flag for screenshots. ```sh ./run_host.sh getting_started1 # interactive SDL2 window ``` ```sh ./run_host.sh -s getting_started1 # headless screenshot ``` ```sh ./run_host.sh -s # screenshot all 171 examples ``` -------------------------------- ### async fn run_app Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Initializes the LVGL environment and starts the main application loop with navigation support. ```APIDOC ## async fn run_app ### Description Initializes LVGL, creates a navigator with an initial screen, and runs the render loop. This is an embassy async task that should be spawned alongside other application tasks. ### Parameters - **w** (i32) - Width of the display. - **h** (i32) - Height of the display. - **bufs** (&'static mut LvglBuffers) - Static mutable reference to LVGL buffers. - **initial** (impl View) - The initial screen to be pushed as the root view. - **wake** (impl Fn() -> Fut) - An async closure called each render tick to race against the inter-tick timer for low-latency updates. ### Returns - **!** (Never) - This function loops forever and never returns. ``` -------------------------------- ### Capture Screenshot of Host Example Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Capture a screenshot of a host example in headless mode, without opening a visible window. ```sh ./run_host.sh -s getting_started1 ``` -------------------------------- ### Create and Start Animations with Anim Builder Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Demonstrates how to create and start animations for widget properties like position. Animations are tied to the target widget's lifetime and copied by the Anim builder on start. ```rust use oxivgl::anim::{Anim, anim_path_ease_in, anim_path_overshoot, anim_set_x, ANIM_REPEAT_INFINITE}; use oxivgl::enums::{EventCode, ObjState}; use oxivgl::event::Event; use oxivgl::view::{NavAction, View}; use oxivgl::widgets::{Label, Obj, Switch, WidgetError}; struct AnimExample { label: Option>, sw: Option>, } impl View for AnimExample { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { let label = Label::new(container)?; label.text("Hello animations!").pos(100, 10); let sw = Switch::new(container)?; sw.center().add_state(ObjState::CHECKED).bubble_events(); self.label = Some(label); self.sw = Some(sw); Ok(()) } fn on_event(&mut self, event: &Event) -> NavAction { if let (Some(sw), Some(label)) = (&self.sw, &self.label) { if event.matches(sw, EventCode::VALUE_CHANGED) { let checked = sw.has_state(ObjState::CHECKED); let mut anim = Anim::new(); anim.set_var(label) .set_duration(500) .set_exec_cb(Some(anim_set_x)); if checked { anim.set_values(label.get_x(), 100) .set_path_cb(Some(anim_path_overshoot)); } else { anim.set_values(label.get_x(), -label.get_width()) .set_path_cb(Some(anim_path_ease_in)); } anim.start(); } } NavAction::None } fn update(&mut self) -> Result { Ok(NavAction::None) } } ``` -------------------------------- ### Flash Example to ESP32 Source: https://github.com/emobotics-dev/oxivgl/blob/master/README.md Flash an LVGL example to an ESP32 development board. Ensure the `fire27.sh` script is available and configured for your target. ```sh ./run_fire27.sh event_trickle # flash to ESP32 ``` -------------------------------- ### Flash ESP32 Example Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Flash an example application to a connected ESP32 board. Requires the Xtensa toolchain and a connected board. ```sh ./run_fire27.sh getting_started1 ``` -------------------------------- ### Capture All Screenshots Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Capture screenshots for all available host examples in headless mode. ```sh ./run_host.sh -s ``` -------------------------------- ### Real Application Example with Channels Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Illustrates a multi-task application using an event channel and a wake signal. The `DashboardView` consumes events, and producer tasks signal the wake signal after sending data. ```rust // Wake signal — shared between producer tasks and run_app static WAKE: Signal = Signal::new(); // Event channel (one of potentially many data paths) static EVENT_CHANNEL: Channel = Channel::new(); // View that consumes events struct DashboardView { voltage_label: Option>, } impl View for DashboardView { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { self.voltage_label = Some(Label::new(container)?); Ok(()) } fn update(&mut self) -> Result { // Drain the channel — may have 0..N events since last tick while let Ok(evt) = EVENT_CHANNEL.try_recv() { match evt { AppEvent::ButtonBack => return Ok(NavAction::Pop(None)), AppEvent::SensorReading(v) => { if let Some(lbl) = &self.voltage_label { lbl.set_text_fmt(fmt dinâmica("{:.1}V", v)); } } AppEvent::BleDisconnected => { return Ok(NavAction::modal(ReconnectDialog::new())); } _ => {} } } Ok(NavAction::None) } } // Button task — proper debouncing, signals wake after send #[embassy_executor::task] async fn button_task(back_pin: Input<'static>) { loop { back_pin.wait_for_falling_edge().await; Timer::after(Duration::from_millis(50)).await; // debounce if back_pin.is_low() { EVENT_CHANNEL.send(AppEvent::ButtonBack).await; WAKE.signal(()); // wake run_app immediately } } } // Entry point — async closure wakes on signal #[embassy_executor::task] async fn ui_task(bufs: &'static mut LvglBuffers) -> ! { run_app(320, 240, bufs, DashboardView::default(), async || WAKE.wait().await, ).await } ``` -------------------------------- ### Real Application Example with Shared State Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Demonstrates an alternative pattern using shared state protected by a mutex. Sensor tasks update the shared state and signal a wake event. ```rust static APP_STATE: Mutex> = Mutex::new(RefCell::new(AppState::default())); impl View for DashboardView { fn update(&mut self) -> Result { let state = APP_STATE.lock(|cell| cell.borrow().clone()); if let Some(lbl) = &self.voltage_label { lbl.set_text_fmt(fmt dinâmica("{:.1}V", state.voltage)); } Ok(NavAction::None) } } // Sensor task — updates shared state, signals wake #[embassy_executor::task] async fn sensor_task(adc: Adc<'static>) { loop { let reading = adc.read().await; APP_STATE.lock(|cell| cell.borrow_mut().voltage = reading); WAKE.signal(()); } } ``` -------------------------------- ### Handling Navigation Events in Oxivgl Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Example of an `on_event` implementation for a view that navigates to a settings view upon a button click. This pattern is useful for views that trigger navigation actions. ```rust fn on_event(&mut self, event: &Event) -> NavAction { if event.matches(&self.settings_btn, EventCode::CLICKED) { return NavAction::push(SettingsView::default()); } NavAction::None } ``` -------------------------------- ### `example_main!` Macro Signature Change Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Illustrates the updated signature for the `example_main!` macro, which now accepts an initial screen expression. ```rust // Old: example_main!(MyView); // New: example_main!(MyExample::default()); ``` -------------------------------- ### Initialize and run LVGL application Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md This embassy async task initializes the display driver, sets the initial screen, and runs the main loop to process navigation and render updates. ```rust /// Run the LVGL application with navigation support. /// /// This is an embassy async task. Spawn it alongside your other /// application tasks. It initializes LVGL, pushes `initial` as the /// root screen, and loops forever: calling `update`, driving /// `lv_timer_handler`, and processing navigation actions. /// /// `wake` is an async closure called each render tick and raced against /// the inter-tick timer. If `wake` resolves before the timer, `run_app` /// breaks out of the timer loop early and runs `update()` immediately, /// reducing worst-case event-to-screen latency from ~33ms to /// near-instant. For timer-only operation, pass /// `async || core::future::pending().await`. /// /// Never returns. pub async fn run_app>( w: i32, h: i32, bufs: &'static mut LvglBuffers, initial: impl View, wake: impl Fn() -> Fut, ) -> ! { let driver = LvglDriver::init(w, h); unsafe { lvgl_disp_init(w, h, bufs) }; DISPLAY_READY.wait().await; let mut nav = Navigator::new(); nav.push(initial, None); const LVGL_TIMER_DELAY: u64 = LV_DEF_REFR_PERIOD as u64 / 4; loop { // Update active view — polls app state, returns NavAction let action = nav.active_view_mut() .update() .unwrap_or_else(|e| { warn!("view update: {:?}", e); NavAction::None }); // Update modal if present let modal_action = if let Some(modal) = nav.active_modal_mut() { modal.update() .unwrap_or_else(|e| { warn!("modal update: {:?}", e); NavAction::None }) } else { NavAction::None }; // Drive LVGL timers (processes widget events → on_event → NavAction) for _ in 0..4 { driver.timer_handler(); // Race the tick timer against the wake closure. If wake // resolves first, break out early to run update() sooner. let timer = Timer::after(Duration::from_millis(LVGL_TIMER_DELAY)); match select(timer, wake()).await { Either::First(()) => {}, // normal tick Either::Second(()) => break, // early wake → run update } } // Process navigation: on_event actions (collected during timer_handler) // take priority, then update actions. nav.process_pending_event_action(); if !nav.has_pending() { nav.process_action(action); } if !nav.has_pending() { nav.process_action(modal_action); } } } ``` -------------------------------- ### Define Anim struct with lifetime binding Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-memory-lifetime.md Uses a PhantomData marker to tie the animation lifetime to the target widget, ensuring the widget remains valid at start time. ```rust pub struct Anim<'w> { inner: lv_anim_t, _widget: PhantomData<&'w ()>, } impl<'w> Anim<'w> { pub fn set_var(&mut self, widget: &'w W) -> &mut Self; } ``` -------------------------------- ### Migrate View implementation Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Demonstrates the transition from the old View pattern to the evolved version, requiring Option-wrapped widgets and a container-based create method. ```rust // Current View: struct MyExample { label: Label<'static> } impl View for MyExample { fn create() -> Result { let scr = Screen::active().unwrap(); let label = Label::new(&scr)?; Ok(Self { label }) } fn update(&mut self) -> Result<(), WidgetError> { Ok(()) } } example_main!(MyExample); // Evolved View: #[derive(Default)] struct MyExample { label: Option> } impl View for MyExample { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { self.label = Some(Label::new(container)?); Ok(()) } } example_main!(MyExample::default()); ``` -------------------------------- ### Run Unit and Doc Tests Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Execute unit and documentation tests using the provided script. Ensure SDL_VIDEODRIVER is set to dummy for integration tests. ```sh ./run_tests.sh unit ``` -------------------------------- ### Handle Button Clicks with Event Bubbling Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Enable event bubbling on buttons to process interactions within the View::on_event method. This example demonstrates updating a label based on click counts. ```rust use oxivgl::{ enums::EventCode, event::Event, view::{NavAction, View}, widgets::{Align, Button, Label, Obj, WidgetError}, }; struct ClickCounter { btn: Option>, label: Option>, count: u32, } impl View for ClickCounter { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { let btn = Button::new(container)?; btn.size(120, 50).center(); btn.bubble_events(); // Enable event bubbling to View::on_event let label = Label::new(&btn)?; label.text("Click me!").center(); self.btn = Some(btn); self.label = Some(label); self.count = 0; Ok(()) } fn on_event(&mut self, event: &Event) -> NavAction { if let Some(ref btn) = self.btn { if event.matches(btn, EventCode::CLICKED) { self.count += 1; if let Some(ref label) = self.label { let mut buf = heapless::String::<12>::new(); let _ = core::fmt::Write::write_fmt(&mut buf, format_args!("{}", self.count)); label.text(&buf); } } } NavAction::None } fn update(&mut self) -> Result { Ok(NavAction::None) } } ``` -------------------------------- ### Enable LVGL Widget Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-widget-wrapper.md Enable the widget in the configuration file before rebuilding. ```c #define LV_USE_MYWIDGET 1 ``` -------------------------------- ### Check Documentation Coverage Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-example-porting.md Run this command to verify that all public items in the documentation have appropriate doc comments. ```bash RUSTDOCFLAGS="-W missing-docs" cargo doc --no-deps ``` -------------------------------- ### Create Arc Widgets Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Demonstrates creating simple arcs, pre-configured gauge rings, and symmetrical arcs. Ensure the container object is valid before creating arcs. ```rust use oxivgl::widgets::{Align, Arc, ArcMode, Obj, WidgetError}; fn create_arcs(container: &Obj<'static>) -> Result<(), WidgetError> { // Simple arc with physical value range let arc = Arc::new(container)?; arc.set_range(150.0) // 0-150 physical units (e.g., Amps) .set_value(75.0) // Set to 50% .size(150, 150) .align(Align::Center, 0, 0); // Pre-configured gauge ring (no knob, not interactive) let gauge = Arc::gauge_ring( container, 120, // diameter 10, // arc width 100.0, // range max 0x333333, // track color (dark gray) 0x00FF00, // indicator color (green) 150, // rotation (start angle) 240, // sweep (arc extent in degrees) )?; gauge.set_value(67.0); // Set to 67% // Arc with symmetrical mode (expands from center) let sym_arc = Arc::new(container)?; sym_arc.set_mode(ArcMode::Symmetrical) .set_range_raw(0, 100) .set_value_raw(50) .align(Align::BottomMid, 0, -20); Ok(()) } ``` -------------------------------- ### Check Host Build Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Use this command to check the host build without needing the ESP toolchain. ```sh cargo check ``` -------------------------------- ### Build and Check Host Code Source: https://github.com/emobotics-dev/oxivgl/blob/master/README.md Compile and check the Rust code for the host platform (Linux/macOS). Ensure the `LIBCLANG_PATH` environment variable is set correctly if clang is not in the default path. ```sh LIBCLANG_PATH=/usr/lib64 cargo +nightly check --target x86_64-unknown-linux-gnu ``` -------------------------------- ### Async Closure Wake Sources Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Shows various awaitable sources that can be used to wake the application loop, including signals, channels, GPIO pins, and timers. ```rust // Signal async || WAKE.signal.wait().await // Channel (wake when a message is available) async || { let _ = CHANNEL.receive().await; } // GPIO pin directly (simple cases without a separate button task) async || BACK_PIN.wait_for_falling_edge().await // Timer-only (no external wake — for examples) async || core::future::pending().await ``` -------------------------------- ### Run Integration Tests Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Execute integration tests. This requires the SDL_VIDEODRIVER environment variable to be set to 'dummy'. ```sh ./run_tests.sh int ``` -------------------------------- ### Create a basic UI view Source: https://github.com/emobotics-dev/oxivgl/blob/master/README.md Implements the View trait to define a UI layout with a background color and a centered label. ```rust use oxivgl::{ view::{NavAction, View}, widgets::{Align, Label, Obj, WidgetError}, }; #[derive(Default)] struct MyView { _label: Option>, } impl View for MyView { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { container.bg_color(0x003a57).bg_opa(255); let label = Label::new(container)?; label.text("Hello world").align(Align::Center, 0, 0); self._label = Some(label); Ok(()) } fn update(&mut self) -> Result { Ok(NavAction::None) } } oxivgl_examples_common::example_main!(MyView::default()); ``` -------------------------------- ### Create Slider Widgets Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Shows how to create basic, symmetrical, and range sliders. Use `set_range` and `set_value` for basic sliders, and `set_mode` for symmetrical or range sliders. ```rust use oxivgl::widgets::{Align, Obj, Slider, SliderMode, WidgetError}; fn create_sliders(container: &Obj<'static>) -> Result<(), WidgetError> { // Basic slider with custom range let slider = Slider::new(container)?; slider.set_range(0, 100) .set_value(50) .width(200) .align(Align::Center, 0, 0); // Symmetrical slider (fills from center) let sym = Slider::new(container)?; sym.set_mode(SliderMode::Symmetrical) .set_range(-100, 100) .set_value(0) .width(200) .align(Align::Center, 0, 50); // Range slider (two handles) let range = Slider::new(container)?; range.set_mode(SliderMode::Range) .set_range(0, 100) .set_start_value(25) .set_value(75) .width(200) .align(Align::Center, 0, 100); Ok(()) } ``` -------------------------------- ### Full-Screen Navigation Methods Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Methods for managing the view stack, including pushing, popping, and replacing views. ```APIDOC ## Navigator Full-Screen Navigation ### Description Methods to manipulate the navigation stack. Pushing a view preserves the state of the previous view while destroying its widgets, while popping restores the previous view. ### Methods - **push(view: impl View, anim: Option)**: Pushes a new view onto the stack. - **pop(anim: Option) -> Result<(), NavigationError>**: Pops the current view and returns to the previous one. - **replace(view: impl View, anim: Option)**: Replaces the current view without preserving it. - **depth() -> usize**: Returns the number of views on the stack. ``` -------------------------------- ### Build Styled Buttons Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Illustrates creating reusable styles using `StyleBuilder` and applying them to buttons. Styles can include gradients, borders, and color filters, and can be applied to specific widget states. ```rust use oxivgl::{ enums::{Opa, ObjState}, style::{ ColorFilter, GradDir, Palette, Selector, Style, StyleBuilder, darken_filter_cb, palette_lighten, palette_main, }, widgets::{Button, Label, Obj, WidgetError, RADIUS_MAX}, }; fn create_styled_buttons(container: &Obj<'static>) -> Result<(), WidgetError> { // Build a gradient button style let mut style_btn = StyleBuilder::new(); style_btn .radius(10) .bg_opa(Opa::COVER.0) .bg_color(palette_lighten(Palette::Grey, 3)) .bg_grad_color(palette_main(Palette::Grey)) .bg_grad_dir(GradDir::Ver) .border_color_hex(0x000000) .border_opa(Opa::OPA_20.0) .border_width(2) .text_color_hex(0x000000); let style_btn = style_btn.build(); // Build a pressed-state style with color filter let color_filter = ColorFilter::new(darken_filter_cb); let mut style_pressed = StyleBuilder::new(); style_pressed.color_filter(color_filter, Opa::OPA_20.0); let style_pressed = style_pressed.build(); // Build a red accent style let mut style_red = StyleBuilder::new(); style_red .bg_color(palette_main(Palette::Red)) .bg_grad_color(palette_lighten(Palette::Red, 3)); let style_red = style_red.build(); // Apply styles to buttons let btn1 = Button::new(container)?; btn1.remove_style_all().pos(10, 10).size(120, 50); btn1.add_style(&style_btn, Selector::DEFAULT); btn1.add_style(&style_pressed, ObjState::PRESSED); let btn2 = Button::new(container)?; btn2.remove_style_all().pos(10, 80).size(120, 50); btn2.add_style(&style_btn, Selector::DEFAULT); btn2.add_style(&style_red, Selector::DEFAULT); btn2.add_style(&style_pressed, ObjState::PRESSED); btn2.radius(RADIUS_MAX, Selector::DEFAULT); // Pill shape Ok(()) } ``` -------------------------------- ### Run All Tests Source: https://github.com/emobotics-dev/oxivgl/blob/master/CLAUDE.md Execute all available tests, including unit, integration, and memory leak detection tests. ```sh ./run_tests.sh all ``` -------------------------------- ### View Update and Event Handling Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Illustrates the methods within a view that return NavAction for navigation. ```APIDOC ## View Update and Event Handling ### Description Views interact with the navigation system by returning `NavAction` from their `update` and `on_event` methods. The `update` method is typically used for polling external events, while `on_event` handles direct user interactions. ### Methods #### `update()` - **Description**: Called periodically to update the view's state and check for external events. It can return a `NavAction` to trigger navigation. - **Signature**: `fn update(&mut self) -> Result;` #### `on_event()` - **Description**: Handles user input events and other direct interactions. It returns a `NavAction` if a navigation event occurs. - **Signature**: `fn on_event(&mut self, event: &Event) -> NavAction;` ### Navigation Triggers - **Widget events**: Such as button clicks, handled via `on_event`. - **External events**: Such as hardware button presses or BLE commands, typically handled via `update` by polling an event channel. ``` -------------------------------- ### Manage Full-Screen Navigation Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Methods for pushing, popping, and replacing views within the navigation stack. ```rust impl Navigator { /// Push a new view onto the stack. /// /// 1. Calls `will_hide()` on the current top view. /// 2. Destroys the current view's widget tree (`lv_obj_clean`). /// 3. Stores the current view (state preserved, widgets gone). /// 4. Calls `create(container)` on the new view. /// 5. Registers the event trampoline. /// 6. Calls `did_show()` on the new view. /// /// `anim` controls the screen transition animation. Pass `None` for /// an instant switch. pub fn push(&mut self, view: impl View, anim: Option); /// Pop the current view and return to the previous one. /// /// 1. Calls `will_hide()` on the current top view. /// 2. Destroys the current view's widget tree. /// 3. Drops the current view entirely (removed from stack). /// 4. Calls `create(container)` on the now-top view (rebuild widgets). /// 5. Registers the event trampoline. /// 6. Calls `did_show()` on the restored view. /// /// Returns `Err(NavigationError::StackEmpty)` if only one view /// remains (the root view cannot be popped). pub fn pop(&mut self, anim: Option) -> Result<(), NavigationError>; /// Replace the current view without preserving it on the stack. /// /// The current view is dropped (not preserved). The new view /// takes its place at the same stack depth. Use this for /// non-reversible transitions (e.g. login → home). pub fn replace(&mut self, view: impl View, anim: Option); /// Number of views on the stack. pub fn depth(&self) -> usize; } ``` -------------------------------- ### Widget Configuration Methods Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-memory-lifetime.md Methods for configuring widget properties that require static lifetime guarantees for memory safety. ```APIDOC ## Line::set_points ### Description Sets the points for a Line widget. The input array must be static or leaked memory. ### Parameters - **points** (&'static [lv_point_precise_t]) - Required - Array of points that must outlive the widget. ## Dropdown::set_symbol ### Description Sets the symbol string for a Dropdown widget. ### Parameters - **symbol** (&'static CStr) - Required - The symbol string which must outlive the widget. ## TransitionDsc::new ### Description Creates a new transition descriptor for style animations. ### Parameters - **props** (&'static [lv_style_prop_t]) - Required - Array of style properties that must remain stable. ``` -------------------------------- ### Manage Modal Navigation Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Methods for displaying and dismissing modal overlays on top of the current view. ```rust impl Navigator { /// Show a modal overlay on top of the current view. /// /// The current view's widget tree stays alive and visible /// underneath. The modal's widgets are created on `lv_layer_top()`. /// A full-size transparent click-absorbing backdrop is inserted /// automatically to prevent interaction with the view below. /// /// Only one modal can be active at a time. Calling `modal()` while /// a modal is already open replaces it. pub fn modal(&mut self, view: impl View); /// Dismiss the current modal overlay. /// /// Destroys the modal's widgets and the backdrop. The underlying /// view regains input focus. Returns `Err` if no modal is active. pub fn dismiss_modal(&mut self) -> Result<(), NavigationError>; /// Whether a modal is currently showing. pub fn has_modal(&self) -> bool; } ``` -------------------------------- ### Run Host Tests Source: https://github.com/emobotics-dev/oxivgl/blob/master/README.md Execute all automated tests on the host platform. This includes unit, integration, and visual tests. ```sh LIBCLANG_PATH=/usr/lib64 cargo +nightly test --target x86_64-unknown-linux-gnu ``` -------------------------------- ### Define Screen Animations Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Structures and enums for configuring screen transition animations. ```rust /// Screen transition animation, wrapping `lv_screen_load_anim`. pub struct ScreenAnim { /// Animation type (slide, fade, move, etc.) pub anim_type: ScreenAnimType, /// Duration in milliseconds. pub duration_ms: u32, /// Delay before animation starts, in milliseconds. pub delay_ms: u32, } /// Mirrors `lv_screen_load_anim_t` values. pub enum ScreenAnimType { None, OverLeft, OverRight, OverTop, OverBottom, MoveLeft, MoveRight, MoveTop, MoveBottom, FadeIn, FadeOut, } ``` -------------------------------- ### Initialize transition property array Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-memory-lifetime.md The static lifetime bound is load-bearing because LVGL stores a raw pointer to the properties. ```rust TransitionDsc::new(props: &'static [lv_style_prop_t], ...) ``` -------------------------------- ### Implement View Trait for UI Screens Source: https://context7.com/emobotics-dev/oxivgl/llms.txt The View trait manages UI lifecycle methods including creation, updates, and event handling. Use the example_main macro to initialize the application entry point. ```rust use oxivgl::{ view::{NavAction, View}, widgets::{Align, Label, Obj, WidgetError}, }; #[derive(Default)] struct MyView { _label: Option>, } impl View for MyView { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { // Set container background container.bg_color(0x003a57).bg_opa(255); container.text_color(0xffffff); // Create a centered label let label = Label::new(container)?; label.text("Hello world").align(Align::Center, 0, 0); self._label = Some(label); Ok(()) } fn update(&mut self) -> Result { // Called every render tick - poll state, return NavAction for navigation Ok(NavAction::None) } } // Entry point macro for examples oxivgl_examples_common::example_main!(MyView::default()); ``` -------------------------------- ### Configure Flexbox Layout for Rows and Columns Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Illustrates how to use `set_flex_flow()` to enable flexbox layout on container objects for arranging children in rows or columns with optional wrapping. ```rust use oxivgl::layout::FlexFlow; use oxivgl::style::{lv_pct, LV_SIZE_CONTENT}; use oxivgl::widgets::{Align, Button, Label, Obj, WidgetError}; fn create_flex_layout(container: &Obj<'static>) -> Result<(), WidgetError> { // Horizontal row container let row = Obj::new(container)?; row.size(300, 75).align(Align::TopMid, 0, 5); row.set_flex_flow(FlexFlow::Row); // Vertical column container let col = Obj::new(container)?; col.size(200, 150); col.align_to(&row, Align::OutBottomMid, 0, 5); col.set_flex_flow(FlexFlow::Column); // Add items to row for i in 0..5 { let btn = Button::new(&row)?; btn.size(50, lv_pct(100)); let lbl = Label::new(&btn)?; let mut buf = heapless::String::<8>::new(); let _ = core::fmt::Write::write_fmt(&mut buf, format_args!("{}", i)); lbl.text(&buf).center(); } // Add items to column for i in 0..5 { let btn = Button::new(&col)?; btn.size(lv_pct(100), LV_SIZE_CONTENT); let lbl = Label::new(&btn)?; let mut buf = heapless::String::<8>::new(); let _ = core::fmt::Write::write_fmt(&mut buf, format_args!("Item {}", i)); lbl.text(&buf).center(); } Ok(()) } ``` -------------------------------- ### Add oxivgl as a dependency Source: https://github.com/emobotics-dev/oxivgl/blob/master/README.md Include the library in your Cargo.toml using a git source. ```toml [dependencies] oxivgl = { git = "https://github.com/emobotics-dev/oxivgl" } ``` -------------------------------- ### Event Handling with Bubbling Source: https://context7.com/emobotics-dev/oxivgl/llms.txt Shows how to enable event bubbling on widgets and use event.matches() to filter events within a View implementation. ```rust use oxivgl::{ enums::EventCode, event::Event, view::{NavAction, View}, widgets::{Button, Label, Obj, Slider, WidgetError}, }; struct MultiWidgetHandler { btn_inc: Option>, btn_dec: Option>, slider: Option>, label: Option>, value: i32, } impl View for MultiWidgetHandler { fn create(&mut self, container: &Obj<'static>) -> Result<(), WidgetError> { let btn_inc = Button::new(container)?; btn_inc.pos(10, 10).size(60, 40).bubble_events(); let lbl = Label::new(&btn_inc)?; lbl.text("+").center(); let btn_dec = Button::new(container)?; btn_dec.pos(80, 10).size(60, 40).bubble_events(); let lbl = Label::new(&btn_dec)?; lbl.text("-").center(); let slider = Slider::new(container)?; slider.set_range(0, 100).set_value(50).pos(10, 60).width(200); slider.bubble_events(); let label = Label::new(container)?; label.text("50").pos(10, 120); self.btn_inc = Some(btn_inc); self.btn_dec = Some(btn_dec); self.slider = Some(slider); self.label = Some(label); self.value = 50; Ok(()) } fn on_event(&mut self, event: &Event) -> NavAction { // Handle button clicks if let Some(ref btn) = self.btn_inc { if event.matches(btn, EventCode::CLICKED) { self.value = (self.value + 10).min(100); self.update_display(); } } if let Some(ref btn) = self.btn_dec { if event.matches(btn, EventCode::CLICKED) { self.value = (self.value - 10).max(0); self.update_display(); } } // Handle slider changes if let Some(ref slider) = self.slider { if event.matches(slider, EventCode::VALUE_CHANGED) { self.value = slider.get_value(); self.update_display(); } } NavAction::None } fn update(&mut self) -> Result { Ok(NavAction::None) } } impl MultiWidgetHandler { fn update_display(&mut self) { if let Some(ref label) = self.label { let mut buf = heapless::String::<8>::new(); let _ = core::fmt::Write::write_fmt(&mut buf, format_args!("{}", self.value)); label.text(&buf); } if let Some(ref slider) = self.slider { slider.set_value(self.value); } } } ``` -------------------------------- ### Modal Navigation Methods Source: https://github.com/emobotics-dev/oxivgl/blob/master/docs/spec-navigation.md Methods for displaying and dismissing modal overlays on top of the current view. ```APIDOC ## Navigator Modal Navigation ### Description Methods to manage modal overlays. Modals are rendered on the top layer and block interaction with the underlying view via a backdrop. ### Methods - **modal(view: impl View)**: Shows a modal overlay on top of the current view. - **dismiss_modal() -> Result<(), NavigationError>**: Dismisses the current modal overlay. - **has_modal() -> bool**: Checks if a modal is currently active. ```