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}
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 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 pub fn kind(&self) -> &str {
82 &self.kind
83 }
84
85 pub const fn props(&self) -> &Map<String, Value> {
87 &self.props
88 }
89
90 pub fn child_nodes(&self) -> &[Self] {
92 &self.children
93 }
94
95 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 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 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 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
159pub 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
171impl<M> From<Vec<Self>> for Node<M> {
174 fn from(children: Vec<Self>) -> Self {
175 Self { children, ..Self::new("col") }
176 }
177}
178
179impl<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
186pub trait AsNode<M> {
188 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
198pub struct View<M = ()> {
215 pub(crate) main: Option<Node<M>>,
217 pub(crate) dock: Option<Node<M>>,
219 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 pub fn new() -> Self {
248 Self::default()
249 }
250
251 pub fn main(mut self, region: impl Into<Node<M>>) -> Self {
254 self.main = Some(region.into());
255 self
256 }
257
258 pub fn dock(mut self, region: impl Into<Node<M>>) -> Self {
260 self.dock = Some(region.into());
261 self
262 }
263
264 pub fn layer(mut self, region: impl Into<Node<M>>) -> Self {
266 self.layer = Some(region.into());
267 self
268 }
269
270 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 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
314impl<M> From<Node<M>> for View<M> {
316 fn from(node: Node<M>) -> Self {
317 Self::new().main(vec![node])
318 }
319}
320
321impl<M> From<Vec<Node<M>>> for View<M> {
323 fn from(children: Vec<Node<M>>) -> Self {
324 Self::new().main(children)
325 }
326}
327
328impl<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
335pub fn msg_handler<M: Clone + Send + Sync + 'static>(msg: M) -> Handler<M> {
337 Arc::new(move |_| Some(msg.clone()))
338}
339
340pub 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
351pub 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
362pub 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
373pub 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
384pub 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
395pub 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
406macro_rules! base_methods {
408 () => {
409 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 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 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 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 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 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 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 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 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 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 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 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
484macro_rules! title_method {
486 () => {
487 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
495macro_rules! max_method {
497 () => {
498 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
506macro_rules! actions_methods {
508 () => {
509 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 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 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 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
553macro_rules! event_methods {
555 () => {
556 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 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 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 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 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 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 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 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 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 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
665macro_rules! children_methods {
667 () => {
668 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 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
689macro_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
704macro_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 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}