use super::*;
use crate::render::Style;
use crate::ui::{text, MouseEvent, MouseKind, UiEvent};
#[test]
fn handles_survive_store_drop_and_double_remove() {
let overlays = Overlays::new();
overlays.ensure_root(Size::new(10, 4));
let h = overlays.layer(5, Rect::new(1, 1, 4, 2));
assert!(h.is_alive());
h.remove();
assert!(!h.is_alive());
h.remove(); h.set_opacity(0.5); let h2 = overlays.layer(5, Rect::new(0, 0, 2, 2));
drop(overlays);
h2.set_visible(false);
assert!(!h2.is_alive());
}
#[test]
fn removing_a_layer_damages_the_root_underneath() {
let overlays = Overlays::new();
overlays.ensure_root(Size::new(10, 4));
let mut scratch = Vec::new();
{
let mut store = overlays.store().borrow_mut();
let i = store.index_of(ROOT_LAYER_ID).unwrap();
store.layers[i].surface_mut().take_damage(&mut scratch);
}
let h = overlays.layer(5, Rect::new(2, 1, 4, 2));
h.remove();
let mut store = overlays.store().borrow_mut();
let i = store.index_of(ROOT_LAYER_ID).unwrap();
scratch.clear();
store.layers[i].surface_mut().take_damage(&mut scratch);
assert!(
scratch.iter().any(|r| r.intersects(Rect::new(2, 1, 4, 2))),
"vacated region must repaint from the root: {scratch:?}"
);
}
#[test]
fn draw_layer_repaints_only_when_damaged() {
use std::cell::Cell as StdCell;
let overlays = Overlays::new();
let paints: Rc<StdCell<u32>> = Rc::new(StdCell::new(0));
let p2 = paints.clone();
let h = overlays.layer_draw(1, Rect::new(0, 0, 6, 1), move |canvas, rect| {
p2.set(p2.get() + 1);
canvas.print_styled(rect.origin(), "toast", &Style::new());
});
overlays.draw_all();
assert_eq!(paints.get(), 1, "initial paint");
overlays.draw_all();
assert_eq!(paints.get(), 1, "clean layer skips the closure");
h.damage();
overlays.draw_all();
assert_eq!(paints.get(), 2, "damage() re-runs the closure");
let store = overlays.store().borrow();
let i = store.index_of(h.id).unwrap();
let surface = store.layers[i].surface();
let cell = surface.get(0, 0).expect("cell in bounds");
assert_eq!(surface.glyph_str(cell), "t");
}
#[test]
fn non_modal_overlay_with_focus_owns_keys_until_outside_press() {
use crate::reactive::create_root;
use crate::ui::{Key, KeyEvent};
let overlays = Overlays::new();
let keys: Rc<std::cell::RefCell<u32>> = Rc::new(std::cell::RefCell::new(0));
let k2 = keys.clone();
let (root, ()) = create_root(|cx| {
let view = crate::ui::Element::new()
.style(
crate::layout::Style::default()
.width(crate::layout::Dimension::Percent(1.0))
.height(crate::layout::Dimension::Percent(1.0)),
)
.focusable()
.on(crate::ui::Phase::Bubble, move |c, e| {
if matches!(e, UiEvent::Key(_)) {
*k2.borrow_mut() += 1;
c.stop_propagation();
}
})
.child(text("popup"))
.build();
overlays.layer_tree(10, Rect::new(4, 1, 10, 2), false, cx, view);
});
let key_event = UiEvent::Key(KeyEvent::new(Key::Char('x'), crate::ui::Mods::NONE));
assert_eq!(overlays.dispatch(&key_event), None);
assert_eq!(*keys.borrow(), 0);
let click = |pos: Point| {
UiEvent::Mouse(MouseEvent {
kind: MouseKind::Down(crate::ui::MouseButton::Left),
pos,
mods: crate::ui::Mods::NONE,
})
};
assert!(overlays.dispatch(&click(Point::new(5, 2))).is_some());
assert_eq!(overlays.dispatch(&key_event), Some(true));
assert_eq!(*keys.borrow(), 1);
assert_eq!(overlays.dispatch(&click(Point::new(0, 0))), None);
assert_eq!(
overlays.dispatch(&key_event),
None,
"keys fall to the root again"
);
assert_eq!(*keys.borrow(), 1);
root.dispose();
}
#[test]
fn non_modal_tree_overlay_owns_pointer_inside_bounds_only() {
use crate::reactive::create_root;
let overlays = Overlays::new();
let hits: Rc<std::cell::RefCell<Vec<Point>>> = Rc::new(std::cell::RefCell::new(Vec::new()));
let h2 = hits.clone();
let (root, ()) = create_root(|cx| {
let view = crate::ui::Element::new()
.style(
crate::layout::Style::default()
.width(crate::layout::Dimension::Percent(1.0))
.height(crate::layout::Dimension::Percent(1.0)),
)
.on(crate::ui::Phase::Bubble, move |c, e| {
if matches!(e, UiEvent::Mouse(_)) {
h2.borrow_mut().push(c.target_rect().origin());
c.stop_propagation();
}
})
.child(text("panel"))
.build();
overlays.layer_tree(10, Rect::new(5, 2, 8, 2), false, cx, view);
});
let click = |pos: Point| {
UiEvent::Mouse(MouseEvent {
kind: MouseKind::Down(crate::ui::MouseButton::Left),
pos,
mods: crate::ui::Mods::NONE,
})
};
assert_eq!(overlays.dispatch(&click(Point::new(6, 3))), Some(true));
assert_eq!(hits.borrow().len(), 1);
assert_eq!(overlays.dispatch(&click(Point::new(0, 0))), None);
assert_eq!(hits.borrow().len(), 1);
root.dispose();
}