pub mod base;
pub mod border;
pub mod builder;
pub mod info;
pub mod inspector;
pub mod node;
mod easing;
pub use easing::*;
use atomic::Atomic;
use parking_lot::{Mutex, RwLock};
use std::{
borrow::Cow,
sync::{Arc, atomic::Ordering::Relaxed},
};
use zng_app_context::context_local;
use zng_handle::Handle;
use zng_layout::unit::{DipPoint, DipToPx as _, Layout1d, Layout2d, Px, PxPoint, PxTransform};
use zng_state_map::{OwnedStateMap, StateId, StateMapMut, StateMapRef, StateValue};
use zng_task::UiTask;
use zng_txt::{Txt, formatx};
use zng_var::{AnyVar, AnyVarHookArgs, AnyVarValue, ResponseVar, Var, VarHandle, VarHandles, VarValue};
use zng_view_api::display_list::ReuseRange;
use crate::{
event::{Event, EventArgs, EventHandle, EventHandles},
handler::{AppHandler, AppHandlerArgs, app_hn, app_hn_once},
update::{LayoutUpdates, RenderUpdates, UPDATES, UpdateFlags, UpdateOp, UpdatesTrace},
window::WINDOW,
};
use self::info::{WidgetBorderInfo, WidgetBoundsInfo, WidgetInfo};
#[doc(hidden)]
pub use zng_app_proc_macros::{property_impl, property_meta, widget_new};
pub use zng_app_proc_macros::{property, ui_node, widget, widget_mixin};
#[macro_export]
macro_rules! widget_set {
(
$(#[$skip:meta])*
$($invalid:ident)::+ = $($tt:tt)*
) => {
compile_error!{
"expected `&mut <wgt>;` at the beginning"
}
};
(
$(#[$skip:meta])*
when = $($invalid:tt)*
) => {
compile_error!{
"expected `&mut <wgt>;` at the beginning"
}
};
(
$wgt_mut:ident;
$($tt:tt)*
) => {
$crate::widget::widget_set! {
&mut *$wgt_mut;
$($tt)*
}
};
(
$wgt_borrow_mut:expr;
$($tt:tt)*
) => {
$crate::widget::widget_new! {
new {
let wgt__ = $wgt_borrow_mut;
}
build { }
set { $($tt)* }
}
};
}
#[doc(inline)]
pub use widget_set;
#[macro_export]
macro_rules! widget_impl {
(
$(
$(#[$attr:meta])*
$vis:vis $($property:ident)::+ ($($arg:ident : $arg_ty:ty)*);
)+
) => {
$(
$crate::widget::property_impl! {
attrs { $(#[$attr])* }
vis { $vis }
path { $($property)::* }
args { $($arg:$arg_ty),* }
}
)+
}
}
#[doc(inline)]
pub use widget_impl;
zng_unique_id::unique_id_64! {
pub struct WidgetId;
}
zng_unique_id::impl_unique_id_name!(WidgetId);
zng_unique_id::impl_unique_id_fmt!(WidgetId);
zng_unique_id::impl_unique_id_bytemuck!(WidgetId);
zng_var::impl_from_and_into_var! {
fn from(name: &'static str) -> WidgetId {
WidgetId::named(name)
}
fn from(name: String) -> WidgetId {
WidgetId::named(name)
}
fn from(name: Cow<'static, str>) -> WidgetId {
WidgetId::named(name)
}
fn from(name: char) -> WidgetId {
WidgetId::named(name)
}
fn from(name: Txt) -> WidgetId {
WidgetId::named(name)
}
fn from(id: WidgetId) -> zng_view_api::access::AccessNodeId {
zng_view_api::access::AccessNodeId(id.get())
}
fn from(some: WidgetId) -> Option<WidgetId>;
}
impl serde::Serialize for WidgetId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
let name = self.name();
if name.is_empty() {
use serde::ser::Error;
return Err(S::Error::custom("cannot serialize unnamed `WidgetId`"));
}
name.serialize(serializer)
}
}
impl<'de> serde::Deserialize<'de> for WidgetId {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let name = Txt::deserialize(deserializer)?;
Ok(WidgetId::named(name))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WidgetUpdateMode {
Ignore,
Bubble,
}
pub struct WIDGET;
impl WIDGET {
pub fn is_in_widget(&self) -> bool {
!WIDGET_CTX.is_default()
}
pub fn try_id(&self) -> Option<WidgetId> {
if self.is_in_widget() { Some(WIDGET_CTX.get().id) } else { None }
}
pub fn trace_path(&self) -> Txt {
if let Some(w_id) = WINDOW.try_id() {
if let Some(id) = self.try_id() {
let tree = WINDOW.info();
if let Some(wgt) = tree.get(id) {
wgt.trace_path()
} else {
formatx!("{w_id:?}//<no-info>/{id:?}")
}
} else {
formatx!("{w_id:?}//<no-widget>")
}
} else if let Some(id) = self.try_id() {
formatx!("<no-window>//{id:?}")
} else {
Txt::from_str("<no-widget>")
}
}
pub fn trace_id(&self) -> Txt {
if let Some(id) = self.try_id() {
if WINDOW.try_id().is_some() {
let tree = WINDOW.info();
if let Some(wgt) = tree.get(id) {
wgt.trace_id()
} else {
formatx!("{id:?}")
}
} else {
formatx!("{id:?}")
}
} else {
Txt::from("<no-widget>")
}
}
pub fn id(&self) -> WidgetId {
WIDGET_CTX.get().id
}
pub fn info(&self) -> WidgetInfo {
WINDOW.info().get(WIDGET.id()).expect("widget info not init")
}
pub fn bounds(&self) -> WidgetBoundsInfo {
WIDGET_CTX.get().bounds.lock().clone()
}
pub fn border(&self) -> WidgetBorderInfo {
WIDGET_CTX.get().border.lock().clone()
}
pub fn parent_id(&self) -> Option<WidgetId> {
WIDGET_CTX.get().parent_id.load(Relaxed)
}
pub fn update_op(&self, op: UpdateOp) -> &Self {
match op {
UpdateOp::Update => self.update(),
UpdateOp::Info => self.update_info(),
UpdateOp::Layout => self.layout(),
UpdateOp::Render => self.render(),
UpdateOp::RenderUpdate => self.render_update(),
}
}
fn update_impl(&self, flag: UpdateFlags) -> &Self {
let _ = WIDGET_CTX.get().flags.fetch_update(Relaxed, Relaxed, |mut f| {
if !f.contains(flag) {
f.insert(flag);
Some(f)
} else {
None
}
});
self
}
pub fn update(&self) -> &Self {
UpdatesTrace::log_update();
self.update_impl(UpdateFlags::UPDATE)
}
pub fn update_info(&self) -> &Self {
UpdatesTrace::log_info();
self.update_impl(UpdateFlags::INFO)
}
pub fn layout(&self) -> &Self {
UpdatesTrace::log_layout();
self.update_impl(UpdateFlags::LAYOUT)
}
pub fn render(&self) -> &Self {
UpdatesTrace::log_render();
self.update_impl(UpdateFlags::RENDER)
}
pub fn render_update(&self) -> &Self {
UpdatesTrace::log_render();
self.update_impl(UpdateFlags::RENDER_UPDATE)
}
pub fn reinit(&self) {
let _ = WIDGET_CTX.get().flags.fetch_update(Relaxed, Relaxed, |mut f| {
if !f.contains(UpdateFlags::REINIT) {
f.insert(UpdateFlags::REINIT);
Some(f)
} else {
None
}
});
}
pub fn with_state<R>(&self, f: impl FnOnce(StateMapRef<WIDGET>) -> R) -> R {
f(WIDGET_CTX.get().state.read().borrow())
}
pub fn with_state_mut<R>(&self, f: impl FnOnce(StateMapMut<WIDGET>) -> R) -> R {
f(WIDGET_CTX.get().state.write().borrow_mut())
}
pub fn get_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> Option<T> {
let id = id.into();
self.with_state(|s| s.get_clone(id))
}
pub fn req_state<T: StateValue + Clone>(&self, id: impl Into<StateId<T>>) -> T {
let id = id.into();
self.with_state(|s| s.req(id).clone())
}
pub fn set_state<T: StateValue>(&self, id: impl Into<StateId<T>>, value: impl Into<T>) -> Option<T> {
let id = id.into();
let value = value.into();
self.with_state_mut(|mut s| s.set(id, value))
}
pub fn flag_state(&self, id: impl Into<StateId<()>>) -> bool {
let id = id.into();
self.with_state_mut(|mut s| s.flag(id))
}
pub fn init_state<T: StateValue>(&self, id: impl Into<StateId<T>>, init: impl FnOnce() -> T) {
let id = id.into();
self.with_state_mut(|mut s| {
s.entry(id).or_insert_with(init);
});
}
pub fn init_state_default<T: StateValue + Default>(&self, id: impl Into<StateId<T>>) {
self.init_state(id.into(), Default::default)
}
pub fn contains_state<T: StateValue>(&self, id: impl Into<StateId<T>>) -> bool {
let id = id.into();
self.with_state(|s| s.contains(id))
}
pub fn sub_var_op(&self, op: UpdateOp, var: &impl AnyVar) -> &Self {
let w = WIDGET_CTX.get();
let s = var.subscribe(op, w.id);
fn push(w: Arc<WidgetCtxData>, s: VarHandle) {
if WIDGET_HANDLES_CTX.is_default() {
w.handles.var_handles.lock().push(s);
} else {
WIDGET_HANDLES_CTX.get().var_handles.lock().push(s);
}
}
push(w, s);
self
}
pub fn sub_var_op_when<T: VarValue>(
&self,
op: UpdateOp,
var: &impl Var<T>,
predicate: impl Fn(&T) -> bool + Send + Sync + 'static,
) -> &Self {
let w = WIDGET_CTX.get();
let s = var.subscribe_when(op, w.id, predicate);
fn push(w: Arc<WidgetCtxData>, s: VarHandle) {
if WIDGET_HANDLES_CTX.is_default() {
w.handles.var_handles.lock().push(s);
} else {
WIDGET_HANDLES_CTX.get().var_handles.lock().push(s);
}
}
push(w, s);
self
}
pub fn sub_var(&self, var: &impl AnyVar) -> &Self {
self.sub_var_op(UpdateOp::Update, var)
}
pub fn sub_var_when<T: VarValue>(&self, var: &impl Var<T>, predicate: impl Fn(&T) -> bool + Send + Sync + 'static) -> &Self {
self.sub_var_op_when(UpdateOp::Update, var, predicate)
}
pub fn sub_var_info(&self, var: &impl AnyVar) -> &Self {
self.sub_var_op(UpdateOp::Info, var)
}
pub fn sub_var_info_when<T: VarValue>(&self, var: &impl Var<T>, predicate: impl Fn(&T) -> bool + Send + Sync + 'static) -> &Self {
self.sub_var_op_when(UpdateOp::Info, var, predicate)
}
pub fn sub_var_layout(&self, var: &impl AnyVar) -> &Self {
self.sub_var_op(UpdateOp::Layout, var)
}
pub fn sub_var_layout_when<T: VarValue>(&self, var: &impl Var<T>, predicate: impl Fn(&T) -> bool + Send + Sync + 'static) -> &Self {
self.sub_var_op_when(UpdateOp::Layout, var, predicate)
}
pub fn sub_var_render(&self, var: &impl AnyVar) -> &Self {
self.sub_var_op(UpdateOp::Render, var)
}
pub fn sub_var_render_when<T: VarValue>(&self, var: &impl Var<T>, predicate: impl Fn(&T) -> bool + Send + Sync + 'static) -> &Self {
self.sub_var_op_when(UpdateOp::Render, var, predicate)
}
pub fn sub_var_render_update(&self, var: &impl AnyVar) -> &Self {
self.sub_var_op(UpdateOp::RenderUpdate, var)
}
pub fn sub_var_render_update_when<T: VarValue>(
&self,
var: &impl Var<T>,
predicate: impl Fn(&T) -> bool + Send + Sync + 'static,
) -> &Self {
self.sub_var_op_when(UpdateOp::RenderUpdate, var, predicate)
}
pub fn sub_event<A: EventArgs>(&self, event: &Event<A>) -> &Self {
let w = WIDGET_CTX.get();
let s = event.subscribe(w.id);
fn push(w: Arc<WidgetCtxData>, s: EventHandle) {
if WIDGET_HANDLES_CTX.is_default() {
w.handles.event_handles.lock().push(s);
} else {
WIDGET_HANDLES_CTX.get().event_handles.lock().push(s);
}
}
push(w, s);
self
}
pub fn push_event_handle(&self, handle: EventHandle) {
if WIDGET_HANDLES_CTX.is_default() {
WIDGET_CTX.get().handles.event_handles.lock().push(handle);
} else {
WIDGET_HANDLES_CTX.get().event_handles.lock().push(handle);
}
}
pub fn push_event_handles(&self, handles: EventHandles) {
if WIDGET_HANDLES_CTX.is_default() {
WIDGET_CTX.get().handles.event_handles.lock().extend(handles);
} else {
WIDGET_HANDLES_CTX.get().event_handles.lock().extend(handles);
}
}
pub fn push_var_handle(&self, handle: VarHandle) {
if WIDGET_HANDLES_CTX.is_default() {
WIDGET_CTX.get().handles.var_handles.lock().push(handle);
} else {
WIDGET_HANDLES_CTX.get().var_handles.lock().push(handle);
}
}
pub fn push_var_handles(&self, handles: VarHandles) {
if WIDGET_HANDLES_CTX.is_default() {
WIDGET_CTX.get().handles.var_handles.lock().extend(handles);
} else {
WIDGET_HANDLES_CTX.get().var_handles.lock().extend(handles);
}
}
pub fn win_point_to_wgt(&self, point: DipPoint) -> Option<PxPoint> {
let wgt_info = WIDGET.info();
wgt_info
.inner_transform()
.inverse()?
.transform_point(point.to_px(wgt_info.tree().scale_factor()))
}
pub fn win_to_wgt(&self) -> Option<PxTransform> {
WIDGET.info().inner_transform().inverse()
}
pub fn with_handles<R>(&self, handles: &mut WidgetHandlesCtx, f: impl FnOnce() -> R) -> R {
WIDGET_HANDLES_CTX.with_context(&mut handles.0, f)
}
pub fn with_context<R>(&self, ctx: &mut WidgetCtx, update_mode: WidgetUpdateMode, f: impl FnOnce() -> R) -> R {
let parent_id = WIDGET.try_id();
if let Some(ctx) = ctx.0.as_mut() {
ctx.parent_id.store(parent_id, Relaxed);
} else {
unreachable!()
}
let prev_flags = match update_mode {
WidgetUpdateMode::Ignore => ctx.0.as_mut().unwrap().flags.load(Relaxed),
WidgetUpdateMode::Bubble => UpdateFlags::empty(),
};
let r = WIDGET_CTX.with_context(&mut ctx.0, f);
let ctx = ctx.0.as_mut().unwrap();
match update_mode {
WidgetUpdateMode::Ignore => {
ctx.flags.store(prev_flags, Relaxed);
}
WidgetUpdateMode::Bubble => {
let wgt_flags = ctx.flags.load(Relaxed);
if let Some(parent) = parent_id.map(|_| WIDGET_CTX.get()) {
let propagate = wgt_flags
& (UpdateFlags::UPDATE
| UpdateFlags::INFO
| UpdateFlags::LAYOUT
| UpdateFlags::RENDER
| UpdateFlags::RENDER_UPDATE);
let _ = parent.flags.fetch_update(Relaxed, Relaxed, |mut u| {
if !u.contains(propagate) {
u.insert(propagate);
Some(u)
} else {
None
}
});
ctx.parent_id.store(None, Relaxed);
} else if let Some(window_id) = WINDOW.try_id() {
UPDATES.update_flags_root(wgt_flags, window_id, ctx.id);
ctx.flags.store(wgt_flags & UpdateFlags::REINIT, Relaxed);
} else {
UPDATES.update_flags(wgt_flags, ctx.id);
ctx.flags.store(UpdateFlags::empty(), Relaxed);
}
}
}
r
}
pub fn with_no_context<R>(&self, f: impl FnOnce() -> R) -> R {
WIDGET_CTX.with_default(f)
}
#[cfg(any(test, doc, feature = "test_util"))]
pub(crate) fn test_root_updates(&self) {
let ctx = WIDGET_CTX.get();
UPDATES.update_flags_root(ctx.flags.load(Relaxed), WINDOW.id(), ctx.id);
ctx.flags.store(UpdateFlags::empty(), Relaxed);
}
pub(crate) fn layout_is_pending(&self, layout_widgets: &LayoutUpdates) -> bool {
let ctx = WIDGET_CTX.get();
ctx.flags.load(Relaxed).contains(UpdateFlags::LAYOUT) || layout_widgets.delivery_list().enter_widget(ctx.id)
}
pub(crate) fn take_update(&self, flag: UpdateFlags) -> bool {
let mut r = false;
let _ = WIDGET_CTX.get().flags.fetch_update(Relaxed, Relaxed, |mut f| {
if f.intersects(flag) {
r = true;
f.remove(flag);
Some(f)
} else {
None
}
});
r
}
#[cfg(debug_assertions)]
pub(crate) fn pending_update(&self) -> UpdateFlags {
WIDGET_CTX.get().flags.load(Relaxed)
}
pub(crate) fn take_render_reuse(&self, render_widgets: &RenderUpdates, render_update_widgets: &RenderUpdates) -> Option<ReuseRange> {
let ctx = WIDGET_CTX.get();
let mut try_reuse = true;
let _ = ctx.flags.fetch_update(Relaxed, Relaxed, |mut f| {
if f.intersects(UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE) {
try_reuse = false;
f.remove(UpdateFlags::RENDER | UpdateFlags::RENDER_UPDATE);
Some(f)
} else {
None
}
});
if try_reuse && !render_widgets.delivery_list().enter_widget(ctx.id) && !render_update_widgets.delivery_list().enter_widget(ctx.id)
{
ctx.render_reuse.lock().take()
} else {
None
}
}
pub(crate) fn set_render_reuse(&self, range: Option<ReuseRange>) {
*WIDGET_CTX.get().render_reuse.lock() = range;
}
}
context_local! {
pub(crate) static WIDGET_CTX: WidgetCtxData = WidgetCtxData::no_context();
static WIDGET_HANDLES_CTX: WidgetHandlesCtxData = WidgetHandlesCtxData::dummy();
}
pub struct WidgetCtx(Option<Arc<WidgetCtxData>>);
impl WidgetCtx {
pub fn new(id: WidgetId) -> Self {
Self(Some(Arc::new(WidgetCtxData {
parent_id: Atomic::new(None),
id,
flags: Atomic::new(UpdateFlags::empty()),
state: RwLock::new(OwnedStateMap::default()),
handles: WidgetHandlesCtxData::dummy(),
bounds: Mutex::new(WidgetBoundsInfo::default()),
border: Mutex::new(WidgetBorderInfo::default()),
render_reuse: Mutex::new(None),
})))
}
pub fn deinit(&mut self, retain_state: bool) {
let ctx = self.0.as_mut().unwrap();
ctx.handles.var_handles.lock().clear();
ctx.handles.event_handles.lock().clear();
ctx.flags.store(UpdateFlags::empty(), Relaxed);
*ctx.render_reuse.lock() = None;
if !retain_state {
ctx.state.write().clear();
}
}
pub fn is_pending_reinit(&self) -> bool {
self.0.as_ref().unwrap().flags.load(Relaxed).contains(UpdateFlags::REINIT)
}
pub fn take_reinit(&mut self) -> bool {
let ctx = self.0.as_mut().unwrap();
let mut flags = ctx.flags.load(Relaxed);
let r = flags.contains(UpdateFlags::REINIT);
if r {
flags.remove(UpdateFlags::REINIT);
ctx.flags.store(flags, Relaxed);
}
r
}
pub fn id(&self) -> WidgetId {
self.0.as_ref().unwrap().id
}
pub fn bounds(&self) -> WidgetBoundsInfo {
self.0.as_ref().unwrap().bounds.lock().clone()
}
pub fn border(&self) -> WidgetBorderInfo {
self.0.as_ref().unwrap().border.lock().clone()
}
pub fn with_state<R>(&mut self, f: impl FnOnce(&mut OwnedStateMap<WIDGET>) -> R) -> R {
f(&mut self.0.as_mut().unwrap().state.write())
}
pub fn share(&mut self) -> Self {
Self(self.0.clone())
}
}
pub(crate) struct WidgetCtxData {
parent_id: Atomic<Option<WidgetId>>,
pub(crate) id: WidgetId,
flags: Atomic<UpdateFlags>,
state: RwLock<OwnedStateMap<WIDGET>>,
handles: WidgetHandlesCtxData,
pub(crate) bounds: Mutex<WidgetBoundsInfo>,
border: Mutex<WidgetBorderInfo>,
render_reuse: Mutex<Option<ReuseRange>>,
}
impl WidgetCtxData {
#[track_caller]
fn no_context() -> Self {
panic!("no widget in context")
}
}
struct WidgetHandlesCtxData {
var_handles: Mutex<VarHandles>,
event_handles: Mutex<EventHandles>,
}
impl WidgetHandlesCtxData {
const fn dummy() -> Self {
Self {
var_handles: Mutex::new(VarHandles::dummy()),
event_handles: Mutex::new(EventHandles::dummy()),
}
}
}
pub struct WidgetHandlesCtx(Option<Arc<WidgetHandlesCtxData>>);
impl WidgetHandlesCtx {
pub fn new() -> Self {
Self(Some(Arc::new(WidgetHandlesCtxData::dummy())))
}
pub fn clear(&mut self) {
let h = self.0.as_ref().unwrap();
h.var_handles.lock().clear();
h.event_handles.lock().clear();
}
}
impl Default for WidgetHandlesCtx {
fn default() -> Self {
Self::new()
}
}
pub trait AnyVarSubscribe: AnyVar {
fn subscribe(&self, op: UpdateOp, widget_id: WidgetId) -> VarHandle;
}
impl<V: AnyVar> AnyVarSubscribe for V {
fn subscribe(&self, op: UpdateOp, widget_id: WidgetId) -> VarHandle {
if !self.capabilities().is_always_static() {
self.hook_any(var_subscribe(op, widget_id))
} else {
VarHandle::dummy()
}
}
}
pub trait VarSubscribe<T: VarValue>: Var<T> + AnyVarSubscribe {
fn subscribe_when(&self, op: UpdateOp, widget_id: WidgetId, predicate: impl Fn(&T) -> bool + Send + Sync + 'static) -> VarHandle;
fn on_pre_new<H>(&self, handler: H) -> VarHandle
where
H: AppHandler<OnVarArgs<T>>,
{
var_on_new(self, handler, true)
}
fn on_new<H>(&self, handler: H) -> VarHandle
where
H: AppHandler<OnVarArgs<T>>,
{
var_on_new(self, handler, false)
}
}
impl<T: VarValue, V: Var<T>> VarSubscribe<T> for V {
fn subscribe_when(&self, op: UpdateOp, widget_id: WidgetId, predicate: impl Fn(&T) -> bool + Send + Sync + 'static) -> VarHandle {
self.hook_any(var_subscribe_when(op, widget_id, predicate))
}
}
pub trait ResponseVarSubscribe<T: VarValue> {
fn on_pre_rsp<H>(&self, handler: H) -> VarHandle
where
H: AppHandler<OnVarArgs<T>>;
fn on_rsp<H>(&self, handler: H) -> VarHandle
where
H: AppHandler<OnVarArgs<T>>;
}
impl<T: VarValue> ResponseVarSubscribe<T> for ResponseVar<T> {
fn on_pre_rsp<H>(&self, mut handler: H) -> VarHandle
where
H: AppHandler<OnVarArgs<T>>,
{
if self.is_done() {
return VarHandle::dummy();
}
self.on_pre_new(app_hn!(|args: &OnVarArgs<zng_var::types::Response<T>>, handler_args| {
if let zng_var::types::Response::Done(value) = &args.value {
handler.event(
&OnVarArgs::new(value.clone(), args.tags.iter().map(|t| (*t).clone_boxed()).collect()),
&crate::handler::AppHandlerArgs {
handle: handler_args,
is_preview: true,
},
)
}
}))
}
fn on_rsp<H>(&self, mut handler: H) -> VarHandle
where
H: AppHandler<OnVarArgs<T>>,
{
if self.is_done() {
return VarHandle::dummy();
}
self.on_new(app_hn!(|args: &OnVarArgs<zng_var::types::Response<T>>, handler_args| {
if let zng_var::types::Response::Done(value) = &args.value {
handler.event(
&OnVarArgs::new(value.clone(), args.tags.iter().map(|t| (*t).clone_boxed()).collect()),
&crate::handler::AppHandlerArgs {
handle: handler_args,
is_preview: false,
},
)
}
}))
}
}
fn var_subscribe(op: UpdateOp, widget_id: WidgetId) -> Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync> {
Box::new(move |_| {
UPDATES.update_op(op, widget_id);
true
})
}
fn var_subscribe_when<T: VarValue>(
op: UpdateOp,
widget_id: WidgetId,
when: impl Fn(&T) -> bool + Send + Sync + 'static,
) -> Box<dyn Fn(&AnyVarHookArgs) -> bool + Send + Sync> {
Box::new(move |a| {
if let Some(a) = a.downcast_value::<T>() {
if when(a) {
UPDATES.update_op(op, widget_id);
}
true
} else {
false
}
})
}
fn var_on_new<T>(var: &impl Var<T>, handler: impl AppHandler<OnVarArgs<T>>, is_preview: bool) -> VarHandle
where
T: VarValue,
{
if var.capabilities().is_always_static() {
return VarHandle::dummy();
}
let handler = Arc::new(Mutex::new(handler));
let (inner_handle_owner, inner_handle) = Handle::new(());
var.hook(move |args| {
if inner_handle_owner.is_dropped() {
return false;
}
let handle = inner_handle.downgrade();
let value = args.value().clone();
let tags = args.tags().iter().map(|t| (*t).clone_boxed()).collect();
let update_once = app_hn_once!(handler, value, |_| {
handler.lock().event(
&OnVarArgs::new(value, tags),
&AppHandlerArgs {
handle: &handle,
is_preview,
},
);
});
if is_preview {
UPDATES.on_pre_update(update_once).perm();
} else {
UPDATES.on_update(update_once).perm();
}
true
})
}
pub struct OnVarArgs<T: VarValue> {
pub value: T,
pub tags: Vec<Box<dyn AnyVarValue>>,
}
impl<T: VarValue> OnVarArgs<T> {
pub fn new(value: T, tags: Vec<Box<dyn AnyVarValue>>) -> Self {
Self { value, tags }
}
pub fn downcast_tags<Ta: VarValue>(&self) -> impl Iterator<Item = &Ta> + '_ {
self.tags.iter().filter_map(|t| (*t).as_any().downcast_ref::<Ta>())
}
}
impl<T: VarValue> Clone for OnVarArgs<T> {
fn clone(&self) -> Self {
Self {
value: self.value.clone(),
tags: self.tags.iter().map(|t| (*t).clone_boxed()).collect(),
}
}
}
pub trait VarLayout<T: VarValue>: Var<T> {
fn layout(&self) -> T::Px
where
T: Layout2d,
{
self.with(|s| s.layout())
}
fn layout_dft(&self, default: T::Px) -> T::Px
where
T: Layout2d,
{
self.with(move |s| s.layout_dft(default))
}
fn layout_x(&self) -> Px
where
T: Layout1d,
{
self.with(|s| s.layout_x())
}
fn layout_y(&self) -> Px
where
T: Layout1d,
{
self.with(|s| s.layout_y())
}
fn layout_z(&self) -> Px
where
T: Layout1d,
{
self.with(|s| s.layout_z())
}
fn layout_dft_x(&self, default: Px) -> Px
where
T: Layout1d,
{
self.with(move |s| s.layout_dft_x(default))
}
fn layout_dft_y(&self, default: Px) -> Px
where
T: Layout1d,
{
self.with(move |s| s.layout_dft_y(default))
}
fn layout_dft_z(&self, default: Px) -> Px
where
T: Layout1d,
{
self.with(move |s| s.layout_dft_z(default))
}
}
impl<T: VarValue, V: Var<T>> VarLayout<T> for V {}
pub trait UiTaskWidget<R> {
fn new<F>(target: Option<WidgetId>, task: impl IntoFuture<IntoFuture = F>) -> Self
where
F: Future<Output = R> + Send + 'static;
}
impl<R> UiTaskWidget<R> for UiTask<R> {
fn new<F>(target: Option<WidgetId>, task: impl IntoFuture<IntoFuture = F>) -> Self
where
F: Future<Output = R> + Send + 'static,
{
UiTask::new_raw(UPDATES.waker(target), task)
}
}