zng_wgt/
func.rs

1use std::{fmt, ops, sync::Arc};
2
3use crate::prelude::*;
4
5use zng_app::event::{CommandMetaVar, CommandMetaVarId};
6use zng_var::AnyVar;
7#[doc(hidden)]
8pub use zng_wgt::prelude::clmv as __clmv;
9
10type BoxedWgtFn<D> = Box<dyn Fn(D) -> UiNode + Send + Sync>;
11
12/// Boxed shared closure that generates a widget for a given data.
13///
14/// You can also use the [`wgt_fn!`] macro do instantiate.
15///
16/// See `presenter` for a way to quickly use the widget function in the UI.
17pub struct WidgetFn<D: ?Sized>(Option<Arc<BoxedWgtFn<D>>>);
18impl<D> Clone for WidgetFn<D> {
19    fn clone(&self) -> Self {
20        WidgetFn(self.0.clone())
21    }
22}
23impl<D> fmt::Debug for WidgetFn<D> {
24    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25        write!(f, "WidgetFn<{}>", pretty_type_name::pretty_type_name::<D>())
26    }
27}
28impl<D> PartialEq for WidgetFn<D> {
29    fn eq(&self, other: &Self) -> bool {
30        match (&self.0, &other.0) {
31            (None, None) => true,
32            (Some(a), Some(b)) => Arc::ptr_eq(a, b),
33            _ => false,
34        }
35    }
36}
37impl<D> Default for WidgetFn<D> {
38    /// `nil`.
39    fn default() -> Self {
40        Self::nil()
41    }
42}
43impl<D> WidgetFn<D> {
44    /// New from a closure that generates a node from data.
45    pub fn new(func: impl Fn(D) -> UiNode + Send + Sync + 'static) -> Self {
46        WidgetFn(Some(Arc::new(Box::new(func))))
47    }
48
49    /// Function that always produces the [`UiNode::nil`].
50    ///
51    /// No heap allocation happens to create this `WidgetFn`.
52    pub const fn nil() -> Self {
53        WidgetFn(None)
54    }
55
56    /// If this is the [`nil`] function.
57    ///
58    /// If `true` the function always generates a node that is [`UiNode::is_nil`], if
59    /// `false` the function may still return a nil node some of the time.
60    ///
61    /// See [`call_checked`] for more details.
62    ///
63    /// [`nil`]: WidgetFn::nil
64    /// [`call_checked`]: Self::call_checked
65    /// [`UiNode::is_nil`]: zng_app::widget::node::UiNode::is_nil
66    pub fn is_nil(&self) -> bool {
67        self.0.is_none()
68    }
69
70    /// Calls the function with `data` argument.
71    ///
72    /// Note that you can call the widget function directly where `D: 'static`:
73    ///
74    /// ```
75    /// # use zng_wgt::WidgetFn;
76    /// fn foo(func: &WidgetFn<bool>) {
77    ///     let a = func.call(true);
78    ///     let b = func(true);
79    /// }
80    /// ```
81    ///
82    /// In the example above `a` and `b` are both calls to the widget function.
83    pub fn call(&self, data: D) -> UiNode {
84        if let Some(g) = &self.0 { g(data) } else { UiNode::nil() }
85    }
86
87    /// Calls the function with `data` argument and only returns a node if is not nil.
88    ///
89    /// Returns `None` if [`is_nil`] or [`UiNode::is_nil`].
90    ///
91    /// [`is_nil`]: Self::is_nil
92    /// [`UiNode::is_nil`]: zng_app::widget::node::UiNode::is_nil
93    pub fn call_checked(&self, data: D) -> Option<UiNode> {
94        let r = self.0.as_ref()?(data);
95        if r.is_nil() { None } else { Some(r) }
96    }
97
98    /// New widget function that returns the same `widget` for every call.
99    ///
100    /// The `widget` is wrapped in an [`ArcNode`] and every function call returns an [`ArcNode::take_on_init`] node.
101    /// Note that `take_on_init` is not always the `widget` on init as it needs to wait for it to deinit first if
102    /// it is already in use, this could have an effect if the widget function caller always expects a full widget.
103    ///
104    /// [`ArcNode`]: zng_app::widget::node::ArcNode
105    /// [`ArcNode::take_on_init`]: zng_app::widget::node::ArcNode::take_on_init
106    pub fn singleton(widget: impl IntoUiNode) -> Self {
107        let widget = ArcNode::new(widget);
108        Self::new(move |_| widget.take_on_init())
109    }
110
111    /// Creates a [`WeakWidgetFn<D>`] reference to this function.
112    pub fn downgrade(&self) -> WeakWidgetFn<D> {
113        match &self.0 {
114            Some(f) => WeakWidgetFn(Arc::downgrade(f)),
115            None => WeakWidgetFn::nil(),
116        }
117    }
118}
119impl<D: 'static> ops::Deref for WidgetFn<D> {
120    type Target = dyn Fn(D) -> UiNode;
121
122    fn deref(&self) -> &Self::Target {
123        match self.0.as_ref() {
124            Some(f) => &**f,
125            None => &nil_call::<D>,
126        }
127    }
128}
129fn nil_call<D>(_: D) -> UiNode {
130    UiNode::nil()
131}
132
133/// Weak reference to a [`WidgetFn<D>`].
134pub struct WeakWidgetFn<D>(std::sync::Weak<BoxedWgtFn<D>>);
135impl<D> WeakWidgetFn<D> {
136    /// New weak reference to nil.
137    pub const fn nil() -> Self {
138        WeakWidgetFn(std::sync::Weak::new())
139    }
140
141    /// If this weak reference only upgrades to a nil function.
142    pub fn is_nil(&self) -> bool {
143        self.0.strong_count() == 0
144    }
145
146    /// Upgrade to strong reference if it still exists or nil.
147    pub fn upgrade(&self) -> WidgetFn<D> {
148        match self.0.upgrade() {
149            Some(f) => WidgetFn(Some(f)),
150            None => WidgetFn::nil(),
151        }
152    }
153}
154
155/// <span data-del-macro-root></span> Declares a widget function closure.
156///
157/// The output type is a [`WidgetFn`], the closure is [`clmv!`].
158///
159/// # Syntax
160///
161/// * `wgt_fn!(cloned, |_args| Wgt!())` - Clone-move closure, the same syntax as [`clmv!`] you can
162///   list the cloned values before the closure.
163/// * `wgt_fn!(path::to::func)` - The macro also accepts unction, the signature must receive the args and return
164///   a widget.
165/// * `wgt_fn!()` - An empty call generates the [`WidgetFn::nil()`] value.
166///
167/// # Examples
168///
169/// Declares a basic widget function that ignores the argument and does not capture any value:
170///
171/// ```
172/// # zng_wgt::enable_widget_macros!();
173/// # use zng_wgt::{prelude::*, Wgt, on_init};
174/// #
175/// # fn main() {
176/// # let wgt: WidgetFn<bool> =
177/// wgt_fn!(|_| Wgt! {
178///     on_init = hn!(|_| println!("generated widget init"));
179/// });
180/// # ; }
181/// ```
182///
183/// The macro is clone-move, meaning you can use the same syntax as [`clmv!`] to capture clones of values:
184///
185/// ```
186/// # zng_wgt::enable_widget_macros!();
187/// # use zng_wgt::{prelude::*, Wgt};
188/// # fn main() {
189/// let moved_var = var('a');
190/// let cloned_var = var('b');
191///
192/// # let wgt: WidgetFn<bool> =
193/// wgt_fn!(cloned_var, |args| {
194///     println!(
195///         "wgt_fn, args: {:?}, moved_var: {}, cloned_var: {}",
196///         args,
197///         moved_var.get(),
198///         cloned_var.get()
199///     );
200///     Wgt!()
201/// });
202/// # ; }
203/// ```
204///
205/// [`clmv!`]: zng_clone_move::clmv
206#[macro_export]
207macro_rules! wgt_fn {
208    ($fn:path) => {
209        $crate::WidgetFn::new($fn)
210    };
211    ($($tt:tt)+) => {
212        $crate::WidgetFn::new($crate::__clmv! {
213            $($tt)+
214        })
215    };
216    () => {
217        $crate::WidgetFn::nil()
218    };
219}
220
221/// Service that provides editor widgets for a given variable.
222///
223/// Auto generating widgets such as a settings list or a properties list can use this
224/// service to instantiate widgets for each item.
225///
226/// The main crate registers some common editors.
227pub struct EDITORS;
228impl EDITORS {
229    /// Register an `editor` handler.
230    ///
231    /// The handler must return [`UiNode::nil`] if it cannot handle the request. Later added handlers are called first.
232    pub fn register(&self, editor: WidgetFn<EditorRequestArgs>) {
233        if !editor.is_nil() {
234            UPDATES
235                .run(async move {
236                    EDITORS_SV.write().push(editor);
237                })
238                .perm();
239        }
240    }
241
242    /// Register an `editor` handler to be called if none of the `register` editors can handle the value.
243    ///
244    /// The handler must return [`UiNode::nil`] if it cannot handle the request. Later added handlers are called last.
245    pub fn register_fallback(&self, editor: WidgetFn<EditorRequestArgs>) {
246        if !editor.is_nil() {
247            UPDATES
248                .run(async move {
249                    EDITORS_SV.write().push_fallback(editor);
250                })
251                .perm();
252        }
253    }
254
255    /// Instantiate an editor for the `value`.
256    ///
257    /// Returns [`UiNode::nil`] if no registered editor can handle the value type.
258    pub fn get(&self, value: AnyVar) -> UiNode {
259        EDITORS_SV.read().get(EditorRequestArgs { value })
260    }
261
262    /// Same as [`get`], but also logs an error is there are no available editor for the type.
263    ///
264    /// [`get`]: Self::get
265    pub fn req<T: VarValue>(&self, value: Var<T>) -> UiNode {
266        let e = self.get(value.into());
267        if e.is_nil() {
268            tracing::error!("no editor available for `{}`", std::any::type_name::<T>())
269        }
270        e
271    }
272}
273
274/// Service that provides icon drawing widgets.
275///
276/// This service enables widgets to use icons in an optional way, without needing to bundle icon resources. It
277/// also enables app wide icon theming.
278pub struct ICONS;
279impl ICONS {
280    /// Register an `icon` handler.
281    ///
282    /// The handler must return [`UiNode::nil`] if it cannot handle the request. Later added handlers are called first.
283    pub fn register(&self, icon: WidgetFn<IconRequestArgs>) {
284        if !icon.is_nil() {
285            UPDATES
286                .run(async move {
287                    ICONS_SV.write().push(icon);
288                })
289                .perm();
290        }
291    }
292
293    /// Register an `icon` handler to be called if none of the `register` handlers can handle request.
294    ///
295    /// The handler must return [`UiNode::nil`] if it cannot handle the request. Later added handlers are called last.
296    pub fn register_fallback(&self, icon: WidgetFn<IconRequestArgs>) {
297        if !icon.is_nil() {
298            UPDATES
299                .run(async move {
300                    ICONS_SV.write().push_fallback(icon);
301                })
302                .perm();
303        }
304    }
305
306    /// Instantiate an icon drawing widget for the `icon_name`.
307    ///
308    /// Returns [`UiNode::nil`] if no registered handler can provide an icon.
309    pub fn get(&self, icon_name: impl IconNames) -> UiNode {
310        self.get_impl(&mut icon_name.names())
311    }
312    fn get_impl(&self, names: &mut dyn Iterator<Item = Txt>) -> UiNode {
313        let sv = ICONS_SV.read();
314        for name in names {
315            let node = sv.get(IconRequestArgs { name });
316            if !node.is_nil() {
317                return node;
318            }
319        }
320        UiNode::nil()
321    }
322
323    /// Instantiate an icon drawing widget for the `icon_name` or call `fallback` to do it
324    /// if no handler can handle the request.
325    pub fn get_or(&self, icon_name: impl IconNames, fallback: impl FnOnce() -> UiNode) -> UiNode {
326        let i = self.get(icon_name);
327        if i.is_nil() { fallback() } else { i }
328    }
329
330    /// Same as [`get`], but also logs an error is there are no available icon for any of the names.
331    ///
332    /// [`get`]: Self::get
333    pub fn req(&self, icon_name: impl IconNames) -> UiNode {
334        self.req_impl(&mut icon_name.names())
335    }
336    fn req_impl(&self, names: &mut dyn Iterator<Item = Txt>) -> UiNode {
337        let sv = ICONS_SV.read();
338        let mut missing = vec![];
339        for name in names {
340            let node = sv.get(IconRequestArgs { name: name.clone() });
341            if !node.is_nil() {
342                return node;
343            } else {
344                missing.push(name);
345            }
346        }
347        tracing::error!("no icon available for {missing:?}");
348        UiNode::nil()
349    }
350
351    //// Same as [`get_or`], but also logs an error is there are no available icon for any of the names.
352    ///
353    /// [`get_or`]: Self::get_or
354    pub fn req_or(&self, icon_name: impl IconNames, fallback: impl FnOnce() -> UiNode) -> UiNode {
355        let i = self.req(icon_name);
356        if i.is_nil() { fallback() } else { i }
357    }
358}
359
360/// Adapter for [`ICONS`] queries.
361///
362/// Can be `"name"` or `["name", "fallback-name1"]` names.
363pub trait IconNames {
364    /// Iterate over names, from most wanted to least.
365    fn names(self) -> impl Iterator<Item = Txt>;
366}
367impl IconNames for &'static str {
368    fn names(self) -> impl Iterator<Item = Txt> {
369        [Txt::from(self)].into_iter()
370    }
371}
372impl IconNames for Txt {
373    fn names(self) -> impl Iterator<Item = Txt> {
374        [self].into_iter()
375    }
376}
377impl IconNames for Vec<Txt> {
378    fn names(self) -> impl Iterator<Item = Txt> {
379        self.into_iter()
380    }
381}
382impl IconNames for &[Txt] {
383    fn names(self) -> impl Iterator<Item = Txt> {
384        self.iter().cloned()
385    }
386}
387impl IconNames for &[&'static str] {
388    fn names(self) -> impl Iterator<Item = Txt> {
389        self.iter().copied().map(Txt::from)
390    }
391}
392impl<const N: usize> IconNames for [&'static str; N] {
393    fn names(self) -> impl Iterator<Item = Txt> {
394        self.into_iter().map(Txt::from)
395    }
396}
397
398/// Adds the [`icon`](CommandIconExt::icon) command metadata.
399///
400/// The value is an [`WidgetFn<()>`] that can generate any icon widget, the [`ICONS`] service is recommended.
401///
402/// [`WidgetFn<()>`]: WidgetFn
403pub trait CommandIconExt {
404    /// Gets a read-write variable that is the icon for the command.
405    fn icon(self) -> CommandMetaVar<WidgetFn<()>>;
406
407    /// Sets the initial icon if it is not set.
408    fn init_icon(self, icon: WidgetFn<()>) -> Self;
409}
410static_id! {
411    static ref COMMAND_ICON_ID: CommandMetaVarId<WidgetFn<()>>;
412}
413impl CommandIconExt for Command {
414    fn icon(self) -> CommandMetaVar<WidgetFn<()>> {
415        self.with_meta(|m| m.get_var_or_default(*COMMAND_ICON_ID))
416    }
417
418    fn init_icon(self, icon: WidgetFn<()>) -> Self {
419        self.with_meta(|m| m.init_var(*COMMAND_ICON_ID, icon));
420        self
421    }
422}
423
424/// Arguments for [`EDITORS.register`].
425///
426/// Note that the handler is usually called in the widget context that will host the editor, so context
427/// variables and services my also be available to inform the editor preferences.
428///
429/// [`EDITORS.register`]: EDITORS::register
430#[derive(Clone)]
431pub struct EditorRequestArgs {
432    value: AnyVar,
433}
434impl EditorRequestArgs {
435    /// The value variable.
436    pub fn value_any(&self) -> &AnyVar {
437        &self.value
438    }
439
440    /// Try to downcast the value variable to `T`.
441    pub fn value<T: VarValue>(&self) -> Option<Var<T>> {
442        self.value_any().clone().downcast::<T>().ok()
443    }
444}
445
446/// Arguments for [`ICONS.register`].
447///
448/// Note that the handler is usually called in the widget context that will host the editor, so context
449/// variables and services my also be available to inform the editor preferences.
450///
451/// [`ICONS.register`]: ICONS::register
452#[derive(Clone)]
453pub struct IconRequestArgs {
454    name: Txt,
455}
456impl IconRequestArgs {
457    /// Icon unique name,
458    pub fn name(&self) -> &str {
459        &self.name
460    }
461}
462
463app_local! {
464    static EDITORS_SV: WidgetProviderService<EditorRequestArgs> = const { WidgetProviderService::new() };
465    static ICONS_SV: WidgetProviderService<IconRequestArgs> = const { WidgetProviderService::new() };
466}
467struct WidgetProviderService<A> {
468    handlers: Vec<WidgetFn<A>>,
469    fallback: Vec<WidgetFn<A>>,
470}
471impl<A: Clone + 'static> WidgetProviderService<A> {
472    const fn new() -> Self {
473        Self {
474            handlers: vec![],
475            fallback: vec![],
476        }
477    }
478
479    fn push(&mut self, handler: WidgetFn<A>) {
480        self.handlers.push(handler);
481    }
482
483    fn push_fallback(&mut self, handler: WidgetFn<A>) {
484        self.fallback.push(handler);
485    }
486
487    fn get(&self, args: A) -> UiNode {
488        for handler in self.handlers.iter().rev() {
489            let editor = handler(args.clone());
490            if !editor.is_nil() {
491                return editor;
492            }
493        }
494        for handler in self.fallback.iter() {
495            let editor = handler(args.clone());
496            if !editor.is_nil() {
497                return editor;
498            }
499        }
500        UiNode::nil()
501    }
502}