pub(crate) use crate::widget_tree::*;
use crate::{context::*, prelude::*};
use ribir_algo::ShareResource;
use rxrust::subscription::{BoxSubscription, SubscriptionGuard};
#[doc(hidden)]
pub use std::{
any::{Any, TypeId},
marker::PhantomData,
ops::Deref,
};
use std::{cell::RefCell, rc::Rc};
pub trait Compose: Sized {
fn compose(this: State<Self>) -> Widget;
}
pub struct HitTest {
pub hit: bool,
pub can_hit_child: bool,
}
pub trait Render: Query {
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size;
fn paint(&self, ctx: &mut PaintingCtx);
fn only_sized_by_parent(&self) -> bool { false }
fn can_overflow(&self) -> bool { false }
fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest {
let is_hit = hit_test_impl(ctx, pos);
HitTest { hit: is_hit, can_hit_child: is_hit }
}
fn get_transform(&self) -> Option<Transform> { None }
}
pub(crate) fn hit_test_impl(ctx: &HitTestCtx, pos: Point) -> bool {
ctx.box_rect().map_or(false, |rect| rect.contains(pos))
}
pub enum Widget {
Compose(Box<dyn for<'r> FnOnce(&'r BuildCtx) -> Widget>),
Render {
render: Box<dyn Render>,
children: Option<Vec<Widget>>,
},
}
pub trait Query {
fn query_all(
&self,
type_id: TypeId,
callback: &mut dyn FnMut(&dyn Any) -> bool,
order: QueryOrder,
);
}
#[derive(Clone, Copy)]
pub enum QueryOrder {
InnerFirst,
OutsideFirst,
}
pub trait QueryFiler {
fn query_filter(&self, type_id: TypeId) -> Option<&dyn Any>;
fn query_filter_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any>;
}
pub trait IntoWidget<M: ImplMarker> {
fn into_widget(self) -> Widget;
}
impl<W: 'static> QueryFiler for W {
#[inline]
fn query_filter(&self, type_id: TypeId) -> Option<&dyn Any> {
(self.type_id() == type_id).then_some(self as &dyn Any)
}
#[inline]
fn query_filter_mut(&mut self, type_id: TypeId) -> Option<&mut dyn Any> {
((*self).type_id() == type_id).then_some(self as &mut dyn Any)
}
}
impl<'a> dyn Render + 'a {
#[inline]
pub fn query_all_type<T: Any>(&self, mut callback: impl FnMut(&T) -> bool, order: QueryOrder) {
self.query_all(
TypeId::of::<T>(),
&mut |a: &dyn Any| a.downcast_ref().map_or(true, &mut callback),
order,
)
}
pub fn query_on_first_type<T: Any>(&self, order: QueryOrder, callback: impl FnOnce(&T)) {
let mut callback = Some(callback);
self.query_all_type(
move |a| {
let cb = callback.take().expect("should only call once");
cb(a);
false
},
order,
);
}
pub fn contain_type<T: Any>(&self) -> bool {
let mut hit = false;
self.query_all_type(
|_: &T| {
hit = true;
false
},
QueryOrder::OutsideFirst,
);
hit
}
}
pub trait ImplMarker {}
pub struct SelfImpl;
pub struct NotSelf<M>(PhantomData<fn(M)>);
impl ImplMarker for SelfImpl {}
impl<M> ImplMarker for NotSelf<M> {}
impl IntoWidget<SelfImpl> for Widget {
#[inline]
fn into_widget(self) -> Widget { self }
}
macro_rules! impl_compose_into_widget {
($ty: ty) => {
impl<C: Compose> IntoWidget<NotSelf<[(); 0]>> for $ty {
#[inline]
fn into_widget(self) -> Widget { Compose::compose(State::<C>::from(self)) }
}
};
}
impl_compose_into_widget!(State<C>);
impl_compose_into_widget!(C);
impl_compose_into_widget!(Stateful<C>);
impl<R: Render + 'static> IntoWidget<NotSelf<[(); 1]>> for R {
#[inline]
fn into_widget(self) -> Widget {
Widget::Render {
render: Box::new(self),
children: None,
}
}
}
impl<M1, M2, W> IntoWidget<NotSelf<(M1, M2)>> for State<W>
where
W: IntoWidget<M1>,
Stateful<W>: IntoWidget<M2>,
M1: ImplMarker,
M2: ImplMarker,
{
fn into_widget(self) -> crate::widget::Widget {
match self {
State::Stateless(w) => w.into_widget(),
State::Stateful(s) => s.into_widget(),
}
}
}
macro_rules! impl_compose_option_child_into_widget {
($ty: ty) => {
impl<T, C> IntoWidget<NotSelf<[(); 2]>> for $ty
where
T: ComposeChild<Child = Option<C>> + 'static,
{
#[inline]
fn into_widget(self) -> Widget { ComposeChild::compose_child(State::<T>::from(self), None) }
}
};
}
impl_compose_option_child_into_widget!(Stateful<T>);
impl_compose_option_child_into_widget!(T);
impl<F, R, M> IntoWidget<NotSelf<[M; 3]>> for F
where
F: FnOnce(&BuildCtx) -> R + 'static,
R: IntoWidget<M>,
M: ImplMarker,
{
#[inline]
fn into_widget(self) -> Widget { Widget::Compose(Box::new(move |ctx| self(ctx).into_widget())) }
}
#[macro_export]
macro_rules! impl_proxy_query {
(reverse [$first: expr $(, $rest: expr)*] $($reversed: expr)*) => {
impl_proxy_query!(reverse [$($rest),*] $first $($reversed)*);
};
(reverse [] $($reversed: expr)*) => { $($reversed)* };
(
$($self: ident .$name: ident $(($($args: ident),*))?),+
) => {
#[inline]
fn query_all(
&self,
type_id: TypeId,
callback: &mut dyn FnMut(&dyn Any) -> bool,
order: QueryOrder,
) {
let mut query_more = true;
match order {
QueryOrder::InnerFirst => {
impl_proxy_query!(reverse
[$(
if query_more {
self.$name $(($($args),*))?
.query_all(
type_id,
&mut |any| {
query_more = callback(any);
query_more
},
order,
);
}
),+]
);
if let Some(a) = self.query_filter(type_id) {
callback(a);
}
}
QueryOrder::OutsideFirst => {
if let Some(a) = self.query_filter(type_id) {
query_more = callback(a);
}
if query_more {
$(
if query_more {
self.$name $(($($args),*))?
.query_all(
type_id,
&mut |any| {
query_more = callback(any);
query_more
},
order,
);
}
)+
}
}
}
}
};
}
#[macro_export]
macro_rules! impl_query_self_only {
() => {
#[inline]
fn query_all(
&self,
type_id: TypeId,
callback: &mut dyn FnMut(&dyn Any) -> bool,
_: QueryOrder,
) {
if let Some(a) = self.query_filter(type_id) {
callback(a);
}
}
};
}
impl<T: Render> Render for ribir_algo::ShareResource<T> {
#[inline]
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
T::perform_layout(self, clamp, ctx)
}
#[inline]
fn paint(&self, ctx: &mut PaintingCtx) { T::paint(self, ctx) }
#[inline]
fn only_sized_by_parent(&self) -> bool { T::only_sized_by_parent(self) }
#[inline]
fn can_overflow(&self) -> bool { T::can_overflow(self) }
#[inline]
fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest { T::hit_test(self, ctx, pos) }
#[inline]
fn get_transform(&self) -> Option<Transform> { T::get_transform(self) }
}
impl<T: Query> Query for ShareResource<T> {
fn query_all(
&self,
type_id: TypeId,
callback: &mut dyn FnMut(&dyn Any) -> bool,
order: QueryOrder,
) {
(**self).query_all(type_id, callback, order)
}
}
#[macro_export]
macro_rules! impl_proxy_render {
($($proxy: tt)*) => {
#[inline]
fn perform_layout(&self, clamp: BoxClamp, ctx: &mut LayoutCtx) -> Size {
self.$($proxy)*.perform_layout(clamp, ctx)
}
#[inline]
fn paint(&self, ctx: &mut PaintingCtx) { self.$($proxy)*.paint(ctx) }
#[inline]
fn only_sized_by_parent(&self) -> bool {
self.$($proxy)*.only_sized_by_parent()
}
#[inline]
fn can_overflow(&self) -> bool { self.$($proxy)*.can_overflow() }
#[inline]
fn hit_test(&self, ctx: &HitTestCtx, pos: Point) -> HitTest {
self.$($proxy)*.hit_test(ctx, pos)
}
#[inline]
fn get_transform(&self) -> Option<Transform> {
self.$($proxy)*.get_transform()
}
};
}
impl<W: Render + 'static> Render for RefCell<W> {
impl_proxy_render!(borrow());
}
impl<W: Query + 'static> Query for RefCell<W> {
impl_proxy_query!(self.borrow());
}
impl<W: Render + 'static> Render for Rc<W> {
impl_proxy_render!(deref());
}
impl<W: Query + 'static> Query for Rc<W> {
impl_proxy_query!(self.deref());
}
impl Render for Box<dyn Render> {
impl_proxy_render!(deref());
}
impl Query for Box<dyn Render> {
impl_proxy_query!(self.deref());
}
impl Query for Vec<SubscriptionGuard<BoxSubscription<'static>>> {
impl_query_self_only!();
}
#[inline]
pub const fn from<W>(v: W) -> W { v }
#[inline]
pub fn then<W>(b: bool, f: impl FnOnce() -> W) -> Option<W> { b.then(f) }
#[inline]
pub fn map<T, W>(value: T, f: impl FnOnce(T) -> W) -> W { f(value) }