buoyant 0.6.1

SwiftUI-like UIs in Rust for embedded devices
Documentation
use std::time::Duration;

use buoyant::{
    event::EventContext,
    render::Render,
    render_target::FixedTextBuffer,
    view::{prelude::*, scroll_view::ScrollDirection},
};

use crate::common::{helpers, touch_up};
use crate::{assert_str_grid_eq, common::touch_down};

#[derive(Debug, Clone, Default, PartialEq, Eq)]
struct TestState {
    pub above: u8,
    pub inner_1: u8,
    pub inner_2: u8,
    pub inner_3: u8,
    pub below: u8,
}

fn scroll_view() -> impl View<char, TestState> {
    VStack::new((
        Lens::new(button('a'), |s: &mut TestState| &mut s.above),
        ScrollView::new(VStack::new((
            Lens::new(button('b'), |s: &mut TestState| &mut s.inner_1),
            Lens::new(button('c'), |s: &mut TestState| &mut s.inner_2),
            Lens::new(button('d'), |s: &mut TestState| &mut s.inner_3),
        )))
        .with_direction(ScrollDirection::Vertical)
        .with_bar_visibility(buoyant::view::scroll_view::ScrollBarVisibility::Never),
        Lens::new(button('e'), |s: &mut TestState| &mut s.below),
    ))
}

fn button(c: char) -> impl View<char, u8> + use<> {
    Button::new(
        |i: &mut u8| *i += 1,
        move |is_pressed| {
            Rectangle
                .frame()
                .with_height(2)
                .flex_infinite_width(HorizontalAlignment::Center)
                .foreground_color(if is_pressed { 'X' } else { c })
        },
    )
}

fn ctx(secs: u64) -> EventContext {
    EventContext::new(Duration::from_secs(secs))
}

#[test]
fn button_above_scroll() {
    assert_eq!(
        tap_button(2, 0, true),
        TestState {
            above: 1,
            inner_1: 0,
            inner_2: 0,
            inner_3: 0,
            below: 0,
        }
    );
}
#[test]
fn button_inner_1_scroll() {
    assert_eq!(
        tap_button(2, 2, true),
        TestState {
            above: 0,
            inner_1: 1,
            inner_2: 0,
            inner_3: 0,
            below: 0,
        }
    );
}

#[test]
fn button_inner_2_scroll() {
    assert_eq!(
        tap_button(2, 4, true),
        TestState {
            above: 0,
            inner_1: 0,
            inner_2: 1,
            inner_3: 0,
            below: 0,
        }
    );
}

#[test]
fn button_below_scroll() {
    assert_eq!(
        tap_button(2, 5, true),
        TestState {
            above: 0,
            inner_1: 0,
            inner_2: 0,
            inner_3: 0,
            below: 1,
        }
    );
}

#[test]
fn tap_outside_scroll() {
    assert_eq!(
        tap_button(2, 7, false),
        TestState {
            above: 0,
            inner_1: 0,
            inner_2: 0,
            inner_3: 0,
            below: 0,
        }
    );

    assert_eq!(
        tap_button(2, 28, false),
        TestState {
            above: 0,
            inner_1: 0,
            inner_2: 0,
            inner_3: 0,
            below: 0,
        }
    );

    assert_eq!(
        tap_button(-2, 5, false),
        TestState {
            above: 0,
            inner_1: 0,
            inner_2: 0,
            inner_3: 0,
            below: 0,
        }
    );
}

fn tap_button(x: i32, y: i32, should_recompute: bool) -> TestState {
    let mut buffer = FixedTextBuffer::<4, 7>::default();
    let size = buffer.size();

    let mut captures = TestState::default();
    let view = scroll_view();
    let mut state = view.build_state(&mut captures);

    let mut tree = helpers::tree(
        &view,
        &mut captures,
        &mut state,
        Duration::from_secs(1),
        size,
    );

    tree.render(&mut buffer, &' ');

    assert_str_grid_eq!(
        ["aaaa", "aaaa", "bbbb", "bbbb", "cccc", "eeee", "eeee",],
        &buffer.text
    );

    let result = view.handle_event(
        &touch_down(x, y),
        &ctx(2),
        &mut tree,
        &mut captures,
        &mut state,
    );
    assert_eq!(result.recompute_view, should_recompute);

    let result = view.handle_event(
        &touch_up(x, y),
        &ctx(3),
        &mut tree,
        &mut captures,
        &mut state,
    );
    assert_eq!(result.recompute_view, should_recompute);

    buffer.clear();

    tree = helpers::tree(
        &view,
        &mut captures,
        &mut state,
        Duration::from_secs(4),
        size,
    );

    tree.render(&mut buffer, &' ');

    assert_str_grid_eq!(
        ["aaaa", "aaaa", "bbbb", "bbbb", "cccc", "eeee", "eeee",],
        &buffer.text
    );

    captures
}