use embedded_graphics::{geometry::Size, prelude::Point, primitives::Rectangle};
use faststep::{
ChildView, FsTheme, I18n, ListActivity, ListDataSource, ListDelegate, ListRow, ListSelection,
ListView, Localized, SplitAxis, SplitView, TouchEvent, TouchPhase, ViewEnvironment, ViewKind,
ViewRedraw, ViewRegistration,
};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum DemoViewId {
List,
}
#[test]
fn registration_tracks_view_properties_and_children() {
let mut registration =
ViewRegistration::<DemoViewId, 2>::new(Rectangle::new(Point::zero(), Size::new(320, 240)));
registration.set_title(Localized::new("root.title", "Root"));
registration.set_alpha(220);
registration.set_hidden(false);
registration.set_clips_to_bounds(true);
let _ = registration.add_child(
ChildView::new(
DemoViewId::List,
Rectangle::new(Point::new(0, 0), Size::new(320, 240)),
)
.with_kind(ViewKind::List)
.with_title(Localized::new("devices.title", "Devices")),
);
assert_eq!(
registration.properties().title.map(|title| title.fallback),
Some("Root")
);
assert_eq!(registration.properties().alpha, 220);
assert!(registration.properties().clips_to_bounds);
assert_eq!(registration.children().len(), 1);
assert_eq!(registration.children()[0].kind, ViewKind::List);
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum DemoMessage {
Selected(u8),
}
struct DemoDataSource;
impl ListDataSource for DemoDataSource {
type ItemId = u8;
fn item_count(&self) -> usize {
3
}
fn item_id(&self, index: usize) -> Self::ItemId {
index as u8
}
fn item_height(&self, index: usize) -> u32 {
match index {
0 => 40,
1 => 56,
_ => 48,
}
}
}
struct DemoDelegate;
impl ListDelegate<'static, u8> for DemoDelegate {
type Message = DemoMessage;
fn draw_row<D>(&self, _display: &mut D, _row: ListRow<u8>, _env: &ViewEnvironment<'_, 'static>)
where
D: embedded_graphics::draw_target::DrawTarget<
Color = embedded_graphics::pixelcolor::Rgb565,
>,
{
}
fn did_select_item(&mut self, selection: ListSelection<u8>) -> Option<Self::Message> {
Some(DemoMessage::Selected(selection.id))
}
}
#[test]
fn list_view_uses_datasource_and_delegate_for_selection() {
let viewport = Rectangle::new(Point::zero(), Size::new(320, 120));
let mut list = ListView::new(DemoDataSource, DemoDelegate);
let start = list.handle_touch(
TouchEvent::new(Point::new(12, 12), TouchPhase::Start, 1),
viewport,
);
assert!(start.captured);
assert_eq!(start.activity, ListActivity::Interactive);
assert_eq!(start.redraw, ViewRedraw::Dirty(viewport));
let end = list.handle_touch(
TouchEvent::new(Point::new(12, 12), TouchPhase::End, 2),
viewport,
);
assert_eq!(end.message, Some(DemoMessage::Selected(0)));
assert_eq!(
list.selected_item(),
Some(ListSelection { id: 0, index: 0 })
);
}
#[test]
fn list_view_tick_marks_viewport_dirty_when_animating() {
let viewport = Rectangle::new(Point::zero(), Size::new(320, 120));
let mut list = ListView::new(DemoDataSource, DemoDelegate);
let _ = list.handle_touch(
TouchEvent::new(Point::new(16, 80), TouchPhase::Start, 1),
viewport,
);
let _ = list.handle_touch(
TouchEvent::new(Point::new(16, 20), TouchPhase::Move, 2),
viewport,
);
let _ = list.handle_touch(
TouchEvent::new(Point::new(16, 20), TouchPhase::End, 3),
viewport,
);
assert_eq!(list.tick(16, viewport), ViewRedraw::Dirty(viewport));
assert!(list.scroll_bar(viewport).is_some());
let _ = FsTheme::default();
let _ = I18n::new("en", "en", &[]);
}
#[test]
fn list_view_enters_motion_mode_as_soon_as_drag_slop_is_crossed() {
let viewport = Rectangle::new(Point::zero(), Size::new(320, 120));
let mut list = ListView::new(DemoDataSource, DemoDelegate);
let start = list.handle_touch(
TouchEvent::new(Point::new(16, 16), TouchPhase::Start, 1),
viewport,
);
assert_eq!(start.activity, ListActivity::Interactive);
let move_event = list.handle_touch(
TouchEvent::new(Point::new(16, 24), TouchPhase::Move, 2),
viewport,
);
assert_eq!(move_event.activity, ListActivity::Motion);
assert_eq!(move_event.redraw, ViewRedraw::Dirty(viewport));
}
#[test]
fn split_view_partitions_frame() {
let split = SplitView::new(SplitAxis::Horizontal, 625, 12);
let layout = split.layout(Rectangle::new(Point::zero(), Size::new(320, 120)));
assert_eq!(layout.primary.size.height, 120);
assert_eq!(layout.secondary.size.height, 120);
assert_eq!(
layout.primary.size.width + layout.secondary.size.width + 12,
320
);
assert_eq!(
layout.secondary.top_left.x,
layout.primary.top_left.x + layout.primary.size.width as i32 + 12
);
}
#[test]
fn theme_can_be_configured_at_startup() {
let theme = FsTheme::default()
.with_accent(
embedded_graphics::pixelcolor::Rgb565::new(3, 18, 28),
embedded_graphics::pixelcolor::Rgb565::new(31, 63, 31),
)
.with_status_colors(
embedded_graphics::pixelcolor::Rgb565::new(6, 38, 12),
embedded_graphics::pixelcolor::Rgb565::new(31, 45, 4),
embedded_graphics::pixelcolor::Rgb565::new(27, 12, 10),
embedded_graphics::pixelcolor::Rgb565::new(31, 63, 31),
);
assert_eq!(
theme.accent,
embedded_graphics::pixelcolor::Rgb565::new(3, 18, 28)
);
assert_eq!(
theme.text_on_accent,
embedded_graphics::pixelcolor::Rgb565::new(31, 63, 31)
);
assert_eq!(
theme.danger,
embedded_graphics::pixelcolor::Rgb565::new(27, 12, 10)
);
assert_eq!(
theme.text_on_danger,
embedded_graphics::pixelcolor::Rgb565::new(31, 63, 31)
);
}