use std::ptr::NonNull;
use ribir_algo::Sc;
use ribir_geom::{Point, Rect, Size};
use crate::{
query::QueryRef,
state::WriteRef,
widget::{BoxClamp, HitTest, WidgetTree},
widget_tree::WidgetId,
window::Window,
};
pub trait WidgetCtx {
fn widget_id(&self) -> WidgetId;
fn parent(&self) -> Option<WidgetId>;
fn ancestor_of(&self, w: WidgetId) -> bool;
fn successor_of(&self, w: WidgetId) -> bool;
fn widget_parent(&self, w: WidgetId) -> Option<WidgetId>;
fn single_child(&self) -> Option<WidgetId>;
#[inline]
fn assert_single_child(&self) -> WidgetId { self.single_child().expect("Must have one child.") }
fn has_child(&self) -> bool { self.first_child().is_some() }
fn first_child(&self) -> Option<WidgetId>;
fn single_child_box(&self) -> Option<Rect>;
fn box_rect(&self) -> Option<Rect>;
fn box_size(&self) -> Option<Size>;
fn box_pos(&self) -> Option<Point>;
fn layout_clamp(&self) -> Option<BoxClamp>;
fn widget_box_size(&self, wid: WidgetId) -> Option<Size>;
fn widget_box_rect(&self, wid: WidgetId) -> Option<Rect>;
fn widget_box_pos(&self, wid: WidgetId) -> Option<Point>;
fn map_to_global(&self, pos: Point) -> Point;
fn map_from_global(&self, pos: Point) -> Point;
fn map_to_parent(&self, pos: Point) -> Point;
fn map_from_parent(&self, pos: Point) -> Point;
fn map_to(&self, pos: Point, w: WidgetId) -> Point;
fn map_from(&self, pos: Point, w: WidgetId) -> Point;
fn query_all_iter<T: 'static>(&self) -> impl DoubleEndedIterator<Item = QueryRef<T>>;
fn query<T: 'static>(&self) -> Option<QueryRef<T>>;
fn query_write<T: 'static>(&self) -> Option<WriteRef<T>>;
fn query_of_widget<T: 'static>(&self, w: WidgetId) -> Option<QueryRef<T>>;
fn query_write_of_widget<T: 'static>(&self, w: WidgetId) -> Option<WriteRef<T>>;
fn window(&self) -> Sc<Window>;
}
pub(crate) trait WidgetCtxImpl {
fn id(&self) -> WidgetId;
fn tree(&self) -> &WidgetTree;
}
impl<T: WidgetCtxImpl> WidgetCtx for T {
#[inline]
fn widget_id(&self) -> WidgetId { self.id() }
#[inline]
fn parent(&self) -> Option<WidgetId> { self.id().parent(self.tree()) }
#[inline]
fn ancestor_of(&self, w: WidgetId) -> bool { self.id().ancestor_of(w, self.tree()) }
#[inline]
fn successor_of(&self, w: WidgetId) -> bool { w.ancestor_of(self.id(), self.tree()) }
#[inline]
fn widget_parent(&self, w: WidgetId) -> Option<WidgetId> { w.parent(self.tree()) }
#[inline]
fn single_child(&self) -> Option<WidgetId> { self.id().single_child(self.tree()) }
#[inline]
fn first_child(&self) -> Option<WidgetId> { self.id().first_child(self.tree()) }
#[inline]
fn box_rect(&self) -> Option<Rect> { self.widget_box_rect(self.id()) }
#[inline]
fn box_pos(&self) -> Option<Point> { self.widget_box_pos(self.id()) }
#[inline]
fn box_size(&self) -> Option<Size> { self.widget_box_size(self.id()) }
fn layout_clamp(&self) -> Option<BoxClamp> {
self
.tree()
.store
.layout_info(self.id())
.map(|info| info.clamp)
}
fn single_child_box(&self) -> Option<Rect> {
self
.single_child()
.and_then(|c| self.widget_box_rect(c))
}
fn widget_box_size(&self, wid: WidgetId) -> Option<Size> {
self
.tree()
.store
.layout_info(wid)
.and_then(|info| info.size)
}
fn widget_box_pos(&self, wid: WidgetId) -> Option<Point> {
self
.tree()
.store
.layout_info(wid)
.map(|info| info.pos)
}
fn widget_box_rect(&self, wid: WidgetId) -> Option<Rect> {
self
.tree()
.store
.layout_info(wid)
.and_then(|info| info.size.map(|size| Rect::new(info.pos, size)))
}
fn map_to_global(&self, pos: Point) -> Point { self.tree().map_to_global(pos, self.id()) }
fn map_from_global(&self, pos: Point) -> Point { self.tree().map_from_global(pos, self.id()) }
fn map_to_parent(&self, pos: Point) -> Point { self.tree().map_to_parent(self.id(), pos) }
fn map_from_parent(&self, pos: Point) -> Point { self.tree().map_from_parent(self.id(), pos) }
fn map_to(&self, pos: Point, w: WidgetId) -> Point {
let global = self.map_to_global(pos);
self.tree().map_from_global(global, w)
}
fn map_from(&self, pos: Point, w: WidgetId) -> Point {
let global = self.tree().map_to_global(pos, w);
self.map_from_global(global)
}
fn query_all_iter<Q: 'static>(&self) -> impl DoubleEndedIterator<Item = QueryRef<Q>> {
self.id().query_all_iter(self.tree())
}
fn query<Q: 'static>(&self) -> Option<QueryRef<Q>> { self.query_of_widget::<Q>(self.id()) }
#[inline]
fn query_write<Q: 'static>(&self) -> Option<WriteRef<Q>> {
self.query_write_of_widget::<Q>(self.id())
}
fn query_of_widget<Q: 'static>(&self, w: WidgetId) -> Option<QueryRef<Q>> {
w.query_ref::<Q>(self.tree())
}
fn query_write_of_widget<Q: 'static>(&self, w: WidgetId) -> Option<WriteRef<Q>> {
w.query_write(self.tree())
}
fn window(&self) -> Sc<Window> { self.tree().window() }
}
macro_rules! define_widget_context {
(
$(#[$outer:meta])*
$name: ident $(, $extra_name: ident: $extra_ty: ty)*
) => {
$(#[$outer])*
pub struct $name {
pub(crate) id: WidgetId,
pub(crate) tree: NonNull<WidgetTree>,
$(pub(crate) $extra_name: $extra_ty,)*
}
impl WidgetCtxImpl for $name {
#[inline]
fn id(&self) -> WidgetId { self.id }
fn tree(&self) -> &WidgetTree {
unsafe {self.tree.as_ref()}
}
}
};
}
pub(crate) use define_widget_context;
define_widget_context!(HitTestCtx);
impl HitTestCtx {
pub fn box_hit_test(&self, pos: Point) -> HitTest {
let is_hit = self
.box_rect()
.map_or(false, |rect| rect.contains(pos));
HitTest { hit: is_hit, can_hit_child: is_hit }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
prelude::*,
reset_test_env,
test_helper::{MockBox, TestWindow},
};
define_widget_context!(TestCtx);
#[test]
fn map_self_eq_self() {
reset_test_env!();
let w = fn_widget! {
@MockBox {
size: Size::zero(),
margin: EdgeInsets::all(2.),
}
};
let mut wnd = TestWindow::new(w);
wnd.draw_frame();
let tree = wnd.tree();
let root = tree.root();
let pos = Point::zero();
let child = root.single_child(tree).unwrap();
let w_ctx = TestCtx { id: child, tree: wnd.tree };
assert_eq!(w_ctx.map_from(pos, child), pos);
assert_eq!(w_ctx.map_to(pos, child), pos);
}
#[test]
fn map_transform_test() {
reset_test_env!();
let w = fn_widget! {
@MockBox {
size: Size::new(100., 100.),
@MockBox {
transform: Transform::scale(0.5, 0.5),
anchor: Anchor::left_top(30., 30.),
size: Size::new(40., 40.)
}
}
};
let mut wnd = TestWindow::new_with_size(w, Size::new(100., 100.));
wnd.draw_frame();
let root = wnd.tree().root();
let child = get_single_child_by_depth(root, wnd.tree(), 2);
let w_ctx = TestCtx { id: root, tree: wnd.tree };
let from_pos = Point::new(30., 30.);
assert_eq!(w_ctx.map_from(from_pos, child), Point::new(45., 45.));
let to_pos = Point::new(50., 50.);
assert_eq!(w_ctx.map_to(to_pos, child), Point::new(40., 40.));
}
fn get_single_child_by_depth(id: WidgetId, tree: &WidgetTree, mut depth: u32) -> WidgetId {
let mut child = id;
while depth > 0 {
child = child.single_child(tree).unwrap();
depth -= 1;
}
child
}
}