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}