Skip to main content

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