use ribir_types::{Point, Size, Transform};
use smallvec::SmallVec;
use widget_id::RenderQueryable;
use crate::prelude::*;
pub trait WrapRender {
fn measure(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut MeasureCtx) -> Size {
host.measure(clamp, ctx)
}
fn place_children(&self, size: Size, host: &dyn Render, ctx: &mut PlaceCtx) {
host.place_children(size, ctx)
}
fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) { host.paint(ctx) }
fn size_affected_by_child(&self, host: &dyn Render) -> bool {
host.size_affected_by_child()
}
fn hit_test(&self, host: &dyn Render, ctx: &mut HitTestCtx, pos: Point) -> HitTest {
host.hit_test(ctx, pos)
}
fn get_transform(&self, host: &dyn Render) -> Option<Transform> { host.get_transform() }
fn dirty_phase(&self, host: &dyn Render) -> DirtyPhase { host.dirty_phase() }
fn adjust_position(&self, host: &dyn Render, pos: Point, ctx: &mut PlaceCtx) -> Point {
host.adjust_position(pos, ctx)
}
#[cfg(feature = "debug")]
fn debug_type(&self) -> Option<&'static str> { None }
#[cfg(feature = "debug")]
fn debug_properties(&self) -> Option<serde_json::Value> { None }
fn wrapper_dirty_phase(&self) -> DirtyPhase;
fn combine_x_multi_child(this: impl StateWriter<Value = Self>, x: XMultiChild) -> XMultiChild
where
Self: Sized + 'static,
{
let combine = combine_method::<Self>(this);
let parent = CombinedParent { combine: Box::new(combine), parent: x.0 };
XChild::from_boxed(Box::new(parent))
}
fn combine_x_single_child(this: impl StateWriter<Value = Self>, x: XSingleChild) -> XSingleChild
where
Self: Sized + 'static,
{
let combine = combine_method::<Self>(this);
let parent = CombinedParent { combine: Box::new(combine), parent: x.0 };
XChild::from_boxed(Box::new(parent))
}
fn combine_child(this: impl StateWriter<Value = Self>, child: Widget) -> Widget
where
Self: Sized + 'static,
{
let combine = combine_method::<Self>(this);
combine(child)
}
}
struct RenderPair {
wrapper: Box<dyn WrapRender>,
host: Box<dyn RenderQueryable>,
}
impl Query for RenderPair {
fn query_all<'q>(&'q self, query_id: &QueryId, out: &mut SmallVec<[QueryHandle<'q>; 1]>) {
self.host.query_all(query_id, out)
}
fn query_all_write<'q>(&'q self, query_id: &QueryId, out: &mut SmallVec<[QueryHandle<'q>; 1]>) {
self.host.query_all_write(query_id, out)
}
fn query<'q>(&'q self, query_id: &QueryId) -> Option<QueryHandle<'q>> {
self.host.query(query_id)
}
fn query_write<'q>(&'q self, query_id: &QueryId) -> Option<QueryHandle<'q>> {
self.host.query_write(query_id)
}
fn queryable(&self) -> bool { self.host.queryable() }
}
impl Render for RenderPair {
fn measure(&self, clamp: BoxClamp, ctx: &mut MeasureCtx) -> Size {
self.wrapper.measure(clamp, &*self.host, ctx)
}
fn place_children(&self, size: Size, ctx: &mut PlaceCtx) {
self
.wrapper
.place_children(size, &*self.host, ctx)
}
fn paint(&self, ctx: &mut PaintingCtx) { self.wrapper.paint(&*self.host, ctx); }
fn size_affected_by_child(&self) -> bool { self.wrapper.size_affected_by_child(&*self.host) }
fn hit_test(&self, ctx: &mut HitTestCtx, pos: Point) -> HitTest {
self
.wrapper
.hit_test(self.host.as_render(), ctx, pos)
}
fn dirty_phase(&self) -> DirtyPhase { self.wrapper.dirty_phase(self.host.as_render()) }
fn get_transform(&self) -> Option<Transform> { self.wrapper.get_transform(self.host.as_render()) }
fn adjust_position(&self, pos: Point, ctx: &mut PlaceCtx) -> Point {
self
.wrapper
.adjust_position(self.host.as_render(), pos, ctx)
}
#[cfg(feature = "debug")]
fn debug_name(&self) -> std::borrow::Cow<'static, str> { self.host.as_render().debug_name() }
#[cfg(feature = "debug")]
fn debug_properties(&self) -> serde_json::Value {
use serde_json::{Map, Value, json};
let host_props = self.host.as_render().debug_properties();
let mut obj: Map<String, Value> = match host_props {
Value::Object(map) => map,
Value::Null => Map::new(),
other => {
let mut map = Map::new();
map.insert("_value".to_string(), other);
map
}
};
if let Some(ty) = self.wrapper.debug_type() {
let wrapper_props = self
.wrapper
.debug_properties()
.unwrap_or(Value::Null);
let entry = json!({ "type": ty, "properties": wrapper_props });
match obj.get_mut("_wrappers") {
Some(Value::Array(arr)) => arr.insert(0, entry),
Some(other) => {
let prev = std::mem::replace(other, Value::Array(vec![entry]));
if !prev.is_null()
&& let Value::Array(arr) = other
{
arr.push(prev);
}
}
None => {
obj.insert("_wrappers".to_string(), Value::Array(vec![entry]));
}
}
}
Value::Object(obj)
}
}
impl<R> WrapRender for R
where
R: StateReader,
R::Value: WrapRender,
{
fn measure(&self, clamp: BoxClamp, host: &dyn Render, ctx: &mut MeasureCtx) -> Size {
self.read().measure(clamp, host, ctx)
}
fn place_children(&self, size: Size, host: &dyn Render, ctx: &mut PlaceCtx) {
self.read().place_children(size, host, ctx)
}
fn paint(&self, host: &dyn Render, ctx: &mut PaintingCtx) { self.read().paint(host, ctx) }
fn size_affected_by_child(&self, host: &dyn Render) -> bool {
self.read().size_affected_by_child(host)
}
fn hit_test(&self, host: &dyn Render, ctx: &mut HitTestCtx, pos: Point) -> HitTest {
self.read().hit_test(host, ctx, pos)
}
fn get_transform(&self, host: &dyn Render) -> Option<Transform> {
self.read().get_transform(host)
}
fn wrapper_dirty_phase(&self) -> DirtyPhase { self.read().wrapper_dirty_phase() }
fn adjust_position(&self, host: &dyn Render, pos: Point, ctx: &mut PlaceCtx) -> Point {
self.read().adjust_position(host, pos, ctx)
}
#[cfg(feature = "debug")]
fn debug_type(&self) -> Option<&'static str> { self.read().debug_type() }
#[cfg(feature = "debug")]
fn debug_properties(&self) -> Option<serde_json::Value> { self.read().debug_properties() }
}
#[macro_export]
macro_rules! impl_compose_child_for_wrap_render {
($name:ty) => {
impl<'c> ComposeChild<'c> for $name {
type Child = Widget<'c>;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}
};
}
pub(crate) use impl_compose_child_for_wrap_render;
pub struct CombinedParent<'p> {
combine: Box<dyn FnOnce(Widget) -> Widget>,
parent: Box<dyn BoxedParent + 'p>,
}
impl<'p> BoxedParent for CombinedParent<'p> {
fn boxed_with_children<'w>(self: Box<Self>, children: Vec<Widget<'w>>) -> Widget<'w>
where
Self: 'w,
{
let Self { combine, parent } = *self;
let widget = parent.boxed_with_children(children);
combine(widget)
}
}
fn combine_method<Wrapper: WrapRender + 'static>(
this: impl StateWriter<Value = Wrapper>,
) -> impl FnOnce(Widget) -> Widget {
let dirty_phase = this.wrapper_dirty_phase();
move |mut host| {
let wrapper: Box<dyn WrapRender> = match this.try_into_value() {
Ok(this) => Box::new(this),
Err(this) => {
let reader = match this.into_reader() {
Ok(r) => r,
Err(s) => {
host = host.dirty_on(s.raw_modifies(), dirty_phase);
s.clone_reader()
}
};
Box::new(reader)
}
};
host.on_build(|id| {
id.wrap_node(BuildCtx::get_mut().tree_mut(), move |host| {
Box::new(RenderPair { wrapper, host })
});
})
}
}