use kui_core::testing::{click_at, drive};
use kui_core::{Core, InputEvent, NodeSpec, Size, Vec2, WindowButton, WindowCommand, WindowId};
fn frame(core: &mut Core) {
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
ui.with(
NodeSpec::row().grow_width().height(40.0).window_drag(),
|ui| {
ui.leaf(NodeSpec::row().grow_width());
for b in [
WindowButton::Minimize,
WindowButton::Maximize,
WindowButton::Close,
] {
ui.leaf(NodeSpec::row().width(40.0).grow_height().window_button(b));
}
},
);
ui.leaf(NodeSpec::row().size(100.0, 30.0).on_click("content-click"));
ui.finish();
}
#[test]
fn press_on_drag_strip_emits_start_drag_and_no_ui_event() {
let mut core = Core::new();
frame(&mut core);
let evs = drive(
&mut core,
&[
InputEvent::CursorMoved(Vec2::new(100.0, 20.0)),
InputEvent::mouse_down(1),
],
);
assert!(
evs.is_empty(),
"chrome nodes must not emit UiEvents, got {evs:?}"
);
assert_eq!(
core.take_window_commands(),
vec![WindowCommand::StartDrag(WindowId::MAIN)]
);
}
#[test]
fn window_buttons_emit_their_commands_on_click() {
let mut core = Core::new();
frame(&mut core);
let mut evs = click_at(&mut core, 300.0, 20.0);
evs.extend(click_at(&mut core, 340.0, 20.0));
evs.extend(click_at(&mut core, 380.0, 20.0));
assert!(
evs.is_empty(),
"chrome nodes must not emit UiEvents, got {evs:?}"
);
assert_eq!(
core.take_window_commands(),
vec![
WindowCommand::Minimize(WindowId::MAIN),
WindowCommand::ToggleMaximize(WindowId::MAIN),
WindowCommand::Close(WindowId::MAIN)
]
);
}
#[test]
fn buttons_win_over_the_drag_strip_below_them() {
let mut core = Core::new();
frame(&mut core);
click_at(&mut core, 380.0, 20.0);
assert_eq!(
core.take_window_commands(),
vec![WindowCommand::Close(WindowId::MAIN)]
);
}
#[test]
fn press_then_drag_off_a_button_emits_nothing() {
let mut core = Core::new();
frame(&mut core);
drive(
&mut core,
&[
InputEvent::CursorMoved(Vec2::new(380.0, 20.0)),
InputEvent::mouse_down(1),
InputEvent::CursorMoved(Vec2::new(200.0, 200.0)),
InputEvent::mouse_up(),
],
);
assert!(core.take_window_commands().is_empty());
}
#[test]
fn commands_drain_once() {
let mut core = Core::new();
frame(&mut core);
click_at(&mut core, 380.0, 20.0);
assert_eq!(core.take_window_commands().len(), 1);
assert!(core.take_window_commands().is_empty());
}
#[test]
fn size_and_focus_requests_queue_in_order_with_chrome_commands() {
let mut core = Core::new();
frame(&mut core);
core.set_window_size(WindowId::MAIN, Size::new(640.0, 480.0));
click_at(&mut core, 300.0, 20.0); core.focus_window(WindowId::MAIN);
assert_eq!(
core.take_window_commands(),
vec![
WindowCommand::SetSize {
window: WindowId::MAIN,
size: Size::new(640.0, 480.0),
},
WindowCommand::Minimize(WindowId::MAIN),
WindowCommand::Focus(WindowId::MAIN),
]
);
assert!(core.take_window_commands().is_empty());
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
ui.focus_window(WindowId::MAIN);
ui.set_window_size(WindowId::MAIN, Size::new(800.0, 600.0));
ui.finish();
assert_eq!(
core.take_window_commands(),
vec![
WindowCommand::Focus(WindowId::MAIN),
WindowCommand::SetSize {
window: WindowId::MAIN,
size: Size::new(800.0, 600.0),
},
]
);
frame(&mut core);
assert!(core.take_window_commands().is_empty());
assert_eq!(core.viewport(), Size::new(400.0, 300.0));
}
#[test]
fn plain_on_click_nodes_are_unaffected() {
let mut core = Core::new();
frame(&mut core);
let evs = click_at(&mut core, 50.0, 60.0);
assert_eq!(evs.len(), 1);
assert_eq!(evs[0].payload.as_str(), Some("content-click"));
assert!(core.take_window_commands().is_empty());
}
#[test]
fn titlebar_widget_declares_chrome_from_env() {
use kui_core::{Rect, WindowEnv, widgets};
let mut core = Core::new();
core.env.window = WindowEnv {
custom_chrome: true,
..Default::default()
};
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
widgets::titlebar(&mut ui, "app");
ui.finish();
click_at(&mut core, 400.0 - 23.0, 20.0);
assert_eq!(
core.take_window_commands(),
vec![WindowCommand::Close(WindowId::MAIN)]
);
core.env.window = WindowEnv {
custom_chrome: true,
native_controls: Some(Rect::new(0.0, 0.0, 78.0, 28.0)),
..Default::default()
};
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
widgets::titlebar(&mut ui, "app");
ui.finish();
drive(
&mut core,
&[
InputEvent::CursorMoved(Vec2::new(400.0 - 23.0, 20.0)),
InputEvent::mouse_down(1),
],
);
assert_eq!(
core.take_window_commands(),
vec![WindowCommand::StartDrag(WindowId::MAIN)]
);
}
#[test]
fn the_cluster_alone_in_a_fitted_row_is_a_titlebar_tall() {
use kui_core::{WindowEnv, widgets};
let mut core = Core::new();
core.set_inspect(true);
core.env.window = WindowEnv {
custom_chrome: true,
..Default::default()
};
let h = core.metrics().titlebar_h;
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
let wrap = ui.with_keyed("wrap", NodeSpec::row().pad(6.0), |ui| {
widgets::window_buttons(ui)
});
ui.finish();
let r = core
.nodes()
.into_iter()
.find(|n| n.key == wrap)
.expect("the row is laid out")
.rect;
assert_eq!(r.h, h + 12.0, "the buttons' height and the padding");
click_at(&mut core, 6.0 + 46.0 * 2.0 + 23.0, 6.0 + h / 2.0);
assert_eq!(
core.take_window_commands(),
vec![WindowCommand::Close(WindowId::MAIN)]
);
}
#[test]
fn titlebar_insets_past_the_native_controls() {
use kui_core::{Rect, WindowEnv, widgets};
let title_x = |controls: Option<Rect>| {
let mut core = Core::new();
core.env.window = WindowEnv {
custom_chrome: true,
native_controls: controls,
..Default::default()
};
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
widgets::titlebar(&mut ui, "app");
ui.finish();
core.access_tree()
.nodes
.iter()
.find(|n| n.name.as_deref() == Some("app"))
.expect("the titlebar draws its title")
.rect
.x
};
assert_eq!(title_x(None), 12.0);
assert_eq!(title_x(Some(Rect::new(0.0, 0.0, 78.0, 28.0))), 78.0);
assert_eq!(title_x(Some(Rect::new(4.0, 0.0, 78.0, 28.0))), 82.0);
}
#[test]
fn titlebar_is_as_tall_as_the_os_s_where_the_os_keeps_controls_over_it() {
use kui_core::{Key, Metrics, Rect, WindowEnv, widgets};
let strip_h = |controls: Option<Rect>| {
let mut core = Core::new();
core.set_inspect(true);
core.env.window = WindowEnv {
custom_chrome: true,
native_controls: controls,
..Default::default()
};
let mut ui = core.frame(Size::new(400.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
let read = widgets::titlebar_height(&ui);
widgets::titlebar(&mut ui, "app");
ui.finish();
let strip = Key::ROOT.str("kui:titlebar");
let drawn = core
.nodes()
.iter()
.find(|n| n.key == strip)
.expect("the strip is laid out under its own key")
.rect
.h;
assert_eq!(read, drawn, "titlebar_height is what the strip drew");
drawn
};
assert_eq!(strip_h(None), Metrics::default().titlebar_h);
assert_eq!(strip_h(Some(Rect::new(0.0, 0.0, 78.0, 32.0))), 32.0);
assert_eq!(strip_h(Some(Rect::new(0.0, 0.0, 78.0, 28.0))), 28.0);
assert_eq!(
strip_h(Some(Rect::new(0.0, 0.0, 78.0, 0.0))),
Metrics::default().titlebar_h
);
}
#[test]
fn events_carry_the_window_the_driver_declared() {
let mut core = Core::new();
frame(&mut core);
let evs = click_at(&mut core, 50.0, 60.0);
assert_eq!(evs.len(), 1);
assert_eq!(
evs[0].window,
WindowId::MAIN,
"the window an app starts in, and 0 across every transport"
);
core.env.window.id = WindowId(7);
frame(&mut core);
let mut ui = core.frame(Size::new(500.0, 300.0), 1.0);
ui.configure_root(NodeSpec::column().fill());
ui.finish();
let pending = core.take_pending_events();
assert!(
pending
.iter()
.any(|ev| ev.payload.get("kind").and_then(kui_core::Value::as_str) == Some("resize")),
"the viewport change should have raised a resize: {pending:?}"
);
assert!(
pending.iter().all(|ev| ev.window == WindowId(7)),
"pending events are stamped too: {pending:?}"
);
frame(&mut core);
let evs = click_at(&mut core, 50.0, 60.0);
assert!(evs.len() > 1, "click and resize: {evs:?}");
assert!(evs.iter().all(|ev| ev.window == WindowId(7)), "{evs:?}");
}