use smallvec::SmallVec;
use crate::prelude::*;
#[simple_declare]
pub struct Provider {
#[declare(custom)]
pub provider: Box<dyn Query>,
}
#[macro_export]
macro_rules! providers {
($($q: expr),*) => {
Provider::new(Box::new([$(Box::new($q) as Box<dyn Query>),*]))
};
}
impl Provider {
#[inline]
pub fn new(provider: Box<dyn Query>) -> Self { Provider { provider } }
#[inline]
pub fn of<T: 'static>(ctx: &impl ProviderCtx) -> Option<QueryRef<T>> { ctx.provider_of() }
#[inline]
pub fn write_of<T: 'static>(ctx: &impl ProviderCtx) -> Option<WriteRef<T>> {
ctx.provider_write_of()
}
}
impl ProviderDeclarer {
pub fn provider(mut self, p: impl Query) -> Self {
self.provider = Some(Box::new(p));
self
}
}
impl<'c> ComposeChild<'c> for Provider {
type Child = FnWidget<'c>;
fn compose_child(this: impl StateWriter<Value = Self>, child: Self::Child) -> Widget<'c> {
let provider = this
.try_into_value()
.unwrap_or_else(|_| {
panic!(
"Provider should not be treated as a shared object to be held onto; instead, utilize \
Provider::xxx_of to access its content.",
);
})
.provider;
let f = move |ctx: &mut BuildCtx| {
let id = ctx.pre_alloc();
let pushed = ctx.providers.last() == Some(&id);
if !pushed {
ctx.providers.push(id);
}
let (child, provider) = ctx.consume_root_with_provider(child.into_widget(), provider);
id.attach_data(provider, ctx.tree_mut());
if !pushed {
assert_eq!(ctx.providers.pop(), Some(id));
}
child
};
f.into_widget()
}
}
pub trait ProviderCtx {
fn all_providers<Q: 'static>(&self) -> impl Iterator<Item = QueryRef<Q>>;
fn all_write_providers<Q: 'static>(&self) -> impl Iterator<Item = WriteRef<Q>>;
fn provider_of<Q: 'static>(&self) -> Option<QueryRef<Q>> { self.all_providers().next() }
fn provider_write_of<Q: 'static>(&self) -> Option<WriteRef<Q>> {
self.all_write_providers().next()
}
}
impl ProviderCtx for BuildCtx {
fn all_providers<Q: 'static>(&self) -> impl Iterator<Item = QueryRef<Q>> {
self
.current_providers
.iter()
.rev()
.filter_map(|p| p.query(TypeId::of::<Q>()))
.filter_map(QueryHandle::into_ref)
.chain(
self
.providers
.iter()
.rev()
.filter_map(|id| id.query_ref(self.tree())),
)
}
fn all_write_providers<Q: 'static>(&self) -> impl Iterator<Item = WriteRef<Q>> {
self
.current_providers
.iter()
.rev()
.filter_map(|p| p.query_write(TypeId::of::<Q>()))
.filter_map(QueryHandle::into_mut)
.chain(
self
.providers
.iter()
.rev()
.filter_map(|id| id.query_write(self.tree())),
)
}
}
impl<T: Deref<Target: WidgetCtxImpl>> ProviderCtx for T {
fn all_providers<Q: 'static>(&self) -> impl Iterator<Item = QueryRef<Q>> {
queryable_ancestors(self).filter_map(|id| id.query_ref(self.tree()))
}
fn all_write_providers<Q: 'static>(&self) -> impl Iterator<Item = WriteRef<Q>> {
queryable_ancestors(self).filter_map(|id| id.query_write(self.tree()))
}
}
fn queryable_ancestors(
ctx: &impl Deref<Target: WidgetCtxImpl>,
) -> impl Iterator<Item = WidgetId> + '_ {
let tree = ctx.tree();
ctx
.id()
.ancestors(tree)
.filter(|id| id.queryable(tree))
}
impl<const M: usize> Query for [Box<dyn Query>; M] {
fn query_all<'q>(&'q self, type_id: TypeId, out: &mut SmallVec<[QueryHandle<'q>; 1]>) {
self
.iter()
.for_each(|q| q.query_all(type_id, out))
}
fn query(&self, type_id: TypeId) -> Option<QueryHandle> {
self.iter().find_map(|q| q.query(type_id))
}
fn query_write(&self, type_id: TypeId) -> Option<QueryHandle> {
self.iter().find_map(|q| q.query_write(type_id))
}
fn queryable(&self) -> bool { true }
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{reset_test_env, test_helper::*};
#[test]
fn direct_pass() {
reset_test_env!();
let (value, w_value) = split_value(0);
let w = fn_widget! {
let w_value = w_value.clone_writer();
Provider::new(Box::new(Queryable(1i32))).with_child(fn_widget! {
let v = Provider::of::<i32>(ctx!()).unwrap();
*w_value.write() = *v;
Void
})
};
let mut wnd = TestWindow::new(w);
wnd.draw_frame();
assert_eq!(*value.read(), 1);
}
#[test]
fn indirect_pass() {
reset_test_env!();
let (value, w_value) = split_value(0);
let w = fn_widget! {
let w_value = w_value.clone_writer();
Provider::new(Box::new(Queryable(1i32))).with_child(fn_widget! {
@MockBox {
size: Size::new(1.,1.),
@ {
let v = Provider::of::<i32>(ctx!()).unwrap();
*$w_value.write() = *v;
Void
}
}
})
};
let mut wnd = TestWindow::new(w);
wnd.draw_frame();
assert_eq!(*value.read(), 1);
}
#[test]
fn provider_for_pipe() {
reset_test_env!();
let (value, w_value) = split_value(0);
let (trigger, w_trigger) = split_value(true);
let w = fn_widget! {
let trigger = trigger.clone_watcher();
Provider::new(Box::new(w_value.clone_writer()))
.with_child(fn_widget! {
pipe!(*$trigger).map(move |_| {
let mut v = Provider::write_of::<i32>(ctx!()).unwrap();
*v += 1;
Void
})
})
};
let mut wnd = TestWindow::new(w);
wnd.draw_frame();
assert_eq!(*value.read(), 1);
*w_trigger.write() = false;
wnd.draw_frame();
assert_eq!(*value.read(), 2);
}
}