1use 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
15pub type Handler<M> = Arc<dyn Fn(&Event) -> Option<M> + Send + Sync>;
18
19#[derive(Clone, Debug, PartialEq, Eq)]
21pub enum Trigger {
22 Click,
24 Dblclick,
26 Menu(String),
28 Action(String),
30 Event(&'static str),
32}
33
34pub struct Node<M = ()> {
37 pub(crate) kind: String,
39 pub(crate) props: Map<String, Value>,
41 pub(crate) children: Vec<Self>,
43 pub(crate) handlers: Vec<(Trigger, Handler<M>)>,
45 pub(crate) slots: Vec<&'static str>,
48}
49
50impl<M> Clone for Node<M> {
51 fn clone(&self) -> Self {
52 Self {
53 kind: self.kind.clone(),
54 props: self.props.clone(),
55 children: self.children.clone(),
56 handlers: self.handlers.clone(),
57 slots: self.slots.clone(),
58 }
59 }
60}
61
62impl<M> fmt::Debug for Node<M> {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 f.debug_struct("Node")
65 .field("kind", &self.kind)
66 .field("props", &self.props)
67 .field("children", &self.children)
68 .field("handlers", &self.handlers.iter().map(|(t, _)| t).collect::<Vec<_>>())
69 .finish()
70 }
71}
72
73impl<M> Node<M> {
74 pub fn new(kind: impl Into<String>) -> Self {
76 Self {
77 kind: kind.into(),
78 props: Map::new(),
79 children: Vec::new(),
80 handlers: Vec::new(),
81 slots: Vec::new(),
82 }
83 }
84
85 pub fn kind(&self) -> &str {
87 &self.kind
88 }
89
90 pub const fn props(&self) -> &Map<String, Value> {
92 &self.props
93 }
94
95 pub fn child_nodes(&self) -> &[Self] {
97 &self.children
98 }
99
100 pub(crate) fn text_prop(&self, key: &str) -> Option<String> {
102 match self.props.get(key)? {
103 Value::String(s) => Some(s.clone()),
104 Value::Array(spans) => Some(spans_text(spans)),
105 _ => None,
106 }
107 }
108
109 pub(crate) fn set(&mut self, key: &str, value: &impl Serialize) {
111 match serde_json::to_value(value) {
112 Ok(Value::Null) => {
113 self.props.remove(key);
114 },
115 Ok(value) => {
116 self.props.insert(key.to_owned(), value);
117 },
118 Err(err) => tracing::warn!(key, %err, "prop not serializable; left unset"),
119 }
120 }
121
122 pub(crate) fn set_slot(&mut self, slot: &'static str, mut node: Self) {
125 node.props.insert("key".into(), Value::String(slot.into()));
126 self.props.remove(slot);
127 self
128 .children
129 .retain(|c| c.props.get("key").and_then(Value::as_str) != Some(slot));
130 self.children.push(node);
131 if !self.slots.contains(&slot) {
132 self.slots.push(slot);
133 }
134 }
135
136 pub(crate) fn clear_slot(&mut self, slot: &str) {
138 if let Some(at) = self.slots.iter().position(|s| *s == slot) {
139 self.slots.remove(at);
140 self
141 .children
142 .retain(|c| c.props.get("key").and_then(Value::as_str) != Some(slot));
143 }
144 }
145
146 pub(crate) fn set_text(&mut self, text: crate::ui::Text) {
148 match text {
149 crate::ui::Text::Plain(s) => {
150 self.props.remove("spans");
151 self.props.insert("text".into(), Value::String(s));
152 },
153 crate::ui::Text::Spans(spans) => {
154 self.props.remove("text");
155 self.set("spans", &spans);
156 },
157 }
158 }
159
160 pub fn from_json(value: &Value) -> Result<Self, Error> {
166 let Some(obj) = value.as_object() else {
167 return Err(Error::InvalidView(format!("a node must be an object, got {value}")));
168 };
169 let Some(kind) = obj.get("k").and_then(Value::as_str) else {
170 return Err(Error::InvalidView("a node needs a string `k`".into()));
171 };
172 let props = match obj.get("p") {
173 None | Some(Value::Null) => Map::new(),
174 Some(Value::Object(p)) => p.clone(),
175 Some(other) => {
176 return Err(Error::InvalidView(format!("`p` must be an object, got {other}")));
177 },
178 };
179 let children = match obj.get("c") {
180 None | Some(Value::Null) => Vec::new(),
181 Some(Value::Array(c)) => c.iter().map(Self::from_json).collect::<Result<_, _>>()?,
182 Some(other) => return Err(Error::InvalidView(format!("`c` must be a list, got {other}"))),
183 };
184 Ok(Self { children, props, ..Self::new(kind) })
185 }
186
187 pub fn to_json(&self) -> Value {
189 let mut obj = Map::new();
190 obj.insert("k".into(), Value::String(self.kind.clone()));
191 if !self.props.is_empty() {
192 obj.insert("p".into(), Value::Object(self.props.clone()));
193 }
194 if !self.children.is_empty() {
195 obj.insert("c".into(), Value::Array(self.children.iter().map(Self::to_json).collect()));
196 }
197 Value::Object(obj)
198 }
199}
200
201pub fn spans_text(spans: &[Value]) -> String {
203 spans
204 .iter()
205 .filter_map(|s| match s {
206 Value::String(t) => Some(t.as_str()),
207 Value::Object(o) => o.get("t").and_then(Value::as_str),
208 _ => None,
209 })
210 .collect()
211}
212
213impl<M> From<Vec<Self>> for Node<M> {
216 fn from(children: Vec<Self>) -> Self {
217 Self { children, ..Self::new("col") }
218 }
219}
220
221impl<M, C: Into<Self>, const N: usize> From<[C; N]> for Node<M> {
223 fn from(children: [C; N]) -> Self {
224 Self::from(children.into_iter().map(Into::into).collect::<Vec<Self>>())
225 }
226}
227
228pub trait AsNode<M> {
230 fn as_node(&mut self) -> &mut Node<M>;
232}
233
234impl<M> AsNode<M> for Node<M> {
235 fn as_node(&mut self) -> &mut Self {
236 self
237 }
238}
239
240pub struct View<M = ()> {
257 pub(crate) main: Option<Node<M>>,
259 pub(crate) dock: Option<Node<M>>,
261 pub(crate) layer: Option<Node<M>>,
263}
264
265impl<M> Default for View<M> {
266 fn default() -> Self {
267 Self { main: None, dock: None, layer: None }
268 }
269}
270
271impl<M> Clone for View<M> {
272 fn clone(&self) -> Self {
273 Self { main: self.main.clone(), dock: self.dock.clone(), layer: self.layer.clone() }
274 }
275}
276
277impl<M> fmt::Debug for View<M> {
278 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
279 f.debug_struct("View")
280 .field("main", &self.main)
281 .field("dock", &self.dock)
282 .field("layer", &self.layer)
283 .finish()
284 }
285}
286
287impl<M> View<M> {
288 pub fn new() -> Self {
290 Self::default()
291 }
292
293 pub fn main(mut self, region: impl Into<Node<M>>) -> Self {
296 self.main = Some(region.into());
297 self
298 }
299
300 pub fn dock(mut self, region: impl Into<Node<M>>) -> Self {
302 self.dock = Some(region.into());
303 self
304 }
305
306 pub fn layer(mut self, region: impl Into<Node<M>>) -> Self {
308 self.layer = Some(region.into());
309 self
310 }
311
312 pub const fn regions(&self) -> [Option<&Node<M>>; 3] {
314 [self.main.as_ref(), self.dock.as_ref(), self.layer.as_ref()]
315 }
316
317 pub fn from_json(value: &Value) -> Result<Self, Error> {
323 let mut view = Self::new();
324 match value {
325 Value::Null => {},
326 Value::Object(obj) => {
327 for (name, node) in obj {
328 let node = match node {
329 Value::Null => continue,
330 Value::Array(list) => Node::from(
331 list
332 .iter()
333 .map(Node::from_json)
334 .collect::<Result<Vec<_>, _>>()?,
335 ),
336 other => Node::from_json(other)?,
337 };
338 match name.as_str() {
339 "main" => view.main = Some(node),
340 "dock" => view.dock = Some(node),
341 "layer" => view.layer = Some(node),
342 other => return Err(Error::InvalidView(format!("unknown region {other:?}"))),
343 }
344 }
345 },
346 other => {
347 return Err(Error::InvalidView(format!(
348 "expected {{main?, dock?, layer?}}, got {other}"
349 )));
350 },
351 }
352 Ok(view)
353 }
354}
355
356impl<M> From<Node<M>> for View<M> {
358 fn from(node: Node<M>) -> Self {
359 Self::new().main(vec![node])
360 }
361}
362
363impl<M> From<Vec<Node<M>>> for View<M> {
365 fn from(children: Vec<Node<M>>) -> Self {
366 Self::new().main(children)
367 }
368}
369
370impl<M, C: Into<Node<M>>, const N: usize> From<[C; N]> for View<M> {
372 fn from(children: [C; N]) -> Self {
373 Self::new().main(children)
374 }
375}
376
377pub fn msg_handler<M: Clone + Send + Sync + 'static>(msg: M) -> Handler<M> {
379 Arc::new(move |_| Some(msg.clone()))
380}
381
382pub fn action_handler<M, F>(f: F) -> Handler<M>
384where
385 F: Fn(&Action) -> M + Send + Sync + 'static,
386{
387 Arc::new(move |e| match e {
388 Event::Action(a) => Some(f(a)),
389 _ => None,
390 })
391}
392
393pub fn toggle_handler<M, F>(f: F) -> Handler<M>
396where
397 F: Fn(bool) -> M + Send + Sync + 'static,
398{
399 Arc::new(move |e| match e {
400 Event::Toggle(t) => Some(f(t.open.map_or(t.collapsed, |open| !open))),
401 _ => None,
402 })
403}
404
405pub fn item_handler<M, F>(f: F, activate: bool) -> Handler<M>
407where
408 F: Fn(&str) -> M + Send + Sync + 'static,
409{
410 Arc::new(move |e| match (e, activate) {
411 (Event::Select(s), false) | (Event::Activate(s), true) => Some(f(&s.item)),
412 _ => None,
413 })
414}
415
416pub fn change_handler<M, F>(f: F) -> Handler<M>
418where
419 F: Fn(&Change) -> M + Send + Sync + 'static,
420{
421 Arc::new(move |e| match e {
422 Event::Change(c) => Some(f(c)),
423 _ => None,
424 })
425}
426
427pub fn edit_handler<M, F>(f: F) -> Handler<M>
429where
430 F: Fn(&Edit) -> M + Send + Sync + 'static,
431{
432 Arc::new(move |e| match e {
433 Event::Edit(ed) => Some(f(ed)),
434 _ => None,
435 })
436}
437
438pub fn send_handler<M, F>(f: F) -> Handler<M>
440where
441 F: Fn(&str) -> M + Send + Sync + 'static,
442{
443 Arc::new(move |e| match e {
444 Event::Send(s) => Some(f(&s.text)),
445 _ => None,
446 })
447}
448
449macro_rules! base_methods {
451 () => {
452 pub fn key(mut self, key: impl Into<$crate::ui::NodeKey>) -> Self {
455 $crate::ui::node::AsNode::as_node(&mut self).set("key", &key.into());
456 self
457 }
458
459 pub fn role(mut self, role: impl Into<String>) -> Self {
461 $crate::ui::node::AsNode::as_node(&mut self).set("role", &role.into());
462 self
463 }
464
465 pub fn tone(mut self, tone: impl Into<$crate::ui::Tone>) -> Self {
467 $crate::ui::node::AsNode::as_node(&mut self).set("tone", &tone.into());
468 self
469 }
470
471 pub fn hidden(mut self, hidden: bool) -> Self {
473 $crate::ui::node::AsNode::as_node(&mut self).set("hidden", &hidden);
474 self
475 }
476
477 pub fn mark(mut self, mark: impl Into<$crate::ui::Mark>) -> Self {
479 $crate::ui::node::AsNode::as_node(&mut self).set("mark", &mark.into());
480 self
481 }
482
483 pub fn aria(mut self, aria: impl Into<String>) -> Self {
485 $crate::ui::node::AsNode::as_node(&mut self).set("aria", &aria.into());
486 self
487 }
488
489 pub fn hovercard<N>(mut self, card: impl Into<$crate::ui::Node<N>>) -> Self {
493 let card = card.into().to_json();
494 $crate::ui::node::AsNode::as_node(&mut self).set("hovercard", &card);
495 self
496 }
497
498 pub fn href(mut self, href: impl Into<String>) -> Self {
500 $crate::ui::node::AsNode::as_node(&mut self).set("href", &href.into());
501 self
502 }
503
504 pub fn grow(mut self, grow: f64) -> Self {
506 $crate::ui::node::AsNode::as_node(&mut self).set("grow", &grow);
507 self
508 }
509
510 pub fn shrink(mut self, shrink: f64) -> Self {
512 $crate::ui::node::AsNode::as_node(&mut self).set("shrink", &shrink);
513 self
514 }
515
516 pub fn basis(mut self, basis: impl Into<$crate::ui::Basis>) -> Self {
518 $crate::ui::node::AsNode::as_node(&mut self).set("basis", &basis.into());
519 self
520 }
521
522 pub fn prop(mut self, name: &str, value: impl ::serde::Serialize) -> Self {
524 $crate::ui::node::AsNode::as_node(&mut self).set(name, &value);
525 self
526 }
527 };
528}
529
530macro_rules! title_method {
532 () => {
533 pub fn title(mut self, title: impl Into<String>) -> Self {
535 $crate::ui::node::AsNode::as_node(&mut self).set("title", &title.into());
536 self
537 }
538 };
539}
540
541macro_rules! min_method {
543 () => {
544 pub fn min(mut self, min: $crate::ui::Bound) -> Self {
546 $crate::ui::node::AsNode::as_node(&mut self).set("min", &min);
547 self
548 }
549 };
550}
551
552macro_rules! max_method {
554 () => {
555 pub fn max(mut self, max: $crate::ui::Bound) -> Self {
557 $crate::ui::node::AsNode::as_node(&mut self).set("max", &max);
558 self
559 }
560 };
561}
562
563macro_rules! actions_methods {
565 () => {
566 pub fn actions(mut self, actions: $crate::ui::Actions) -> Self {
568 $crate::ui::node::AsNode::as_node(&mut self).set("actions", &actions);
569 self
570 }
571
572 pub fn on_click(mut self, msg: M) -> Self
575 where
576 M: Clone + Send + Sync + 'static,
577 {
578 $crate::ui::node::AsNode::as_node(&mut self)
579 .handlers
580 .push(($crate::ui::node::Trigger::Click, $crate::ui::node::msg_handler(msg)));
581 self
582 }
583
584 pub fn on_dblclick(mut self, msg: M) -> Self
587 where
588 M: Clone + Send + Sync + 'static,
589 {
590 $crate::ui::node::AsNode::as_node(&mut self)
591 .handlers
592 .push(($crate::ui::node::Trigger::Dblclick, $crate::ui::node::msg_handler(msg)));
593 self
594 }
595
596 pub fn on_menu(mut self, act: impl Into<String>, msg: M) -> Self
598 where
599 M: Clone + Send + Sync + 'static,
600 {
601 $crate::ui::node::AsNode::as_node(&mut self)
602 .handlers
603 .push((
604 $crate::ui::node::Trigger::Menu(act.into()),
605 $crate::ui::node::msg_handler(msg),
606 ));
607 self
608 }
609 };
610}
611
612macro_rules! event_methods {
614 () => {
615 pub fn on_action(mut self, act: impl Into<String>, msg: M) -> Self
618 where
619 M: Clone + Send + Sync + 'static,
620 {
621 $crate::ui::node::AsNode::as_node(&mut self)
622 .handlers
623 .push((
624 $crate::ui::node::Trigger::Action(act.into()),
625 $crate::ui::node::msg_handler(msg),
626 ));
627 self
628 }
629
630 pub fn on_action_with(
632 mut self,
633 act: impl Into<String>,
634 f: impl Fn(&$crate::wire::Action) -> M + Send + Sync + 'static,
635 ) -> Self {
636 $crate::ui::node::AsNode::as_node(&mut self)
637 .handlers
638 .push((
639 $crate::ui::node::Trigger::Action(act.into()),
640 $crate::ui::node::action_handler(f),
641 ));
642 self
643 }
644
645 pub fn on_toggle(mut self, f: impl Fn(bool) -> M + Send + Sync + 'static) -> Self {
648 $crate::ui::node::AsNode::as_node(&mut self)
649 .handlers
650 .push((
651 $crate::ui::node::Trigger::Event("toggle"),
652 $crate::ui::node::toggle_handler(f),
653 ));
654 self
655 }
656
657 pub fn on_select(mut self, f: impl Fn(&str) -> M + Send + Sync + 'static) -> Self {
660 $crate::ui::node::AsNode::as_node(&mut self)
661 .handlers
662 .push((
663 $crate::ui::node::Trigger::Event("select"),
664 $crate::ui::node::item_handler(f, false),
665 ));
666 self
667 }
668
669 pub fn on_activate(mut self, f: impl Fn(&str) -> M + Send + Sync + 'static) -> Self {
671 $crate::ui::node::AsNode::as_node(&mut self)
672 .handlers
673 .push((
674 $crate::ui::node::Trigger::Event("activate"),
675 $crate::ui::node::item_handler(f, true),
676 ));
677 self
678 }
679
680 pub fn on_change(
682 mut self,
683 f: impl Fn(&$crate::wire::Change) -> M + Send + Sync + 'static,
684 ) -> Self {
685 $crate::ui::node::AsNode::as_node(&mut self)
686 .handlers
687 .push((
688 $crate::ui::node::Trigger::Event("change"),
689 $crate::ui::node::change_handler(f),
690 ));
691 self
692 }
693
694 pub fn on_focus(mut self, msg: M) -> Self
696 where
697 M: Clone + Send + Sync + 'static,
698 {
699 $crate::ui::node::AsNode::as_node(&mut self)
700 .handlers
701 .push(($crate::ui::node::Trigger::Event("focus"), $crate::ui::node::msg_handler(msg)));
702 self
703 }
704
705 pub fn on_edit(
707 mut self,
708 f: impl Fn(&$crate::wire::Edit) -> M + Send + Sync + 'static,
709 ) -> Self {
710 $crate::ui::node::AsNode::as_node(&mut self)
711 .handlers
712 .push(($crate::ui::node::Trigger::Event("edit"), $crate::ui::node::edit_handler(f)));
713 self
714 }
715
716 pub fn on_undo(mut self, msg: M) -> Self
718 where
719 M: Clone + Send + Sync + 'static,
720 {
721 $crate::ui::node::AsNode::as_node(&mut self)
722 .handlers
723 .push(($crate::ui::node::Trigger::Event("undo"), $crate::ui::node::msg_handler(msg)));
724 self
725 }
726
727 pub fn on_send(mut self, f: impl Fn(&str) -> M + Send + Sync + 'static) -> Self {
729 $crate::ui::node::AsNode::as_node(&mut self)
730 .handlers
731 .push(($crate::ui::node::Trigger::Event("send"), $crate::ui::node::send_handler(f)));
732 self
733 }
734 };
735}
736
737macro_rules! children_methods {
739 () => {
740 pub fn child(mut self, child: impl Into<$crate::ui::Node<M>>) -> Self {
742 $crate::ui::node::AsNode::as_node(&mut self)
743 .children
744 .push(child.into());
745 self
746 }
747
748 pub fn children<C: Into<$crate::ui::Node<M>>>(
750 mut self,
751 children: impl IntoIterator<Item = C>,
752 ) -> Self {
753 $crate::ui::node::AsNode::as_node(&mut self)
754 .children
755 .extend(children.into_iter().map(Into::into));
756 self
757 }
758 };
759}
760
761macro_rules! props {
764 ($($(#[$m:meta])* $method:ident($ty:ty) = $key:literal;)*) => {
765 $(
766 $(#[$m])*
767 pub fn $method(mut self, value: impl Into<$ty>) -> Self {
768 let value: $ty = value.into();
769 $crate::ui::node::AsNode::as_node(&mut self).set($key, &value);
770 self
771 }
772 )*
773 };
774}
775
776macro_rules! kind_type {
778 ($(#[$m:meta])* $name:ident) => {
779 $(#[$m])*
780 pub struct $name<M = ()>(pub(crate) $crate::ui::Node<M>);
781
782 impl<M> Clone for $name<M> {
783 fn clone(&self) -> Self {
784 Self(self.0.clone())
785 }
786 }
787
788 impl<M> ::std::fmt::Debug for $name<M> {
789 fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
790 self.0.fmt(f)
791 }
792 }
793
794 impl<M> From<$name<M>> for $crate::ui::Node<M> {
795 fn from(n: $name<M>) -> Self {
796 n.0
797 }
798 }
799
800 impl<M> From<$name<M>> for $crate::ui::View<M> {
802 fn from(n: $name<M>) -> Self {
803 Self::from(n.0)
804 }
805 }
806
807 impl<M> $crate::ui::node::AsNode<M> for $name<M> {
808 fn as_node(&mut self) -> &mut $crate::ui::Node<M> {
809 &mut self.0
810 }
811 }
812 };
813}
814
815pub(crate) use actions_methods;
816pub(crate) use base_methods;
817pub(crate) use children_methods;
818pub(crate) use event_methods;
819pub(crate) use kind_type;
820pub(crate) use max_method;
821pub(crate) use min_method;
822pub(crate) use props;
823pub(crate) use title_method;
824
825impl<M> Node<M> {
826 base_methods!();
827
828 min_method!();
829
830 title_method!();
831
832 max_method!();
833
834 actions_methods!();
835
836 event_methods!();
837
838 children_methods!();
839}