backer 0.12.0

A library for straight-forward UI layout.
Documentation

Backer

rust crates.io downloads license

A library for straight-forward UI layout.

Dependency free & framework-agnostic. Backer can be used in an index-based layout approach or with inline drawing code.

This library only implements layout & would be most useful along with a GUI library that can do GUI things (like macroquad or egui).

Features ✨

  • Declarative API: The code should look like the structure it defines
  • Minimal interface: No confusing overloads or magic, cascading effects
  • Intuitive constraints: Backer adheres to & smoothly resolves size constraints with an advanced algorithm
  • Performant: Layout can be comfortably recalculated every frame
  • Easy integration & compatibility: Backer should work with just about any UI library with a bit of glue - so it isn't really fine-tuned for any specific UI solution.

This project intends to be a flexible layout tool & not much else.

Quick Start

1. Define your views.

use backer::nodes::*;
use backer::{Area, Layout};

struct MyState;

enum View {
    Label { area: Area, text: &'static str },
}

2. Implement a draw node

The View in Layout<'a, View, MyState> is returned by draw nodes as Vec<View> and collected by layout.draw.

fn label<'a>(text: &'static str) -> Layout<'a, View, MyState> {
    draw(move |area: Area, _: &mut MyState| {
        // The `area` parameter is the space alotted for your view after layout is calculated
        // The `state` parameter is *your* global mutable state that you pass when you call layout (necessary for things like text caches)
        // These returned values will be collected by `layout.draw`
        vec![View::Label { area, text }]
    })
}

3. Combine nodes to define & customize your layout

fn my_layout<'a>() -> Layout<'a, View, MyState> {
    row(vec![
        label("Hello"),
    ])
}

let mut layout = my_layout();

4. Draw your layout

// UI libraries generally will expose methods to get the available space for layout.
// In a real implementation this would use the real screen size!
let available_area = Area::new(
    0.,
    0.,
    400.,
    600.,
);
let mut my_state = MyState;

// Perform layout & collect the values returned by your draw nodes.
let views: Vec<View> = layout.draw(available_area, &mut my_state);

Preview

Check out the demo site: a mock page showcasing layout capabilities in a realistic interface. Built with egui!

Backer relies on simple rules that can compose to create complex, flexible layouts.

stretched

    column_spaced(
        10.,
        vec![
            draw_a(ui),
            row_spaced(
                10.,
                vec![
                    draw_b(ui).width(180.).align(Align::Leading),
                    column_spaced(10., vec![draw_a(ui), draw_b(ui), draw_c(ui)]),
                ],
            ),
            draw_c(ui),
        ],
    )
    .pad(10.)

Status

The crate is currently usable but new! Breaking changes may be relatively frequent as the crate matures.

Contributing

[!NOTE] This repo uses cargo insta to snapshot test the public API. If your PR changes the public API, one of the checks will fail by default. If the changes to the public API were intentional you can update the snapshot by running:

INSTA_UPDATE=always cargo test --features test-api

Contributions are always welcome 🤗