use crate::*;
use std::any::Any;
pub struct StateHandle<S> {
pub(crate) id: ViewId,
phantom: std::marker::PhantomData<S>,
}
impl<S> Copy for StateHandle<S> {}
impl<S> Clone for StateHandle<S> {
fn clone(&self) -> Self {
*self
}
}
impl<S: 'static> StateHandle<S> {
pub fn new(id: ViewId) -> Self {
Self {
id,
phantom: Default::default(),
}
}
pub fn setter(self) -> impl Fn(S, &mut Context) {
move |s, cx| cx[self] = s
}
}
impl<S: 'static> Binding<S> for StateHandle<S> {
fn get<'a>(&self, cx: &'a Context) -> &'a S {
cx.get(*self)
}
fn get_mut<'a>(&self, cx: &'a mut Context) -> &'a mut S {
cx.get_mut(*self)
}
}
#[derive(Clone)]
struct StateView<D, F> {
default: D,
func: F,
}
impl<S, V, D, F> View for StateView<D, F>
where
V: View,
S: 'static,
D: Fn() -> S + 'static,
F: Fn(StateHandle<S>, &Context) -> V + 'static,
{
fn process(
&self,
event: &Event,
id: ViewId,
cx: &mut Context,
actions: &mut Vec<Box<dyn Any>>,
) {
cx.init_state(id, &self.default);
(self.func)(StateHandle::new(id), cx).process(event, id.child(&0), cx, actions);
}
fn draw(&self, id: ViewId, args: &mut DrawArgs) {
args.cx.init_state(id, &self.default);
(self.func)(StateHandle::new(id), args.cx).draw(id.child(&0), args);
}
fn layout(&self, id: ViewId, args: &mut LayoutArgs) -> LocalSize {
args.cx.init_state(id, &self.default);
let mut compute_layout = true;
if let Some(deps) = args.cx.deps.get(&id) {
let mut any_dirty = false;
for dep in deps {
if let Some(holder) = args.cx.state_map.get_mut(dep) {
if holder.dirty {
any_dirty = true;
break;
}
}
}
compute_layout = any_dirty;
}
if compute_layout {
args.cx.id_stack.push(id);
let view = (self.func)(StateHandle::new(id), args.cx);
let child_size = view.layout(id.child(&0), args);
let mut deps = vec![];
deps.append(&mut args.cx.id_stack.clone());
view.gc(id.child(&0), args.cx, &mut deps);
args.cx.deps.insert(id, deps);
args.cx.layout.insert(
id,
LayoutBox {
rect: LocalRect::new(LocalPoint::zero(), child_size),
offset: LocalOffset::zero(),
},
);
args.cx.id_stack.pop();
}
args.cx.layout[&id].rect.size
}
fn dirty(&self, id: ViewId, xform: LocalToWorld, cx: &mut Context) {
let default = &self.default;
let holder = cx.state_map.entry(id).or_insert_with(|| StateHolder {
state: Box::new((default)()),
dirty: false,
});
if holder.dirty {
let rect = cx.layout[&id].rect;
let pts: [LocalPoint; 4] = [
rect.min(),
[rect.max_x(), rect.min_y()].into(),
[rect.min_x(), rect.max_y()].into(),
rect.max(),
];
let world_pts = pts.map(|p| xform.transform_point(p));
cx.dirty_region.add_rect(WorldRect::from_points(world_pts));
} else {
(self.func)(StateHandle::new(id), cx).dirty(id.child(&0), xform, cx);
}
}
fn hittest(&self, id: ViewId, pt: LocalPoint, cx: &mut Context) -> Option<ViewId> {
cx.init_state(id, &self.default);
(self.func)(StateHandle::new(id), cx).hittest(id.child(&0), pt, cx)
}
fn commands(&self, id: ViewId, cx: &mut Context, cmds: &mut Vec<CommandInfo>) {
cx.init_state(id, &self.default);
(self.func)(StateHandle::new(id), cx).commands(id.child(&0), cx, cmds);
}
fn gc(&self, id: ViewId, cx: &mut Context, map: &mut Vec<ViewId>) {
cx.init_state(id, &self.default);
map.push(id);
(self.func)(StateHandle::new(id), cx).gc(id.child(&0), cx, map);
}
fn access(
&self,
id: ViewId,
cx: &mut Context,
nodes: &mut Vec<(accesskit::NodeId, accesskit::Node)>,
) -> Option<accesskit::NodeId> {
cx.init_state(id, &self.default);
(self.func)(StateHandle::new(id), cx).access(id.child(&0), cx, nodes)
}
}
impl<S, F> private::Sealed for StateView<S, F> {}
pub fn state<
S: 'static,
V: View,
D: Fn() -> S + 'static,
F: Fn(StateHandle<S>, &Context) -> V + 'static,
>(
initial: D,
f: F,
) -> impl View {
StateView {
default: initial,
func: f,
}
}
pub fn with_cx<V: View, F: Fn(&Context) -> V + 'static>(f: F) -> impl View {
state(|| (), move |_, cx| f(cx))
}
pub fn with_ref<V: View, F: Fn(&T) -> V + 'static, T>(binding: impl Binding<T>, f: F) -> impl View {
with_cx(move |cx| f(binding.get(cx)))
}