use std::{borrow::Cow, marker::PhantomData, ops::Deref};
use crate::view::{AppendArg, View, ViewChild, ViewParent};
#[derive(Default)]
pub struct Proxy<T> {
model: T,
#[expect(clippy::type_complexity, reason = "not that complex")]
update: Option<Box<dyn FnMut(&T) + 'static>>,
}
impl<T> Deref for Proxy<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.model
}
}
impl<T> AsRef<T> for Proxy<T> {
fn as_ref(&self) -> &T {
&self.model
}
}
impl<T: PartialEq> Proxy<T> {
pub fn set(&mut self, t: T) {
if t != self.model {
self.model = t;
if let Some(update) = self.update.as_mut() {
update(&self.model);
}
}
}
}
impl<T> Proxy<T> {
pub fn new(model: T) -> Self {
Self {
model,
update: None,
}
}
pub fn on_update(&mut self, f: impl FnMut(&T) + 'static) {
self.update = Some(Box::new(f))
}
pub fn modify(&mut self, f: impl FnOnce(&mut T)) {
f(&mut self.model);
if let Some(update) = self.update.as_mut() {
update(&self.model);
}
}
}
pub struct ProxyChild<V: View> {
_phantom: PhantomData<V>,
nodes: Vec<V::Node>,
}
impl<V: View> Clone for ProxyChild<V> {
fn clone(&self) -> Self {
Self {
_phantom: PhantomData,
nodes: self.nodes.clone(),
}
}
}
impl<V: View> ViewChild<V> for ProxyChild<V> {
fn as_append_arg(
&self,
) -> crate::prelude::AppendArg<V, impl Iterator<Item = Cow<'_, <V as View>::Node>>> {
AppendArg::new(self.nodes.iter().map(Cow::Borrowed))
}
}
impl<V: View> ProxyChild<V> {
pub fn new(child: impl ViewChild<V>) -> Self {
let mut nodes: Vec<V::Node> = vec![];
for child in child.as_append_arg() {
nodes.push(child.as_ref().clone());
}
Self {
_phantom: PhantomData,
nodes,
}
}
pub fn replace(&mut self, parent: &V::Element, child: impl ViewChild<V>) {
let mut previous_nodes = std::mem::take(&mut self.nodes).into_iter().rev();
let mut new_nodes = child
.as_append_arg()
.map(Cow::into_owned)
.collect::<Vec<_>>()
.into_iter()
.rev();
loop {
match (previous_nodes.next(), new_nodes.next()) {
(Some(prev), Some(new)) => {
parent.replace_node(Cow::Borrowed(&new), Cow::Borrowed(&prev));
self.nodes.push(new);
}
(Some(prev), None) => {
parent.remove_node(Cow::Borrowed(&prev));
}
(None, Some(new)) => {
parent.insert_node_before(
Cow::Borrowed(&new),
self.nodes.last().map(Cow::Borrowed),
);
self.nodes.push(new);
}
(None, None) => {
self.nodes.reverse();
return;
}
}
}
}
}