use std::any::Any;
use std::future::Future;
use std::marker::PhantomData;
use std::time::Duration;
use std::{mem, thread};
#[doc(hidden)]
pub use zng_clone_move::*;
use zng_handle::{Handle, WeakHandle};
use zng_task::{self as task, UiTask};
use crate::INSTANT;
#[diagnostic::on_unimplemented(
note = "use `hn!(|args: &{A}| {{ }})` to declare a widget handler from a `FnMut` closure",
note = "use `hn_once!`, `async_hn!` or `async_hn_once!` for other closure types"
)]
pub trait WidgetHandler<A: Clone + 'static>: Any + Send {
fn event(&mut self, args: &A) -> bool;
fn update(&mut self) -> bool {
false
}
fn boxed(self) -> Box<dyn WidgetHandler<A>>
where
Self: Sized,
{
Box::new(self)
}
#[cfg(feature = "dyn_closure")]
fn cfg_boxed(self) -> Box<dyn WidgetHandler<A>>
where
Self: Sized,
{
self.boxed()
}
#[cfg(not(feature = "dyn_closure"))]
fn cfg_boxed(self) -> Self
where
Self: Sized,
{
self
}
}
impl<A: Clone + 'static> WidgetHandler<A> for Box<dyn WidgetHandler<A>> {
fn event(&mut self, args: &A) -> bool {
self.as_mut().event(args)
}
fn update(&mut self) -> bool {
self.as_mut().update()
}
fn boxed(self) -> Box<dyn WidgetHandler<A>>
where
Self: Sized,
{
self
}
}
#[doc(hidden)]
pub struct FnMutWidgetHandler<H> {
handler: H,
}
impl<A, H> WidgetHandler<A> for FnMutWidgetHandler<H>
where
A: Clone + 'static,
H: FnMut(&A) + Send + 'static,
{
fn event(&mut self, args: &A) -> bool {
(self.handler)(args);
false
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn hn<A, H>(handler: H) -> FnMutWidgetHandler<H>
where
A: Clone + 'static,
H: FnMut(&A) + Send + 'static,
{
FnMutWidgetHandler { handler }
}
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn hn<A, H>(handler: H) -> FnMutWidgetHandler<Box<dyn FnMut(&A) + Send>>
where
A: Clone + 'static,
H: FnMut(&A) + Send + 'static,
{
FnMutWidgetHandler {
handler: Box::new(handler),
}
}
#[macro_export]
macro_rules! hn {
($($tt:tt)+) => {
$crate::handler::hn($crate::handler::clmv!{ $($tt)+ })
}
}
#[doc(inline)]
pub use crate::hn;
use crate::{AppControlFlow, HeadlessApp};
#[doc(hidden)]
pub struct FnOnceWidgetHandler<H> {
handler: Option<H>,
}
impl<A, H> WidgetHandler<A> for FnOnceWidgetHandler<H>
where
A: Clone + 'static,
H: FnOnce(&A) + Send + 'static,
{
fn event(&mut self, args: &A) -> bool {
if let Some(handler) = self.handler.take() {
handler(args);
}
false
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn hn_once<A, H>(handler: H) -> FnOnceWidgetHandler<H>
where
A: Clone + 'static,
H: FnOnce(&A) + Send + 'static,
{
FnOnceWidgetHandler { handler: Some(handler) }
}
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn hn_once<A, H>(handler: H) -> FnOnceWidgetHandler<Box<dyn FnOnce(&A) + Send>>
where
A: Clone + 'static,
H: FnOnce(&A) + Send + 'static,
{
FnOnceWidgetHandler {
handler: Some(Box::new(handler)),
}
}
#[macro_export]
macro_rules! hn_once {
($($tt:tt)+) => {
$crate::handler::hn_once($crate::handler::clmv! { $($tt)+ })
}
}
#[doc(inline)]
pub use crate::hn_once;
#[doc(hidden)]
pub struct AsyncFnMutWidgetHandler<H> {
handler: H,
tasks: Vec<UiTask<()>>,
}
impl<A, F, H> WidgetHandler<A> for AsyncFnMutWidgetHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnMut(A) -> F + Send + 'static,
{
fn event(&mut self, args: &A) -> bool {
let handler = &mut self.handler;
let mut task = UiTask::new(Some(WIDGET.id()), handler(args.clone()));
let need_update = task.update().is_none();
if need_update {
self.tasks.push(task);
}
need_update
}
fn update(&mut self) -> bool {
self.tasks.retain_mut(|t| t.update().is_none());
!self.tasks.is_empty()
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn async_hn<A, F, H>(handler: H) -> AsyncFnMutWidgetHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnMut(A) -> F + Send + 'static,
{
AsyncFnMutWidgetHandler { handler, tasks: vec![] }
}
#[cfg(feature = "dyn_closure")]
type BoxedAsyncHn<A> = Box<dyn FnMut(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn async_hn<A, F, H>(mut handler: H) -> AsyncFnMutWidgetHandler<BoxedAsyncHn<A>>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnMut(A) -> F + Send + 'static,
{
AsyncFnMutWidgetHandler {
handler: Box::new(move |args| Box::pin(handler(args))),
tasks: vec![],
}
}
#[macro_export]
macro_rules! async_hn {
($($tt:tt)+) => {
$crate::handler::async_hn($crate::handler::async_clmv_fn! { $($tt)+ })
}
}
#[doc(inline)]
pub use crate::async_hn;
enum AsyncFnOnceWhState<H> {
NotCalled(H),
Pending(UiTask<()>),
Done,
}
#[doc(hidden)]
pub struct AsyncFnOnceWidgetHandler<H> {
state: AsyncFnOnceWhState<H>,
}
impl<A, F, H> WidgetHandler<A> for AsyncFnOnceWidgetHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnOnce(A) -> F + Send + 'static,
{
fn event(&mut self, args: &A) -> bool {
match mem::replace(&mut self.state, AsyncFnOnceWhState::Done) {
AsyncFnOnceWhState::NotCalled(handler) => {
let mut task = UiTask::new(Some(WIDGET.id()), handler(args.clone()));
let is_pending = task.update().is_none();
if is_pending {
self.state = AsyncFnOnceWhState::Pending(task);
}
is_pending
}
AsyncFnOnceWhState::Pending(t) => {
self.state = AsyncFnOnceWhState::Pending(t);
false
}
AsyncFnOnceWhState::Done => false,
}
}
fn update(&mut self) -> bool {
let mut is_pending = false;
if let AsyncFnOnceWhState::Pending(t) = &mut self.state {
is_pending = t.update().is_none();
if !is_pending {
self.state = AsyncFnOnceWhState::Done;
}
}
is_pending
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn async_hn_once<A, F, H>(handler: H) -> AsyncFnOnceWidgetHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnOnce(A) -> F + Send + 'static,
{
AsyncFnOnceWidgetHandler {
state: AsyncFnOnceWhState::NotCalled(handler),
}
}
#[cfg(feature = "dyn_closure")]
type BoxedAsyncHnOnce<A> = Box<dyn FnOnce(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn async_hn_once<A, F, H>(handler: H) -> AsyncFnOnceWidgetHandler<BoxedAsyncHnOnce<A>>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnOnce(A) -> F + Send + 'static,
{
AsyncFnOnceWidgetHandler {
state: AsyncFnOnceWhState::NotCalled(Box::new(move |args| Box::pin(handler(args)))),
}
}
#[macro_export]
macro_rules! async_hn_once {
($($tt:tt)+) => {
$crate::handler::async_hn_once($crate::handler::async_clmv_fn_once! { $($tt)+ })
}
}
#[doc(inline)]
pub use crate::async_hn_once;
pub trait AppWeakHandle: Send {
fn clone_boxed(&self) -> Box<dyn AppWeakHandle>;
fn unsubscribe(&self);
}
impl<D: Send + Sync + 'static> AppWeakHandle for WeakHandle<D> {
fn clone_boxed(&self) -> Box<dyn AppWeakHandle> {
Box::new(self.clone())
}
fn unsubscribe(&self) {
if let Some(handle) = self.upgrade() {
handle.force_drop();
}
}
}
pub struct AppHandlerArgs<'a> {
pub handle: &'a dyn AppWeakHandle,
pub is_preview: bool,
}
#[diagnostic::on_unimplemented(
note = "use `app_hn!(|args: &{A}, _| {{ }})` to declare an app handler closure",
note = "use `app_hn_once!`, `async_app_hn!` or `async_app_hn_once!` for other closure types"
)]
pub trait AppHandler<A: Clone + 'static>: Any + Send {
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs);
fn boxed(self) -> Box<dyn AppHandler<A>>
where
Self: Sized,
{
Box::new(self)
}
#[cfg(feature = "dyn_closure")]
fn cfg_boxed(self) -> Box<dyn AppHandler<A>>
where
Self: Sized,
{
self.boxed()
}
#[cfg(not(feature = "dyn_closure"))]
fn cfg_boxed(self) -> Self
where
Self: Sized,
{
self
}
}
impl<A: Clone + 'static> AppHandler<A> for Box<dyn AppHandler<A>> {
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
self.as_mut().event(args, handler_args)
}
fn boxed(self) -> Box<dyn AppHandler<A>> {
self
}
}
#[doc(hidden)]
pub struct FnMutAppHandler<H> {
handler: H,
}
impl<A, H> AppHandler<A> for FnMutAppHandler<H>
where
A: Clone + 'static,
H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
{
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
(self.handler)(args, handler_args.handle);
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn app_hn<A, H>(handler: H) -> FnMutAppHandler<H>
where
A: Clone + 'static,
H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
{
FnMutAppHandler { handler }
}
#[cfg(feature = "dyn_closure")]
type BoxedAppHn<A> = Box<dyn FnMut(&A, &dyn AppWeakHandle) + Send>;
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn app_hn<A, H>(handler: H) -> FnMutAppHandler<BoxedAppHn<A>>
where
A: Clone + 'static,
H: FnMut(&A, &dyn AppWeakHandle) + Send + 'static,
{
FnMutAppHandler {
handler: Box::new(handler),
}
}
#[macro_export]
macro_rules! app_hn {
($($tt:tt)+) => {
$crate::handler::app_hn($crate::handler::clmv!{ $($tt)+ })
}
}
#[doc(inline)]
pub use crate::app_hn;
#[doc(hidden)]
pub struct FnOnceAppHandler<H> {
handler: Option<H>,
}
impl<A, H> AppHandler<A> for FnOnceAppHandler<H>
where
A: Clone + 'static,
H: FnOnce(&A) + Send + 'static,
{
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
if let Some(handler) = self.handler.take() {
handler(args);
handler_args.handle.unsubscribe();
} else {
tracing::error!("`app_hn_once!` called after requesting unsubscribe");
}
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn app_hn_once<A, H>(handler: H) -> FnOnceAppHandler<H>
where
A: Clone + 'static,
H: FnOnce(&A) + Send + 'static,
{
FnOnceAppHandler { handler: Some(handler) }
}
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn app_hn_once<A, H>(handler: H) -> FnOnceAppHandler<Box<dyn FnOnce(&A) + Send>>
where
A: Clone + 'static,
H: FnOnce(&A) + Send + 'static,
{
FnOnceAppHandler {
handler: Some(Box::new(handler)),
}
}
#[macro_export]
macro_rules! app_hn_once {
($($tt:tt)+) => {
$crate::handler::app_hn_once($crate::handler::clmv! { $($tt)+ })
}
}
#[doc(inline)]
pub use crate::app_hn_once;
#[doc(hidden)]
pub struct AsyncFnMutAppHandler<H> {
handler: H,
}
impl<A, F, H> AppHandler<A> for AsyncFnMutAppHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
{
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
let handler = &mut self.handler;
let mut task = UiTask::new(None, handler(args.clone(), handler_args.handle.clone_boxed()));
if task.update().is_none() {
if handler_args.is_preview {
UPDATES
.on_pre_update(app_hn!(|_, handle| {
if task.update().is_some() {
handle.unsubscribe();
}
}))
.perm();
} else {
UPDATES
.on_update(app_hn!(|_, handle| {
if task.update().is_some() {
handle.unsubscribe();
}
}))
.perm();
}
}
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn async_app_hn<A, F, H>(handler: H) -> AsyncFnMutAppHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
{
AsyncFnMutAppHandler { handler }
}
#[cfg(feature = "dyn_closure")]
type BoxedAsyncAppHn<A> = Box<dyn FnMut(A, Box<dyn AppWeakHandle>) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn async_app_hn<A, F, H>(mut handler: H) -> AsyncFnMutAppHandler<BoxedAsyncAppHn<A>>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnMut(A, Box<dyn AppWeakHandle>) -> F + Send + 'static,
{
AsyncFnMutAppHandler {
handler: Box::new(move |args, handle| Box::pin(handler(args, handle))),
}
}
#[macro_export]
macro_rules! async_app_hn {
($($tt:tt)+) => {
$crate::handler::async_app_hn($crate::handler::async_clmv_fn! { $($tt)+ })
}
}
#[doc(inline)]
pub use crate::async_app_hn;
#[doc(hidden)]
pub struct AsyncFnOnceAppHandler<H> {
handler: Option<H>,
}
impl<A, F, H> AppHandler<A> for AsyncFnOnceAppHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnOnce(A) -> F + Send + 'static,
{
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
if let Some(handler) = self.handler.take() {
handler_args.handle.unsubscribe();
let mut task = UiTask::new(None, handler(args.clone()));
if task.update().is_none() {
if handler_args.is_preview {
UPDATES
.on_pre_update(app_hn!(|_, handle| {
if task.update().is_some() {
handle.unsubscribe();
}
}))
.perm();
} else {
UPDATES
.on_update(app_hn!(|_, handle| {
if task.update().is_some() {
handle.unsubscribe();
}
}))
.perm();
}
}
} else {
tracing::error!("`async_app_hn_once!` called after requesting unsubscribe");
}
}
}
#[doc(hidden)]
#[cfg(not(feature = "dyn_closure"))]
pub fn async_app_hn_once<A, F, H>(handler: H) -> AsyncFnOnceAppHandler<H>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnOnce(A) -> F + Send + 'static,
{
AsyncFnOnceAppHandler { handler: Some(handler) }
}
#[cfg(feature = "dyn_closure")]
type BoxedAsyncAppHnOnce<A> = Box<dyn FnOnce(A) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send>> + Send>;
#[doc(hidden)]
#[cfg(feature = "dyn_closure")]
pub fn async_app_hn_once<A, F, H>(handler: H) -> AsyncFnOnceAppHandler<BoxedAsyncAppHnOnce<A>>
where
A: Clone + 'static,
F: Future<Output = ()> + Send + 'static,
H: FnOnce(A) -> F + Send + 'static,
{
AsyncFnOnceAppHandler {
handler: Some(Box::new(move |args| Box::pin(handler(args)))),
}
}
#[macro_export]
macro_rules! async_app_hn_once {
($($tt:tt)+) => {
$crate::handler::async_app_hn_once($crate::handler::async_clmv_fn_once! { $($tt)+ })
}
}
#[doc(inline)]
pub use crate::async_app_hn_once;
use crate::update::UPDATES;
use crate::widget::{UiTaskWidget, WIDGET};
pub struct FilterWidgetHandler<A, H, F> {
_args: PhantomData<fn() -> A>,
handler: H,
filter: F,
}
impl<A, H, F> FilterWidgetHandler<A, H, F>
where
A: Clone + 'static,
H: WidgetHandler<A>,
F: FnMut(&A) -> bool + Send + 'static,
{
pub fn new(handler: H, filter: F) -> Self {
Self {
handler,
filter,
_args: PhantomData,
}
}
}
impl<A, H, F> WidgetHandler<A> for FilterWidgetHandler<A, H, F>
where
A: Clone + 'static,
H: WidgetHandler<A>,
F: FnMut(&A) -> bool + Send + 'static,
{
fn event(&mut self, args: &A) -> bool {
if (self.filter)(args) {
self.handler.event(args)
} else {
false
}
}
fn update(&mut self) -> bool {
self.handler.update()
}
}
pub struct FilterAppHandler<A, H, F> {
_args: PhantomData<fn() -> A>,
handler: H,
filter: F,
}
impl<A, H, F> FilterAppHandler<A, H, F>
where
A: Clone + 'static,
H: AppHandler<A>,
F: FnMut(&A) -> bool + Send + 'static,
{
pub fn new(handler: H, filter: F) -> Self {
Self {
handler,
filter,
_args: PhantomData,
}
}
}
impl<A, H, F> AppHandler<A> for FilterAppHandler<A, H, F>
where
A: Clone + 'static,
H: AppHandler<A>,
F: FnMut(&A) -> bool + Send + 'static,
{
fn event(&mut self, args: &A, handler_args: &AppHandlerArgs) {
if (self.filter)(args) {
self.handler.event(args, handler_args);
}
}
}
impl HeadlessApp {
pub fn block_on<A>(&mut self, handler: &mut dyn AppHandler<A>, args: &A, timeout: Duration) -> Result<(), String>
where
A: Clone + 'static,
{
self.block_on_multi(vec![handler], args, timeout)
}
pub fn block_on_multi<A>(&mut self, handlers: Vec<&mut dyn AppHandler<A>>, args: &A, timeout: Duration) -> Result<(), String>
where
A: Clone + 'static,
{
let (pre_len, pos_len) = UPDATES.handler_lens();
let handler_args = AppHandlerArgs {
handle: &Handle::dummy(()).downgrade(),
is_preview: false,
};
for handler in handlers {
handler.event(args, &handler_args);
}
let mut pending = UPDATES.new_update_handlers(pre_len, pos_len);
if !pending.is_empty() {
let start_time = INSTANT.now();
while {
pending.retain(|h| h());
!pending.is_empty()
} {
UPDATES.update(None);
let flow = self.update(false);
if INSTANT.now().duration_since(start_time) >= timeout {
return Err(format!(
"block_on reached timeout of {timeout:?} before the handler task could finish",
));
}
match flow {
AppControlFlow::Poll => continue,
AppControlFlow::Wait => {
thread::yield_now();
continue;
}
AppControlFlow::Exit => return Ok(()),
}
}
}
Ok(())
}
pub fn block_on_fut<F: Future>(&mut self, future: F, timeout: Duration) -> Result<F::Output, String> {
let future = task::with_deadline(future, timeout);
let mut future = std::pin::pin!(future);
let waker = UPDATES.waker(None);
let mut cx = std::task::Context::from_waker(&waker);
loop {
let mut fut_poll = future.as_mut().poll(&mut cx);
let flow = self.update_observe(
|| {
if fut_poll.is_pending() {
fut_poll = future.as_mut().poll(&mut cx);
}
},
true,
);
match fut_poll {
std::task::Poll::Ready(r) => match r {
Ok(r) => return Ok(r),
Err(e) => return Err(e.to_string()),
},
std::task::Poll::Pending => {}
}
match flow {
AppControlFlow::Poll => continue,
AppControlFlow::Wait => {
thread::yield_now();
continue;
}
AppControlFlow::Exit => return Err("app exited".to_owned()),
}
}
}
#[track_caller]
#[cfg(any(test, doc, feature = "test_util"))]
pub fn doc_test<A, H>(args: A, mut handler: H)
where
A: Clone + 'static,
H: AppHandler<A>,
{
let mut app = crate::APP.minimal().run_headless(false);
app.block_on(&mut handler, &args, DOC_TEST_BLOCK_ON_TIMEOUT).unwrap();
}
#[track_caller]
#[cfg(any(test, doc, feature = "test_util"))]
pub fn doc_test_multi<A>(args: A, mut handlers: Vec<Box<dyn AppHandler<A>>>)
where
A: Clone + 'static,
{
let mut app = crate::APP.minimal().run_headless(false);
app.block_on_multi(handlers.iter_mut().map(|h| h.as_mut()).collect(), &args, DOC_TEST_BLOCK_ON_TIMEOUT)
.unwrap()
}
}
#[cfg(any(test, doc, feature = "test_util"))]
const DOC_TEST_BLOCK_ON_TIMEOUT: Duration = Duration::from_secs(60);