pub use crate::widget_tree::{Anchor, AnchorX, AnchorY, WidgetId};
use crate::{prelude::*, wrap_render::WrapRender};
impl Declare for Anchor {
type Builder = FatObj<()>;
#[inline]
fn declarer() -> Self::Builder { FatObj::new(()) }
}
impl_compose_child_for_wrap_render!(Anchor);
impl WrapRender for Anchor {
fn adjust_position(&self, _host: &dyn Render, pos: Point, ctx: &mut PlaceCtx) -> Point {
let clamp = ctx.clamp();
let size = ctx
.widget_box_size(ctx.widget_id())
.unwrap_or_default();
let max_width = clamp.container_width(size.width);
let max_height = clamp.container_height(size.height);
let anchor_pos = self.calculate(Size::new(max_width, max_height), size);
pos + anchor_pos.to_vector()
}
#[inline]
fn wrapper_dirty_phase(&self) -> DirtyPhase { DirtyPhase::Position }
#[cfg(feature = "debug")]
fn debug_type(&self) -> Option<&'static str> { Some("anchor") }
#[cfg(feature = "debug")]
fn debug_properties(&self) -> Option<serde_json::Value> {
let x = format!("{:?}", self.x);
let y = format!("{:?}", self.y);
Some(serde_json::json!({ "x": x, "y": y }))
}
}
pub type CustomAnchorFn<T> = fn(&T, Size, BoxClamp, &mut PlaceCtx) -> Anchor;
pub struct CustomAnchor<T> {
data: T,
anchor: CustomAnchorFn<T>,
}
pub struct CustomAnchorDeclarer<T: 'static> {
data: Option<PipeValue<T>>,
anchor: Option<CustomAnchorFn<T>>,
}
impl<T: 'static> CustomAnchorDeclarer<T> {
pub fn with_anchor(&mut self, f: CustomAnchorFn<T>) -> &mut Self {
self.anchor = Some(f);
self
}
pub fn with_data<K: ?Sized>(&mut self, data: impl RInto<PipeValue<T>, K>) -> &mut Self {
self.data = Some(data.r_into());
self
}
}
impl<T: 'static> Declare for CustomAnchor<T> {
type Builder = CustomAnchorDeclarer<T>;
fn declarer() -> Self::Builder { CustomAnchorDeclarer { data: None, anchor: None } }
}
impl<T: 'static> ObjDeclarer for CustomAnchorDeclarer<T> {
type Target = FatObj<Stateful<CustomAnchor<T>>>;
fn finish(self) -> Self::Target {
let (data, data_pipe) = self.data.unwrap().unzip();
let anchor = self.anchor.unwrap();
let state = Stateful::new(CustomAnchor { data, anchor });
let writer = state.clone_writer();
let mut fat = FatObj::new(state);
if let Some(pipe) = data_pipe {
let handle = pipe
.subscribe(move |v| {
writer.write().data = v;
})
.unsubscribe_when_dropped();
fat.on_disposed(move |_| drop(handle));
}
fat
}
}
impl<T: 'static> WrapRender for CustomAnchor<T> {
fn adjust_position(&self, _host: &dyn Render, pos: Point, ctx: &mut PlaceCtx) -> Point {
let id = ctx.widget_id();
let clamp = ctx
.tree()
.store
.layout_info(id)
.map_or(BoxClamp::default(), |i| i.clamp);
let size = ctx.widget_box_size(id).unwrap_or_default();
let anchor = (self.anchor)(&self.data, size, clamp, ctx);
let parent_size = id
.parent(ctx.tree())
.and_then(|p| ctx.tree().store.layout_box_size(p))
.unwrap_or(clamp.max);
let offset = anchor.calculate(parent_size, size);
pos + offset.to_vector()
}
#[inline]
fn wrapper_dirty_phase(&self) -> DirtyPhase { DirtyPhase::Position }
}
impl<'c, T: 'static> ComposeChild<'c> for CustomAnchor<T> {
type Child = Widget<'c>;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
WrapRender::combine_child(this, child)
}
}