Skip to main content

tern_sdk/ui/
node.rs

1//! The dynamic node every builder produces, the view of a surface's three
2//! regions, and the macros that give every kind's builder the common props
3//! and handlers.
4
5use std::{fmt, sync::Arc};
6
7use serde::Serialize;
8use serde_json::{Map, Value};
9
10use crate::{
11	Error,
12	wire::{Action, Change, Edit, Event},
13};
14
15/// A routed event turned into a program message (or not, when it doesn't
16/// fit the handler).
17pub type Handler<M> = Arc<dyn Fn(&Event) -> Option<M> + Send + Sync>;
18
19/// What a handler answers to.
20#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum Trigger {
22	/// `actions.click` (set to `"click"` unless the node names it).
23	Click,
24	/// `actions.dblclick` (set to `"dblclick"` unless the node names it).
25	Dblclick,
26	/// A context-menu action, added to `actions.menu`.
27	Menu(String),
28	/// `action` events with this `act`.
29	Action(String),
30	/// Events of this `ev` (`toggle`, `select`, `change`, …).
31	Event(&'static str),
32}
33
34/// A node: a kind, props, children and handlers. It has no id until it is
35/// reconciled. Every kind's builder converts into it.
36pub struct Node<M = ()> {
37	/// The kind.
38	pub(crate) kind:     String,
39	/// Props.
40	pub(crate) props:    Map<String, Value>,
41	/// Children in order.
42	pub(crate) children: Vec<Self>,
43	/// Handlers, in the order they were attached.
44	pub(crate) handlers: Vec<(Trigger, Handler<M>)>,
45}
46
47impl<M> Clone for Node<M> {
48	fn clone(&self) -> Self {
49		Self {
50			kind:     self.kind.clone(),
51			props:    self.props.clone(),
52			children: self.children.clone(),
53			handlers: self.handlers.clone(),
54		}
55	}
56}
57
58impl<M> fmt::Debug for Node<M> {
59	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60		f.debug_struct("Node")
61			.field("kind", &self.kind)
62			.field("props", &self.props)
63			.field("children", &self.children)
64			.field("handlers", &self.handlers.iter().map(|(t, _)| t).collect::<Vec<_>>())
65			.finish()
66	}
67}
68
69impl<M> Node<M> {
70	/// A node of any kind, without props.
71	pub fn new(kind: impl Into<String>) -> Self {
72		Self {
73			kind:     kind.into(),
74			props:    Map::new(),
75			children: Vec::new(),
76			handlers: Vec::new(),
77		}
78	}
79
80	/// The kind.
81	pub fn kind(&self) -> &str {
82		&self.kind
83	}
84
85	/// The props.
86	pub const fn props(&self) -> &Map<String, Value> {
87		&self.props
88	}
89
90	/// The children.
91	pub fn child_nodes(&self) -> &[Self] {
92		&self.children
93	}
94
95	/// A prop as text: a string, or spans joined.
96	pub(crate) fn text_prop(&self, key: &str) -> Option<String> {
97		match self.props.get(key)? {
98			Value::String(s) => Some(s.clone()),
99			Value::Array(spans) => Some(spans_text(spans)),
100			_ => None,
101		}
102	}
103
104	/// Sets a prop; `null` removes it.
105	pub(crate) fn set(&mut self, key: &str, value: &impl Serialize) {
106		match serde_json::to_value(value) {
107			Ok(Value::Null) => {
108				self.props.remove(key);
109			},
110			Ok(value) => {
111				self.props.insert(key.to_owned(), value);
112			},
113			Err(err) => tracing::warn!(key, %err, "prop not serializable; left unset"),
114		}
115	}
116
117	/// Sets the text of a kind that takes `text` or `spans`.
118	pub(crate) fn set_text(&mut self, text: crate::ui::Text) {
119		match text {
120			crate::ui::Text::Plain(s) => {
121				self.props.remove("spans");
122				self.props.insert("text".into(), Value::String(s));
123			},
124			crate::ui::Text::Spans(spans) => {
125				self.props.remove("text");
126				self.set("spans", &spans);
127			},
128		}
129	}
130
131	/// Builds a node from its wire form `{k, p?, c?}` (no handlers).
132	///
133	/// # Errors
134	/// [`Error::InvalidView`] when it isn't an object with a string `k`, or
135	/// `p`/`c` have the wrong shape.
136	pub fn from_json(value: &Value) -> Result<Self, Error> {
137		let Some(obj) = value.as_object() else {
138			return Err(Error::InvalidView(format!("a node must be an object, got {value}")));
139		};
140		let Some(kind) = obj.get("k").and_then(Value::as_str) else {
141			return Err(Error::InvalidView("a node needs a string `k`".into()));
142		};
143		let props = match obj.get("p") {
144			None | Some(Value::Null) => Map::new(),
145			Some(Value::Object(p)) => p.clone(),
146			Some(other) => {
147				return Err(Error::InvalidView(format!("`p` must be an object, got {other}")));
148			},
149		};
150		let children = match obj.get("c") {
151			None | Some(Value::Null) => Vec::new(),
152			Some(Value::Array(c)) => c.iter().map(Self::from_json).collect::<Result<_, _>>()?,
153			Some(other) => return Err(Error::InvalidView(format!("`c` must be a list, got {other}"))),
154		};
155		Ok(Self { kind: kind.to_owned(), props, children, handlers: Vec::new() })
156	}
157}
158
159/// Joins the `t` of spans (strings count as themselves).
160pub fn spans_text(spans: &[Value]) -> String {
161	spans
162		.iter()
163		.filter_map(|s| match s {
164			Value::String(t) => Some(t.as_str()),
165			Value::Object(o) => o.get("t").and_then(Value::as_str),
166			_ => None,
167		})
168		.collect()
169}
170
171/// A list of nodes is a `col` of them (how a region given as a list is
172/// wrapped).
173impl<M> From<Vec<Self>> for Node<M> {
174	fn from(children: Vec<Self>) -> Self {
175		Self { children, ..Self::new("col") }
176	}
177}
178
179/// An array of nodes is a `col` of them.
180impl<M, C: Into<Self>, const N: usize> From<[C; N]> for Node<M> {
181	fn from(children: [C; N]) -> Self {
182		Self::from(children.into_iter().map(Into::into).collect::<Vec<Self>>())
183	}
184}
185
186/// Gives access to the node a builder wraps.
187pub trait AsNode<M> {
188	/// The node.
189	fn as_node(&mut self) -> &mut Node<M>;
190}
191
192impl<M> AsNode<M> for Node<M> {
193	fn as_node(&mut self) -> &mut Self {
194		self
195	}
196}
197
198/// A surface's view: the root of each region (`main`, `dock`, `layer`).
199/// A region left out is not shown.
200///
201/// A node given for a region is the region root itself; a list (a `Vec` or
202/// an array) is wrapped in a `col` root. Tern draws a region's children into
203/// an element of its own and ignores the root's kind and props (but
204/// `role`), so a root should be a container. A bare node or list converts
205/// into a view as `main`'s children:
206///
207/// ```
208/// use tern_sdk::ui::{self, View};
209///
210/// let view: View = ui::md("**hi**").into();
211/// let root = view.regions()[0].unwrap();
212/// assert_eq!((root.kind(), root.child_nodes()[0].kind()), ("col", "md"));
213/// ```
214pub struct View<M = ()> {
215	/// The flowing document.
216	pub(crate) main:  Option<Node<M>>,
217	/// Sticky at the pane's bottom while live.
218	pub(crate) dock:  Option<Node<M>>,
219	/// Overlays.
220	pub(crate) layer: Option<Node<M>>,
221}
222
223impl<M> Default for View<M> {
224	fn default() -> Self {
225		Self { main: None, dock: None, layer: None }
226	}
227}
228
229impl<M> Clone for View<M> {
230	fn clone(&self) -> Self {
231		Self { main: self.main.clone(), dock: self.dock.clone(), layer: self.layer.clone() }
232	}
233}
234
235impl<M> fmt::Debug for View<M> {
236	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237		f.debug_struct("View")
238			.field("main", &self.main)
239			.field("dock", &self.dock)
240			.field("layer", &self.layer)
241			.finish()
242	}
243}
244
245impl<M> View<M> {
246	/// The empty view.
247	pub fn new() -> Self {
248		Self::default()
249	}
250
251	/// Sets `main`, the flowing document: a root node, or a list of
252	/// children wrapped in a `col`.
253	pub fn main(mut self, region: impl Into<Node<M>>) -> Self {
254		self.main = Some(region.into());
255		self
256	}
257
258	/// Sets `dock`, sticky at the pane's bottom: a root node, or a list.
259	pub fn dock(mut self, region: impl Into<Node<M>>) -> Self {
260		self.dock = Some(region.into());
261		self
262	}
263
264	/// Sets `layer`, the overlays: a root node, or a list.
265	pub fn layer(mut self, region: impl Into<Node<M>>) -> Self {
266		self.layer = Some(region.into());
267		self
268	}
269
270	/// The regions in order `main`, `dock`, `layer`.
271	pub const fn regions(&self) -> [Option<&Node<M>>; 3] {
272		[self.main.as_ref(), self.dock.as_ref(), self.layer.as_ref()]
273	}
274
275	/// Builds a view from its wire form `{main?, dock?, layer?}` (`null` is
276	/// the empty view; a region given as a list is wrapped in a `col`).
277	///
278	/// # Errors
279	/// [`Error::InvalidView`] for an unknown region or a malformed node.
280	pub fn from_json(value: &Value) -> Result<Self, Error> {
281		let mut view = Self::new();
282		match value {
283			Value::Null => {},
284			Value::Object(obj) => {
285				for (name, node) in obj {
286					let node = match node {
287						Value::Null => continue,
288						Value::Array(list) => Node::from(
289							list
290								.iter()
291								.map(Node::from_json)
292								.collect::<Result<Vec<_>, _>>()?,
293						),
294						other => Node::from_json(other)?,
295					};
296					match name.as_str() {
297						"main" => view.main = Some(node),
298						"dock" => view.dock = Some(node),
299						"layer" => view.layer = Some(node),
300						other => return Err(Error::InvalidView(format!("unknown region {other:?}"))),
301					}
302				}
303			},
304			other => {
305				return Err(Error::InvalidView(format!(
306					"expected {{main?, dock?, layer?}}, got {other}"
307				)));
308			},
309		}
310		Ok(view)
311	}
312}
313
314/// A bare node is `main`'s one child.
315impl<M> From<Node<M>> for View<M> {
316	fn from(node: Node<M>) -> Self {
317		Self::new().main(vec![node])
318	}
319}
320
321/// A list of nodes is `main`'s children.
322impl<M> From<Vec<Node<M>>> for View<M> {
323	fn from(children: Vec<Node<M>>) -> Self {
324		Self::new().main(children)
325	}
326}
327
328/// An array of nodes is `main`'s children.
329impl<M, C: Into<Node<M>>, const N: usize> From<[C; N]> for View<M> {
330	fn from(children: [C; N]) -> Self {
331		Self::new().main(children)
332	}
333}
334
335/// Wraps a message as a handler that answers events of one shape.
336pub fn msg_handler<M: Clone + Send + Sync + 'static>(msg: M) -> Handler<M> {
337	Arc::new(move |_| Some(msg.clone()))
338}
339
340/// The handler of an `action` event, from its payload.
341pub fn action_handler<M, F>(f: F) -> Handler<M>
342where
343	F: Fn(&Action) -> M + Send + Sync + 'static,
344{
345	Arc::new(move |e| match e {
346		Event::Action(a) => Some(f(a)),
347		_ => None,
348	})
349}
350
351/// The handler of a `toggle` event.
352pub fn toggle_handler<M, F>(f: F) -> Handler<M>
353where
354	F: Fn(bool) -> M + Send + Sync + 'static,
355{
356	Arc::new(move |e| match e {
357		Event::Toggle(t) => Some(f(t.collapsed)),
358		_ => None,
359	})
360}
361
362/// The handler of a `select` (or `activate`) event, from its item.
363pub fn item_handler<M, F>(f: F, activate: bool) -> Handler<M>
364where
365	F: Fn(&str) -> M + Send + Sync + 'static,
366{
367	Arc::new(move |e| match (e, activate) {
368		(Event::Select(s), false) | (Event::Activate(s), true) => Some(f(&s.item)),
369		_ => None,
370	})
371}
372
373/// The handler of a `change` event.
374pub fn change_handler<M, F>(f: F) -> Handler<M>
375where
376	F: Fn(&Change) -> M + Send + Sync + 'static,
377{
378	Arc::new(move |e| match e {
379		Event::Change(c) => Some(f(c)),
380		_ => None,
381	})
382}
383
384/// The handler of an `edit` event.
385pub fn edit_handler<M, F>(f: F) -> Handler<M>
386where
387	F: Fn(&Edit) -> M + Send + Sync + 'static,
388{
389	Arc::new(move |e| match e {
390		Event::Edit(ed) => Some(f(ed)),
391		_ => None,
392	})
393}
394
395/// The handler of a `send` event, from its text.
396pub fn send_handler<M, F>(f: F) -> Handler<M>
397where
398	F: Fn(&str) -> M + Send + Sync + 'static,
399{
400	Arc::new(move |e| match e {
401		Event::Send(s) => Some(f(&s.text)),
402		_ => None,
403	})
404}
405
406/// The props every node takes, except `title`, `max` and `actions`.
407macro_rules! base_methods {
408	() => {
409		/// Identity among siblings: keeps the node's id (and Tern's view
410		/// state) across reorders. A string, or an integer.
411		pub fn key(mut self, key: impl Into<$crate::ui::NodeKey>) -> Self {
412			$crate::ui::node::AsNode::as_node(&mut self).set("key", &key.into());
413			self
414		}
415
416		/// A name of yours, exposed to CSS as `data-role`.
417		pub fn role(mut self, role: impl Into<String>) -> Self {
418			$crate::ui::node::AsNode::as_node(&mut self).set("role", &role.into());
419			self
420		}
421
422		/// The semantic color of the node's chrome.
423		pub fn tone(mut self, tone: impl Into<$crate::ui::Tone>) -> Self {
424			$crate::ui::node::AsNode::as_node(&mut self).set("tone", &tone.into());
425			self
426		}
427
428		/// Keeps the node mounted but not laid out.
429		pub fn hidden(mut self, hidden: bool) -> Self {
430			$crate::ui::node::AsNode::as_node(&mut self).set("hidden", &hidden);
431			self
432		}
433
434		/// A transient selection mark drawn over the node.
435		pub fn mark(mut self, mark: impl Into<$crate::ui::Mark>) -> Self {
436			$crate::ui::node::AsNode::as_node(&mut self).set("mark", &mark.into());
437			self
438		}
439
440		/// The accessible name of a node that shows no text.
441		pub fn aria(mut self, aria: impl Into<String>) -> Self {
442			$crate::ui::node::AsNode::as_node(&mut self).set("aria", &aria.into());
443			self
444		}
445
446		/// A link: ⌘-click opens it; `copy` and `open` use it.
447		pub fn href(mut self, href: impl Into<String>) -> Self {
448			$crate::ui::node::AsNode::as_node(&mut self).set("href", &href.into());
449			self
450		}
451
452		/// Flex grow inside a `row` or `col`.
453		pub fn grow(mut self, grow: f64) -> Self {
454			$crate::ui::node::AsNode::as_node(&mut self).set("grow", &grow);
455			self
456		}
457
458		/// Flex shrink inside a `row` or `col`.
459		pub fn shrink(mut self, shrink: f64) -> Self {
460			$crate::ui::node::AsNode::as_node(&mut self).set("shrink", &shrink);
461			self
462		}
463
464		/// Flex basis: a fraction of the parent, or the content's size.
465		pub fn basis(mut self, basis: impl Into<$crate::ui::Basis>) -> Self {
466			$crate::ui::node::AsNode::as_node(&mut self).set("basis", &basis.into());
467			self
468		}
469
470		/// Lower size bounds.
471		pub fn min(mut self, min: $crate::ui::Bound) -> Self {
472			$crate::ui::node::AsNode::as_node(&mut self).set("min", &min);
473			self
474		}
475
476		/// Sets any prop untyped; `null` removes it.
477		pub fn prop(mut self, name: &str, value: impl ::serde::Serialize) -> Self {
478			$crate::ui::node::AsNode::as_node(&mut self).set(name, &value);
479			self
480		}
481	};
482}
483
484/// The common `title` prop (a tooltip).
485macro_rules! title_method {
486	() => {
487		/// A tooltip.
488		pub fn title(mut self, title: impl Into<String>) -> Self {
489			$crate::ui::node::AsNode::as_node(&mut self).set("title", &title.into());
490			self
491		}
492	};
493}
494
495/// The common `max` prop.
496macro_rules! max_method {
497	() => {
498		/// Upper size bounds (a `max.h` also clips the node).
499		pub fn max(mut self, max: $crate::ui::Bound) -> Self {
500			$crate::ui::node::AsNode::as_node(&mut self).set("max", &max);
501			self
502		}
503	};
504}
505
506/// The common `actions` prop and the pointer handlers.
507macro_rules! actions_methods {
508	() => {
509		/// What a pointer does, by action name.
510		pub fn actions(mut self, actions: $crate::ui::Actions) -> Self {
511			$crate::ui::node::AsNode::as_node(&mut self).set("actions", &actions);
512			self
513		}
514
515		/// Sends `msg` on a click (sets `actions.click` to `"click"` unless
516		/// the node names its click action itself).
517		pub fn on_click(mut self, msg: M) -> Self
518		where
519			M: Clone + Send + Sync + 'static,
520		{
521			$crate::ui::node::AsNode::as_node(&mut self)
522				.handlers
523				.push(($crate::ui::node::Trigger::Click, $crate::ui::node::msg_handler(msg)));
524			self
525		}
526
527		/// Sends `msg` on a double click (sets `actions.dblclick` to
528		/// `"dblclick"` unless the node names it).
529		pub fn on_dblclick(mut self, msg: M) -> Self
530		where
531			M: Clone + Send + Sync + 'static,
532		{
533			$crate::ui::node::AsNode::as_node(&mut self)
534				.handlers
535				.push(($crate::ui::node::Trigger::Dblclick, $crate::ui::node::msg_handler(msg)));
536			self
537		}
538
539		/// Adds `act` to the context menu and sends `msg` when it is picked.
540		pub fn on_menu(mut self, act: impl Into<String>, msg: M) -> Self
541		where
542			M: Clone + Send + Sync + 'static,
543		{
544			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
545				$crate::ui::node::Trigger::Menu(act.into()),
546				$crate::ui::node::msg_handler(msg),
547			));
548			self
549		}
550	};
551}
552
553/// Handlers of the events a node sends.
554macro_rules! event_methods {
555	() => {
556		/// Sends `msg` on `action` events whose action is `act` (`name` or
557		/// `name=value`).
558		pub fn on_action(mut self, act: impl Into<String>, msg: M) -> Self
559		where
560			M: Clone + Send + Sync + 'static,
561		{
562			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
563				$crate::ui::node::Trigger::Action(act.into()),
564				$crate::ui::node::msg_handler(msg),
565			));
566			self
567		}
568
569		/// Maps `action` events whose action is `act` to a message.
570		pub fn on_action_with(
571			mut self,
572			act: impl Into<String>,
573			f: impl Fn(&$crate::wire::Action) -> M + Send + Sync + 'static,
574		) -> Self {
575			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
576				$crate::ui::node::Trigger::Action(act.into()),
577				$crate::ui::node::action_handler(f),
578			));
579			self
580		}
581
582		/// Maps `toggle` events (the new `collapsed`) to a message.
583		pub fn on_toggle(mut self, f: impl Fn(bool) -> M + Send + Sync + 'static) -> Self {
584			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
585				$crate::ui::node::Trigger::Event("toggle"),
586				$crate::ui::node::toggle_handler(f),
587			));
588			self
589		}
590
591		/// Maps `select` events (the item) to a message. On a list's item the
592		/// item's own handler runs first.
593		pub fn on_select(mut self, f: impl Fn(&str) -> M + Send + Sync + 'static) -> Self {
594			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
595				$crate::ui::node::Trigger::Event("select"),
596				$crate::ui::node::item_handler(f, false),
597			));
598			self
599		}
600
601		/// Maps `activate` events (the item) to a message.
602		pub fn on_activate(mut self, f: impl Fn(&str) -> M + Send + Sync + 'static) -> Self {
603			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
604				$crate::ui::node::Trigger::Event("activate"),
605				$crate::ui::node::item_handler(f, true),
606			));
607			self
608		}
609
610		/// Maps `change` events to a message.
611		pub fn on_change(
612			mut self,
613			f: impl Fn(&$crate::wire::Change) -> M + Send + Sync + 'static,
614		) -> Self {
615			$crate::ui::node::AsNode::as_node(&mut self).handlers.push((
616				$crate::ui::node::Trigger::Event("change"),
617				$crate::ui::node::change_handler(f),
618			));
619			self
620		}
621
622		/// Sends `msg` on `focus` events (a click asked for the keys).
623		pub fn on_focus(mut self, msg: M) -> Self
624		where
625			M: Clone + Send + Sync + 'static,
626		{
627			$crate::ui::node::AsNode::as_node(&mut self)
628				.handlers
629				.push(($crate::ui::node::Trigger::Event("focus"), $crate::ui::node::msg_handler(msg)));
630			self
631		}
632
633		/// Maps `edit` events (native editing) to a message.
634		pub fn on_edit(
635			mut self,
636			f: impl Fn(&$crate::wire::Edit) -> M + Send + Sync + 'static,
637		) -> Self {
638			$crate::ui::node::AsNode::as_node(&mut self)
639				.handlers
640				.push(($crate::ui::node::Trigger::Event("edit"), $crate::ui::node::edit_handler(f)));
641			self
642		}
643
644		/// Sends `msg` on `undo` events.
645		pub fn on_undo(mut self, msg: M) -> Self
646		where
647			M: Clone + Send + Sync + 'static,
648		{
649			$crate::ui::node::AsNode::as_node(&mut self)
650				.handlers
651				.push(($crate::ui::node::Trigger::Event("undo"), $crate::ui::node::msg_handler(msg)));
652			self
653		}
654
655		/// Maps `send` events (the text to submit) to a message.
656		pub fn on_send(mut self, f: impl Fn(&str) -> M + Send + Sync + 'static) -> Self {
657			$crate::ui::node::AsNode::as_node(&mut self)
658				.handlers
659				.push(($crate::ui::node::Trigger::Event("send"), $crate::ui::node::send_handler(f)));
660			self
661		}
662	};
663}
664
665/// Children of a container kind.
666macro_rules! children_methods {
667	() => {
668		/// Adds a child.
669		pub fn child(mut self, child: impl Into<$crate::ui::Node<M>>) -> Self {
670			$crate::ui::node::AsNode::as_node(&mut self)
671				.children
672				.push(child.into());
673			self
674		}
675
676		/// Adds children.
677		pub fn children<C: Into<$crate::ui::Node<M>>>(
678			mut self,
679			children: impl IntoIterator<Item = C>,
680		) -> Self {
681			$crate::ui::node::AsNode::as_node(&mut self)
682				.children
683				.extend(children.into_iter().map(Into::into));
684			self
685		}
686	};
687}
688
689/// Typed props: `method(Type) = "key";` sets prop `key` from
690/// `impl Into<Type>`.
691macro_rules! props {
692	($($(#[$m:meta])* $method:ident($ty:ty) = $key:literal;)*) => {
693		$(
694			$(#[$m])*
695			pub fn $method(mut self, value: impl Into<$ty>) -> Self {
696				let value: $ty = value.into();
697				$crate::ui::node::AsNode::as_node(&mut self).set($key, &value);
698				self
699			}
700		)*
701	};
702}
703
704/// Defines a kind's builder type wrapping a [`Node`].
705macro_rules! kind_type {
706	($(#[$m:meta])* $name:ident) => {
707		$(#[$m])*
708		pub struct $name<M = ()>(pub(crate) $crate::ui::Node<M>);
709
710		impl<M> Clone for $name<M> {
711			fn clone(&self) -> Self {
712				Self(self.0.clone())
713			}
714		}
715
716		impl<M> ::std::fmt::Debug for $name<M> {
717			fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
718				self.0.fmt(f)
719			}
720		}
721
722		impl<M> From<$name<M>> for $crate::ui::Node<M> {
723			fn from(n: $name<M>) -> Self {
724				n.0
725			}
726		}
727
728		/// A bare node is `main`'s one child.
729		impl<M> From<$name<M>> for $crate::ui::View<M> {
730			fn from(n: $name<M>) -> Self {
731				Self::from(n.0)
732			}
733		}
734
735		impl<M> $crate::ui::node::AsNode<M> for $name<M> {
736			fn as_node(&mut self) -> &mut $crate::ui::Node<M> {
737				&mut self.0
738			}
739		}
740	};
741}
742
743pub(crate) use actions_methods;
744pub(crate) use base_methods;
745pub(crate) use children_methods;
746pub(crate) use event_methods;
747pub(crate) use kind_type;
748pub(crate) use max_method;
749pub(crate) use props;
750pub(crate) use title_method;
751
752impl<M> Node<M> {
753	base_methods!();
754
755	title_method!();
756
757	max_method!();
758
759	actions_methods!();
760
761	event_methods!();
762
763	children_methods!();
764}