use saudade::mock::MockBackend;
use saudade::{
Color, Column, Dropdown, Event, EventCtx, FontSet, Modal, Painter, PopupKind, PopupRequest,
Rect, Size, Theme, Widget,
};
fn render_into(anchor: Option<Rect>, w: i32, h: i32, mut f: impl FnMut(&mut Painter)) -> Vec<u32> {
let mut pixels = vec![0u32; (w * h) as usize];
{
let mut painter =
Painter::with_popup_anchor(&mut pixels, w, h, 1.0, 0, 0, FontSet::default(), anchor);
f(&mut painter);
}
pixels
}
struct Body {
dd: Dropdown,
rect: Rect,
}
impl Body {
fn new() -> Self {
let mut dd = Dropdown::new(Rect::new(0, 0, 10, 10))
.with_items(["1920 x 1080", "1280 x 720", "800 x 600"])
.with_selected(0);
dd.open();
Self {
dd,
rect: Rect::new(0, 0, 0, 0),
}
}
}
impl Widget for Body {
fn bounds(&self) -> Rect {
self.rect
}
fn layout(&mut self, b: Rect) {
self.rect = b;
self.dd.set_rect(Rect::new(b.x + 16, b.y + 16, 160, 24));
}
fn paint(&mut self, p: &mut Painter, t: &Theme) {
self.dd.paint(p, t);
}
fn paint_overlay(&mut self, p: &mut Painter, t: &Theme) {
self.dd.paint_overlay(p, t);
}
fn event(&mut self, e: &Event, c: &mut EventCtx) {
self.dd.event(e, c);
}
fn focusable(&self) -> bool {
true
}
fn set_focused(&mut self, f: bool) {
self.dd.set_focused(f);
}
fn captures_pointer(&self) -> bool {
self.dd.captures_pointer()
}
fn popup_request(&self) -> Option<PopupRequest> {
self.dd.popup_request()
}
}
fn open_modal() -> Modal {
let mut modal = Modal::new();
modal.layout(Rect::new(0, 0, 400, 320)); modal.show("Display", Size::new(240, 160), Box::new(Body::new()));
modal.layout(Rect::new(0, 0, 400, 320)); modal
}
#[test]
fn dropdown_in_dialog_surfaces_two_nested_popups() {
let modal = open_modal();
let mut popups = Vec::new();
modal.collect_popups(&mut popups);
assert_eq!(popups.len(), 2, "dialog window + dropdown list window");
assert_eq!(popups[0].kind, PopupKind::Dialog);
assert_eq!(popups[1].kind, PopupKind::Popup);
assert!(
popups[1].rect.y >= popups[0].rect.y,
"the list should hang off the dialog, not float above it"
);
}
#[test]
fn closed_dropdown_surfaces_only_the_dialog() {
let mut modal = open_modal();
modal.dismiss();
modal.layout(Rect::new(0, 0, 400, 320));
modal.show("Display", Size::new(240, 160), {
let mut dd = Dropdown::new(Rect::new(0, 0, 10, 10)).with_items(["a", "b"]);
dd.set_selected(Some(0));
Box::new(ClosedBody {
dd,
rect: Rect::new(0, 0, 0, 0),
})
});
modal.layout(Rect::new(0, 0, 400, 320));
let mut popups = Vec::new();
modal.collect_popups(&mut popups);
assert_eq!(
popups.len(),
1,
"only the dialog when the dropdown is closed"
);
assert_eq!(popups[0].kind, PopupKind::Dialog);
}
struct ClosedBody {
dd: Dropdown,
rect: Rect,
}
impl Widget for ClosedBody {
fn bounds(&self) -> Rect {
self.rect
}
fn layout(&mut self, b: Rect) {
self.rect = b;
self.dd.set_rect(Rect::new(b.x + 16, b.y + 16, 160, 24));
}
fn paint(&mut self, p: &mut Painter, t: &Theme) {
self.dd.paint(p, t);
}
fn popup_request(&self) -> Option<PopupRequest> {
self.dd.popup_request()
}
}
#[test]
fn nested_dropdown_renders_without_panicking() {
let mut root = Column::new().add_overlay(open_modal());
let snap = MockBackend::new(400, 320).render(&mut root);
assert!(snap.pixels().iter().any(|&p| p != Color::WHITE.0));
}
#[test]
fn dropdown_list_paints_only_into_its_own_popup_pass() {
let mut dd = Dropdown::new(Rect::new(10, 10, 100, 22))
.with_items(["a", "b", "c"])
.with_selected(0);
dd.open();
let req = dd
.popup_request()
.expect("an open dropdown should ask the runtime for a popup");
let theme = Theme::default();
let other = Rect::new(0, 0, 400, 320);
assert_ne!(other, req.rect, "test setup: anchors must differ");
let foreign = render_into(Some(other), 200, 100, |p| dd.paint_overlay(p, &theme));
assert!(
foreign.iter().all(|&px| px == 0),
"dropdown drew into a popup pass that wasn't its own"
);
let main = render_into(None, 200, 100, |p| dd.paint_overlay(p, &theme));
assert!(
main.iter().all(|&px| px == 0),
"dropdown drew into the main pass"
);
let own = render_into(Some(req.rect), 200, 100, |p| dd.paint_overlay(p, &theme));
assert!(
own.iter().any(|&px| px != 0),
"dropdown failed to paint its list during its own popup pass"
);
}