tests.rs
raw
use std::cell::RefCell;
use std::rc::Rc;
use guiduck_scene::geom::{Point, Size};
use taffy::prelude::percent;
use super::*;
use crate::clipboard::NoClipboard;
use crate::event::{Key, KeyInput, Modifiers, PointerButton, PointerInput};
use crate::widget::{Container, WidgetId, WidgetTree};
fn sample_font() -> guiduck_scene::paint::Blob<u8> {
static FONT: &[u8] = include_bytes!("../../../../guiduck-samples/fonts/DejaVuSans.ttf");
static BLOB: std::sync::OnceLock<guiduck_scene::paint::Blob<u8>> = std::sync::OnceLock::new();
BLOB.get_or_init(|| guiduck_scene::paint::Blob::new(std::sync::Arc::new(FONT)))
.clone()
}
/// Root containing a single button at a known location.
fn fixture() -> (WidgetTree, WidgetId, Rc<RefCell<u32>>) {
let mut tree =
WidgetTree::with_text_context(crate::text::TextContext::hermetic([sample_font()]));
let root = tree.insert(
Container::new(),
taffy::Style {
size: taffy::Size {
width: percent(1.0_f32),
height: percent(1.0_f32),
},
padding: taffy::Rect::length(10.0_f32),
align_items: Some(taffy::AlignItems::FLEX_START),
..Default::default()
},
None,
);
let button = tree.insert(
Button::new("Save").family("DejaVu Sans"),
taffy::Style::default(),
Some(root),
);
let clicks = Rc::new(RefCell::new(0u32));
let sink = clicks.clone();
tree.on_event(button, crate::event::EventKind::Click, move |_ctx, _ev| {
*sink.borrow_mut() += 1;
});
tree.render_frame(Size::new(300.0, 120.0));
(tree, button, clicks)
}
fn key(tree: &mut WidgetTree, key: Key) {
tree.dispatch_key(
&KeyInput {
key,
modifiers: Modifiers::default(),
pressed: true,
},
&mut NoClipboard,
);
}
fn button_center(tree: &WidgetTree, button: WidgetId) -> Point {
let origin = tree.absolute_origin(button);
let size = tree.layout(button).size();
Point::new(origin.x + size.width / 2.0, origin.y + size.height / 2.0)
}
#[test]
fn pointer_click_fires_the_wire() {
let (mut tree, button, clicks) = fixture();
let center = button_center(&tree, button);
tree.dispatch_pointer(
PointerInput::Down {
pos: center,
button: PointerButton::Left,
time: std::time::Instant::now(),
},
&mut NoClipboard,
);
tree.dispatch_pointer(
PointerInput::Up {
pos: center,
button: PointerButton::Left,
},
&mut NoClipboard,
);
assert_eq!(*clicks.borrow(), 1);
}
#[test]
fn enter_and_space_activate_a_focused_button() {
let (mut tree, button, clicks) = fixture();
// The focus invariant already focused the only focusable widget.
assert_eq!(tree.focus(), Some(button));
key(&mut tree, Key::Enter);
tree.render_frame(Size::new(300.0, 120.0));
assert_eq!(*clicks.borrow(), 1, "Enter activates");
key(&mut tree, Key::Character(" ".to_owned()));
tree.render_frame(Size::new(300.0, 120.0));
assert_eq!(*clicks.borrow(), 2, "Space activates");
}
#[test]
fn a_disabled_button_is_inert() {
let (mut tree, button, clicks) = fixture();
tree.set_enabled(button, false);
tree.render_frame(Size::new(300.0, 120.0));
// A disabled button is not focusable, so keyboard activation cannot
// reach it, and pointer clicks retarget to the enabled ancestor.
let center = button_center(&tree, button);
tree.dispatch_pointer(
PointerInput::Down {
pos: center,
button: PointerButton::Left,
time: std::time::Instant::now(),
},
&mut NoClipboard,
);
tree.dispatch_pointer(
PointerInput::Up {
pos: center,
button: PointerButton::Left,
},
&mut NoClipboard,
);
assert_eq!(*clicks.borrow(), 0);
}
#[test]
fn cursor_is_a_pointer_over_the_button() {
let (mut tree, button, _clicks) = fixture();
let center = button_center(&tree, button);
tree.dispatch_pointer(PointerInput::Moved(center), &mut NoClipboard);
assert_eq!(tree.cursor_shape(), CursorShape::Pointer);
}