iris-ui 0.1.0

UI toolkit for no_std embedded Rust
Documentation

Untitled Embedded Rust GUI

screenshot

What is This?

This is a new GUI library for no_std embedded Rust. I currently have it running on the ESP32-S3 based Lilygo T-Deck, but it should run on anything that uses the embedded_graphics traits. It focuses on bandwidth limited devices, such as SPI displays.

Features

  • Incremental redrawing using layout and dirty rect tracking.
  • Built in components for buttons, labels, text input, toggles, and panels.
  • Theming with colors and fonts
  • Scene to manage a tree of View structs
  • Fast single pass layout algorithm

Anti-Features

  • event loop: To make it flexible, the lib does not impose its own event loop. Instead, the application should send events to the scene and then redraw in its own loop. Better documentation coming.
  • animation: The library has no support for animation or transparency because those will perform horribly on bandwidth limited SPI displays.

Usage

Build the library with cargo build.

Run the simulator example with cargo run --example simulator --features std. Note that the simulator needs SDL2. Install instructions.

Run the unit tests with cargo test --features std.

The library has not yet been released as a published crate because I still need a name and need to fix some bugs.

Views

Views are rendered using a Theme which can be customized for different colors and font sizes. Views carry their own internal state using an optional state struct. Application state should remain outside the scene/view structure and be handled by processing actions emitted from the scene when events happen.

Instead of implementing a trait you create components by allocating a View is with optional fields for functions to handle input, state, layout, and drawing. This is the code that creates a button (as implemented in the library provided make_button):

pub fn make_button(name: &ViewId, title: &str) -> View {
    View {
        name: name.clone(),
        title: title.to_string(),
        // the button will determine its own width
        h_flex: Intrinsic,
        // the button will determine its own height
        v_flex: Intrinsic,
        // on tap, requested to be focused
        input: Some(|e| {
            if let EventType::Tap(_pt) = &e.event_type {
                e.scene.set_focused(e.target);
                return Some(Action::Generic);
            }
            None
        }),
        // size self based on the font and the title text
        layout: Some(|e| {
            if let Some(view) = e.scene.get_view_mut(&e.target) {
                view.bounds.size = util::calc_size(e.theme.bold_font, &view.title);
            }
        }),
        // delegate drawing to a draw_button function
        draw: Some(draw_button),
        ..Default::default()
    }
}

fn draw_button(e: &mut DrawEvent) {
    e.ctx.fill_rect(&e.view.bounds, &e.theme.bg);
    e.ctx.stroke_rect(&e.view.bounds, &e.theme.fg);
    if let Some(focused) = e.focused {
        if focused == &e.view.name {
            e.ctx.stroke_rect(&e.view.bounds.contract(2), &e.theme.fg);
        }
    }
    draw_centered_text(
        e.ctx,
        &e.view.title,
        &e.view.bounds,
        &e.theme.bold_font,
        &e.theme.fg,
    );
}

Themes

Theme is a struct passed to every View's draw function. It stores the standard colors and fonts for drawing. However, these are just guidelines. A view can feel free to ignore them and draw whatever it wants. The theme fields should be used for:

  • bg: the background of components like buttons and text inputs.
  • fg: the foreground of components, which usually means the text color.
  • panel_bg: the background of panels and other containers. Depending on the theme this may or may not be the same as bg.
  • font: the default font used for all text.
  • bold_font: the bold variant of the current font. Used for button titles.
  • selected_bg: a background color used to indicate something is selected.
  • selected_fg: a text color used to indicate something is selected. Usually used with selected_bg.

Roadmap

0.1

  • Remove generics for color and font. Just use embedded graphics directly.
  • use simulator for interactive tests
  • use MockDisplay for automated tests
  • support layout using font size. needs padding in the widgets.
  • add hbox and vbox layouts
  • make children drawn and picked relative to the parent.
  • general
    • setup CI on github actions.
  • more components
    • add menu view
    • add list view
  • drawing
    • redo fill_text api.
      • just text. support bg color?
      • proper alignment. provide center point and draw centered
    • draw line
    • remove clear
    • consolidate Display impls
  • layout & rendering
    • calculating dirty rect needs to be converted back to global
    • common view padding
    • new layout algoritm
    • form layout -> grid layout
      • debug lines
      • alignment within grid cells
      • span grid cells
  • pick final name

0.2

  • input improvements
    • cleanup event types and action command signatures.
    • document how to make your own event & draw loop
  • text input
    • move cursor within text
    • forward and backward delete
    • selection?
  • focus management
    • use scroll events to jump between focused elements and perform selection.
    • spec out how focus management works.
      • focus groups
  • improved custom view support
    • view can define the children it uses
      • let tab panel define its own children using a toggle group
    • let tab panel switch its own tabs instead of using external handle action
  • theme accent colors?