Stacks

Adjacent layout

HStack and VStack are the primary tools you'll use to arrange views side-by-side in Buoyant. Both stacks can contain a heterogeneous set of views and can be nested inside other stacks.

HStack

extern crate buoyant;
extern crate embedded_graphics;
extern crate embedded_graphics_simulator;
use buoyant::{
    environment::DefaultEnvironment,
    layout::Layout as _,
    render::{EmbeddedGraphicsRender as _, Renderable as _},
};
use embedded_graphics::{pixelcolor::Rgb888, prelude::*};
use embedded_graphics_simulator::{OutputSettings, SimulatorDisplay, Window};

const BACKGROUND_COLOR: Rgb888 = Rgb888::CSS_DARK_SLATE_GRAY;
const DEFAULT_COLOR: Rgb888 = Rgb888::WHITE;

fn main() {
    let mut window = Window::new("Example", &OutputSettings::default());
    let mut display: SimulatorDisplay<Rgb888> = SimulatorDisplay::new(Size::new(480, 320));

    display.clear(BACKGROUND_COLOR).unwrap();

    let environment = DefaultEnvironment::default();
    let origin = buoyant::primitives::Point::zero();

    let view = view();
    let layout = view.layout(&display.size().into(), &environment);
    let render_tree = view.render_tree(&layout, origin, &environment);

    render_tree.render(&mut display, &DEFAULT_COLOR, origin);

    window.show_static(&display);
}

use buoyant::view::shape::{Circle, Rectangle};
use buoyant::view::HStack;
use buoyant::view::RenderExtensions as _;
use buoyant::render::EmbeddedGraphicsView;

fn view() -> impl EmbeddedGraphicsView<Rgb888> {
    HStack::new((
        Circle.foreground_color(Rgb888::CSS_CORAL),
        Rectangle
            .corner_radius(25)
            .foreground_color(Rgb888::CSS_DARK_ORCHID),
    ))
}

In this example, you can see HStack fairly offers both views half the available width. Rectangle greedily takes all the offered space on both axes, while Circle only takes space greedily on the shorter axis to retain its square aspect ratio.

Arranging views on top of each other

ZStack can be used to overlay views on top of each other. Like HStack and VStack, it can contain a heterogeneous set of views.

ZStack

extern crate buoyant;
extern crate embedded_graphics;
extern crate embedded_graphics_simulator;
use buoyant::{
    environment::DefaultEnvironment,
    layout::Layout as _,
    render::{EmbeddedGraphicsRender as _, Renderable as _},
};
use embedded_graphics::{pixelcolor::Rgb888, prelude::*};
use embedded_graphics_simulator::{OutputSettings, SimulatorDisplay, Window};

const BACKGROUND_COLOR: Rgb888 = Rgb888::CSS_DARK_SLATE_GRAY;
const DEFAULT_COLOR: Rgb888 = Rgb888::WHITE;

fn main() {
    let mut window = Window::new("Example", &OutputSettings::default());
    let mut display: SimulatorDisplay<Rgb888> = SimulatorDisplay::new(Size::new(480, 320));

    display.clear(BACKGROUND_COLOR).unwrap();

    let environment = DefaultEnvironment::default();
    let origin = buoyant::primitives::Point::zero();

    let view = view();
    let layout = view.layout(&display.size().into(), &environment);
    let render_tree = view.render_tree(&layout, origin, &environment);

    render_tree.render(&mut display, &DEFAULT_COLOR, origin);

    window.show_static(&display);
}

use buoyant::view::padding::Edges;
use buoyant::view::shape::{Circle, Rectangle};
use buoyant::view::{LayoutExtensions as _, RenderExtensions as _};
use buoyant::view::ZStack;
use buoyant::render::EmbeddedGraphicsView;

fn view() -> impl EmbeddedGraphicsView<Rgb888> {
    ZStack::new((
        Rectangle
            .corner_radius(50)
            .foreground_color(Rgb888::CSS_DARK_ORCHID),
        Circle.foreground_color(Rgb888::CSS_CORAL),
        Circle
            .foreground_color(Rgb888::CSS_GOLDENROD)
            .padding(Edges::All, 25),
    ))
}

The .padding() modifier is useful here to create space around the topmost yellow circle.

Combining Stacks

Stacks can be nested to create complex layouts.

Mixed Stacks

extern crate buoyant;
extern crate embedded_graphics;
extern crate embedded_graphics_simulator;
use buoyant::{
    environment::DefaultEnvironment,
    layout::Layout as _,
    render::{EmbeddedGraphicsRender as _, Renderable as _},
};
use embedded_graphics::{pixelcolor::Rgb888, prelude::*};
use embedded_graphics_simulator::{OutputSettings, SimulatorDisplay, Window};

const BACKGROUND_COLOR: Rgb888 = Rgb888::CSS_DARK_SLATE_GRAY;
const DEFAULT_COLOR: Rgb888 = Rgb888::WHITE;

fn main() {
    let mut window = Window::new("Example", &OutputSettings::default());
    let mut display: SimulatorDisplay<Rgb888> = SimulatorDisplay::new(Size::new(480, 320));

    display.clear(BACKGROUND_COLOR).unwrap();

    let environment = DefaultEnvironment::default();
    let origin = buoyant::primitives::Point::zero();

    let view = view();
    let layout = view.layout(&display.size().into(), &environment);
    let render_tree = view.render_tree(&layout, origin, &environment);

    render_tree.render(&mut display, &DEFAULT_COLOR, origin);

    window.show_static(&display);
}

use buoyant::view::padding::Edges;
use buoyant::view::shape::{Circle, Rectangle};
use buoyant::view::{LayoutExtensions as _, RenderExtensions as _};
use buoyant::view::{HStack, VStack, ZStack};
use buoyant::render::EmbeddedGraphicsView;

fn view() -> impl EmbeddedGraphicsView<Rgb888> {
    HStack::new((
        VStack::new((
            Circle.foreground_color(Rgb888::CSS_GOLDENROD),
            Circle.foreground_color(Rgb888::CSS_GHOST_WHITE),
        )),
        ZStack::new((
            Rectangle
                .corner_radius(50)
                .foreground_color(Rgb888::CSS_DARK_ORCHID),
            Rectangle
                .corner_radius(25)
                .foreground_color(Rgb888::CSS_CORAL)
                .padding(Edges::All, 25),
        )),
    ))
}

Stack conformance to the necessary traits is macro-derived up to stacks of 10 views