1pub mod component;
4pub mod context;
5pub mod node;
6pub mod unit;
7pub mod utils;
8
9use crate::{
10 application::Application,
11 props::PropsData,
12 widget::{
13 context::{WidgetContext, WidgetMountOrChangeContext, WidgetUnmountContext},
14 node::WidgetNode,
15 },
16 Prefab, PropsData,
17};
18use serde::{Deserialize, Serialize};
19use std::{
20 borrow::Cow,
21 collections::hash_map::DefaultHasher,
22 convert::TryFrom,
23 hash::{Hash, Hasher},
24 ops::{Deref, Range},
25 str::FromStr,
26 sync::{Arc, RwLock},
27};
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct WidgetIdDef(pub String);
31
32impl From<WidgetId> for WidgetIdDef {
33 fn from(data: WidgetId) -> Self {
34 Self(data.to_string())
35 }
36}
37
38#[derive(Debug, Clone, Copy)]
39pub struct WidgetIdMetaParam<'a> {
40 pub name: &'a str,
41 pub value: Option<&'a str>,
42}
43
44impl WidgetIdMetaParam<'_> {
45 pub fn is_flag(&self) -> bool {
46 self.value.is_none()
47 }
48
49 pub fn has_value(&self) -> bool {
50 self.value.is_some()
51 }
52}
53
54#[derive(Debug, Clone, Copy)]
55pub struct WidgetIdMetaParams<'a>(&'a str);
56
57impl<'a> WidgetIdMetaParams<'a> {
58 pub fn new(meta: &'a str) -> Self {
59 Self(meta)
60 }
61
62 pub fn iter(&self) -> impl Iterator<Item = WidgetIdMetaParam> {
63 self.0.split('&').filter_map(|part| {
64 if let Some(index) = part.find('=') {
65 let name = &part[0..index];
66 let value = &part[(index + b"=".len())..];
67 if name.is_empty() {
68 None
69 } else {
70 Some(WidgetIdMetaParam {
71 name,
72 value: Some(value),
73 })
74 }
75 } else if part.is_empty() {
76 None
77 } else {
78 Some(WidgetIdMetaParam {
79 name: part,
80 value: None,
81 })
82 }
83 })
84 }
85
86 pub fn find(&self, name: &str) -> Option<WidgetIdMetaParam> {
87 self.iter().find(|param| param.name == name)
88 }
89
90 pub fn has_flag(&self, name: &str) -> bool {
91 self.iter()
92 .any(|param| param.name == name && param.is_flag())
93 }
94
95 pub fn find_value(&self, name: &str) -> Option<&str> {
96 self.iter().find_map(|param| {
97 if param.name == name {
98 param.value
99 } else {
100 None
101 }
102 })
103 }
104}
105
106#[derive(PropsData, Default, Clone, Serialize, Deserialize)]
107#[serde(try_from = "WidgetIdDef")]
108#[serde(into = "WidgetIdDef")]
109pub struct WidgetId {
110 id: String,
111 type_name: Range<usize>,
112 parts: Vec<(Range<usize>, Range<usize>)>,
114}
115
116impl WidgetId {
117 pub fn empty() -> Self {
118 Self {
119 id: ":".to_owned(),
120 type_name: 0..0,
121 parts: Default::default(),
122 }
123 }
124
125 pub fn new(type_name: &str, path: &[Cow<'_, str>]) -> Self {
126 if path.is_empty() {
127 return Self {
128 id: format!("{}:", type_name),
129 type_name: 0..type_name.len(),
130 parts: Default::default(),
131 };
132 }
133 let count = type_name.len()
134 + b":".len()
135 + path.iter().map(|part| part.len()).sum::<usize>()
136 + path.len().saturating_sub(1) * b"/".len();
137 let mut result = String::with_capacity(count);
138 let mut position = result.len();
139 result.push_str(type_name);
140 let type_name = 0..result.len();
141 result.push(':');
142 let parts = path
143 .iter()
144 .enumerate()
145 .map(|(index, part)| {
146 if index > 0 {
147 result.push('/');
148 }
149 position = result.len();
150 result.push_str(part);
151 let range = position..result.len();
152 if let Some(index) = part.find('?') {
153 let key = range.start..(range.start + index);
154 let meta = (range.start + index + b"?".len())..range.end;
155 (key, meta)
156 } else {
157 let meta = range.end..range.end;
158 (range, meta)
159 }
160 })
161 .collect::<Vec<_>>();
162 Self {
163 id: result,
164 type_name,
165 parts,
166 }
167 }
168
169 pub fn push(&self, part: &str) -> Self {
170 let count = self.id.len() + b"/".len();
171 let mut result = String::with_capacity(count);
172 result.push_str(&self.id);
173 if self.depth() > 0 {
174 result.push('/');
175 }
176 let position = result.len();
177 result.push_str(part);
178 let range = position..result.len();
179 let (key, meta) = if let Some(index) = part.find('?') {
180 let key = range.start..(range.start + index);
181 let meta = (range.start + index + b"?".len())..range.end;
182 (key, meta)
183 } else {
184 let meta = range.end..range.end;
185 (range, meta)
186 };
187 let parts = self
188 .parts
189 .iter()
190 .cloned()
191 .chain(std::iter::once((key, meta)))
192 .collect();
193 Self {
194 id: result,
195 type_name: self.type_name.to_owned(),
196 parts,
197 }
198 }
199
200 pub fn pop(&self) -> Self {
201 let parts = self.parts[0..(self.parts.len().saturating_sub(1))].to_owned();
202 let result = if let Some(range) = parts.last() {
203 self.id[0..range.1.end].to_owned()
204 } else {
205 format!("{}:", self.type_name())
206 };
207 Self {
208 id: result,
209 type_name: self.type_name.to_owned(),
210 parts,
211 }
212 }
213
214 #[inline]
215 pub fn is_valid(&self) -> bool {
216 !self.id.is_empty()
217 }
218
219 #[inline]
220 pub fn depth(&self) -> usize {
221 self.parts.len()
222 }
223
224 #[inline]
225 pub fn type_name(&self) -> &str {
226 &self.id.as_str()[self.type_name.clone()]
227 }
228
229 #[inline]
230 pub fn path(&self) -> &str {
231 if self.parts.is_empty() {
232 &self.id.as_str()[0..0]
233 } else {
234 &self.id.as_str()[self.parts.first().unwrap().0.start..self.parts.last().unwrap().1.end]
235 }
236 }
237
238 #[inline]
239 pub fn key(&self) -> &str {
240 if self.parts.is_empty() {
241 &self.id.as_str()[0..0]
242 } else {
243 &self.id[self.parts.last().cloned().unwrap().0]
244 }
245 }
246
247 #[inline]
248 pub fn meta(&self) -> &str {
249 if self.parts.is_empty() {
250 &self.id.as_str()[0..0]
251 } else {
252 &self.id[self.parts.last().cloned().unwrap().1]
253 }
254 }
255
256 #[inline]
257 pub fn part(&self, index: usize) -> Option<&str> {
258 self.parts
259 .get(index)
260 .cloned()
261 .map(|(key, meta)| &self.id[key.start..meta.end])
262 }
263
264 #[inline]
265 pub fn part_key_meta(&self, index: usize) -> Option<(&str, &str)> {
266 self.parts
267 .get(index)
268 .cloned()
269 .map(|(key, meta)| (&self.id[key], &self.id[meta]))
270 }
271
272 pub fn range(&self, from_inclusive: usize, to_exclusive: usize) -> &str {
273 if self.parts.is_empty() {
274 return &self.id[0..0];
275 }
276 let start = from_inclusive.min(self.parts.len().saturating_sub(1));
277 let end = to_exclusive
278 .saturating_sub(1)
279 .max(start)
280 .min(self.parts.len().saturating_sub(1));
281 let start = self.parts[start].0.start;
282 let end = self.parts[end].1.end;
283 &self.id[start..end]
284 }
285
286 pub fn is_subset_of(&self, other: &Self) -> bool {
287 match self.distance_to(other) {
288 Ok(v) => v < 0,
289 _ => false,
290 }
291 }
292
293 pub fn is_superset_of(&self, other: &Self) -> bool {
294 match self.distance_to(other) {
295 Ok(v) => v > 0,
296 _ => false,
297 }
298 }
299
300 pub fn distance_to(&self, other: &Self) -> Result<isize, isize> {
301 for index in 0..self.depth().max(other.depth()) {
302 match (self.part(index), other.part(index)) {
303 (None, None) => return Ok(0),
304 (None, Some(_)) | (Some(_), None) => {
305 return Ok(self.depth() as isize - other.depth() as isize);
306 }
307 (Some(a), Some(b)) => {
308 if a != b {
309 return Err(index as isize - other.depth() as isize);
310 }
311 }
312 }
313 }
314 Ok(0)
315 }
316
317 #[inline]
318 pub fn parts(&self) -> impl Iterator<Item = &str> + '_ {
319 self.parts
320 .iter()
321 .cloned()
322 .map(move |(key, meta)| &self.id[key.start..meta.end])
323 }
324
325 #[inline]
326 pub fn parts_key_meta(&self) -> impl Iterator<Item = (&str, &str)> + '_ {
327 self.parts
328 .iter()
329 .cloned()
330 .map(move |(key, meta)| (&self.id[key], &self.id[meta]))
331 }
332
333 #[inline]
334 pub fn rparts(&self) -> impl Iterator<Item = &str> + '_ {
335 self.parts
336 .iter()
337 .rev()
338 .cloned()
339 .map(move |(key, meta)| &self.id[key.start..meta.end])
340 }
341
342 #[inline]
343 pub fn rparts_key_meta(&self) -> impl Iterator<Item = (&str, &str)> + '_ {
344 self.parts
345 .iter()
346 .rev()
347 .cloned()
348 .map(move |(key, meta)| (&self.id[key], &self.id[meta]))
349 }
350
351 pub fn hashed_value(&self) -> u64 {
352 let mut hasher = DefaultHasher::new();
353 self.hash(&mut hasher);
354 hasher.finish()
355 }
356}
357
358impl Hash for WidgetId {
359 fn hash<H: Hasher>(&self, state: &mut H) {
360 self.id.hash(state);
361 }
362}
363
364impl PartialEq for WidgetId {
365 fn eq(&self, other: &Self) -> bool {
366 self.id == other.id
367 }
368}
369
370impl Eq for WidgetId {}
371
372impl Deref for WidgetId {
373 type Target = str;
374
375 fn deref(&self) -> &Self::Target {
376 &self.id
377 }
378}
379
380impl AsRef<str> for WidgetId {
381 fn as_ref(&self) -> &str {
382 &self.id
383 }
384}
385
386impl FromStr for WidgetId {
387 type Err = ();
388
389 fn from_str(s: &str) -> Result<Self, Self::Err> {
390 if let Some(index) = s.find(':') {
391 let type_name = s[..index].to_owned();
392 let rest = &s[(index + b":".len())..];
393 let path = rest.split('/').map(Cow::Borrowed).collect::<Vec<_>>();
394 Ok(Self::new(&type_name, &path))
395 } else {
396 Err(())
397 }
398 }
399}
400
401impl std::fmt::Debug for WidgetId {
402 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
403 f.debug_struct("WidgetId")
404 .field("id", &self.id)
405 .field("type_name", &&self.id[self.type_name.clone()])
406 .field(
407 "parts",
408 &self
409 .parts
410 .iter()
411 .map(|(key, meta)| (&self.id[key.to_owned()], &self.id[meta.to_owned()]))
412 .collect::<Vec<_>>(),
413 )
414 .finish()
415 }
416}
417
418impl std::fmt::Display for WidgetId {
419 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
420 f.write_str(self.as_ref())
421 }
422}
423
424impl TryFrom<WidgetIdDef> for WidgetId {
425 type Error = String;
426
427 fn try_from(id: WidgetIdDef) -> Result<Self, Self::Error> {
428 match Self::from_str(&id.0) {
429 Ok(id) => Ok(id),
430 Err(_) => Err(format!("Could not parse id: `{}`", id.0)),
431 }
432 }
433}
434
435#[derive(Debug, Clone, PartialEq, Eq)]
436pub struct WidgetIdCommon {
437 id: Option<WidgetId>,
438 count: usize,
439}
440
441impl Default for WidgetIdCommon {
442 fn default() -> Self {
443 Self {
444 id: None,
445 count: usize::MAX,
446 }
447 }
448}
449
450impl WidgetIdCommon {
451 pub fn new(id: WidgetId) -> Self {
452 Self {
453 count: id.depth(),
454 id: Some(id),
455 }
456 }
457
458 pub fn include(&mut self, id: &WidgetId) -> &mut Self {
459 if self.id.is_none() {
460 self.id = Some(id.to_owned());
461 self.count = id.depth();
462 return self;
463 }
464 if let Some(source) = self.id.as_ref() {
465 for index in 0..self.count.min(id.depth()) {
466 if source.part(index) != id.part(index) {
467 self.count = index;
468 return self;
469 }
470 }
471 }
472 self
473 }
474
475 pub fn include_other(&mut self, other: &Self) -> &mut Self {
476 if let Some(id) = other.id.as_ref() {
477 if self.id.is_none() {
478 self.id = Some(id.to_owned());
479 self.count = other.count;
480 return self;
481 }
482 if let Some(source) = self.id.as_ref() {
483 for index in 0..self.count.min(other.count) {
484 if source.part(index) != id.part(index) {
485 self.count = index;
486 return self;
487 }
488 }
489 }
490 }
491 self
492 }
493
494 pub fn is_valid(&self) -> bool {
495 self.id.is_some()
496 }
497
498 pub fn parts(&self) -> Option<impl Iterator<Item = &str>> {
499 self.id
500 .as_ref()
501 .map(|id| (0..self.count).map_while(move |index| id.part(index)))
502 }
503
504 pub fn path(&self) -> Option<&str> {
505 self.id
506 .as_ref()
507 .map(|id| id.range(0, self.count))
508 .filter(|id| !id.is_empty())
509 }
510}
511
512impl<'a> FromIterator<&'a WidgetId> for WidgetIdCommon {
513 fn from_iter<T: IntoIterator<Item = &'a WidgetId>>(iter: T) -> Self {
514 let mut result = Self::default();
515 for id in iter {
516 result.include(id);
517 }
518 result
519 }
520}
521
522#[derive(Debug, Default, Clone, Serialize, Deserialize)]
523pub struct WidgetRefDef(pub Option<WidgetId>);
524
525impl From<WidgetRef> for WidgetRefDef {
526 fn from(data: WidgetRef) -> Self {
527 match data.0.read() {
528 Ok(data) => Self(data.clone()),
529 Err(_) => Default::default(),
530 }
531 }
532}
533
534#[derive(PropsData, Debug, Default, Clone, Serialize, Deserialize)]
535#[serde(from = "WidgetRefDef")]
536#[serde(into = "WidgetRefDef")]
537pub struct WidgetRef(#[serde(skip)] Arc<RwLock<Option<WidgetId>>>);
538
539impl WidgetRef {
540 pub fn new(id: WidgetId) -> Self {
541 Self(Arc::new(RwLock::new(Some(id))))
542 }
543
544 pub(crate) fn write(&mut self, id: WidgetId) {
545 if let Ok(mut data) = self.0.write() {
546 *data = Some(id);
547 }
548 }
549
550 pub fn read(&self) -> Option<WidgetId> {
551 if let Ok(data) = self.0.read() {
552 data.clone()
553 } else {
554 None
555 }
556 }
557
558 pub fn exists(&self) -> bool {
559 self.0
560 .read()
561 .ok()
562 .map(|data| data.is_some())
563 .unwrap_or_default()
564 }
565}
566
567impl PartialEq for WidgetRef {
568 fn eq(&self, other: &Self) -> bool {
569 Arc::ptr_eq(&self.0, &other.0)
570 }
571}
572
573impl From<WidgetRefDef> for WidgetRef {
574 fn from(data: WidgetRefDef) -> Self {
575 WidgetRef(Arc::new(RwLock::new(data.0)))
576 }
577}
578
579impl std::fmt::Display for WidgetRef {
580 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
581 if let Ok(id) = self.0.read() {
582 if let Some(id) = id.as_ref() {
583 write!(f, "{}", id)
584 } else {
585 Ok(())
586 }
587 } else {
588 Ok(())
589 }
590 }
591}
592
593#[derive(PropsData, Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
594pub enum WidgetIdOrRef {
595 #[default]
596 None,
597 Id(WidgetId),
598 Ref(WidgetRef),
599}
600
601impl WidgetIdOrRef {
602 #[inline]
603 pub fn new_ref() -> Self {
604 Self::Ref(WidgetRef::default())
605 }
606
607 #[inline]
608 pub fn is_none(&self) -> bool {
609 matches!(self, Self::None)
610 }
611
612 #[inline]
613 pub fn is_some(&self) -> bool {
614 !self.is_none()
615 }
616
617 pub fn read(&self) -> Option<WidgetId> {
618 match self {
619 Self::None => None,
620 Self::Id(id) => Some(id.to_owned()),
621 Self::Ref(idref) => idref.read(),
622 }
623 }
624}
625
626impl From<()> for WidgetIdOrRef {
627 fn from(_: ()) -> Self {
628 Self::None
629 }
630}
631
632impl From<WidgetId> for WidgetIdOrRef {
633 fn from(v: WidgetId) -> Self {
634 Self::Id(v)
635 }
636}
637
638impl From<WidgetRef> for WidgetIdOrRef {
639 fn from(v: WidgetRef) -> Self {
640 Self::Ref(v)
641 }
642}
643
644impl std::fmt::Display for WidgetIdOrRef {
645 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
646 match self {
647 Self::None => Ok(()),
648 Self::Id(id) => write!(f, "{}", id),
649 Self::Ref(id) => write!(f, "{}", id),
650 }
651 }
652}
653
654#[derive(Clone)]
655pub enum FnWidget {
656 Pointer(fn(WidgetContext) -> WidgetNode),
657 Closure(Arc<dyn Fn(WidgetContext) -> WidgetNode + Send + Sync>),
658}
659
660impl FnWidget {
661 pub fn pointer(value: fn(WidgetContext) -> WidgetNode) -> Self {
662 Self::Pointer(value)
663 }
664
665 pub fn closure(value: impl Fn(WidgetContext) -> WidgetNode + Send + Sync + 'static) -> Self {
666 Self::Closure(Arc::new(value))
667 }
668
669 pub fn call(&self, context: WidgetContext) -> WidgetNode {
670 match self {
671 Self::Pointer(value) => value(context),
672 Self::Closure(value) => value(context),
673 }
674 }
675}
676
677#[derive(Default)]
678pub struct WidgetLifeCycle {
679 #[allow(clippy::type_complexity)]
680 mount: Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
681 #[allow(clippy::type_complexity)]
682 change: Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
683 #[allow(clippy::type_complexity)]
684 unmount: Vec<Box<dyn FnMut(WidgetUnmountContext) + Send + Sync>>,
685}
686
687impl WidgetLifeCycle {
688 pub fn mount<F>(&mut self, f: F)
689 where
690 F: 'static + FnMut(WidgetMountOrChangeContext) + Send + Sync,
691 {
692 self.mount.push(Box::new(f));
693 }
694
695 pub fn change<F>(&mut self, f: F)
696 where
697 F: 'static + FnMut(WidgetMountOrChangeContext) + Send + Sync,
698 {
699 self.change.push(Box::new(f));
700 }
701
702 pub fn unmount<F>(&mut self, f: F)
703 where
704 F: 'static + FnMut(WidgetUnmountContext) + Send + Sync,
705 {
706 self.unmount.push(Box::new(f));
707 }
708
709 #[allow(clippy::type_complexity)]
710 pub fn unwrap(
711 self,
712 ) -> (
713 Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
714 Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
715 Vec<Box<dyn FnMut(WidgetUnmountContext) + Send + Sync>>,
716 ) {
717 let Self {
718 mount,
719 change,
720 unmount,
721 } = self;
722 (mount, change, unmount)
723 }
724}
725
726pub fn setup(app: &mut Application) {
727 app.register_props::<()>("()");
728 app.register_props::<i8>("i8");
729 app.register_props::<i16>("i16");
730 app.register_props::<i32>("i32");
731 app.register_props::<i64>("i64");
732 app.register_props::<i128>("i128");
733 app.register_props::<u8>("u8");
734 app.register_props::<u16>("u16");
735 app.register_props::<u32>("u32");
736 app.register_props::<u64>("u64");
737 app.register_props::<u128>("u128");
738 app.register_props::<f32>("f32");
739 app.register_props::<f64>("f64");
740 app.register_props::<bool>("bool");
741 app.register_props::<String>("String");
742 app.register_props::<component::containers::anchor_box::AnchorProps>("AnchorProps");
743 app.register_props::<component::containers::anchor_box::PivotBoxProps>("PivotBoxProps");
744 app.register_props::<component::containers::content_box::ContentBoxProps>("ContentBoxProps");
745 app.register_props::<component::containers::flex_box::FlexBoxProps>("FlexBoxProps");
746 app.register_props::<component::containers::grid_box::GridBoxProps>("GridBoxProps");
747 app.register_props::<component::containers::horizontal_box::HorizontalBoxProps>(
748 "HorizontalBoxProps",
749 );
750 app.register_props::<component::containers::hidden_box::HiddenBoxProps>("HiddenBoxProps");
751 app.register_props::<component::containers::scroll_box::ScrollBoxOwner>("ScrollBoxOwner");
752 app.register_props::<component::containers::scroll_box::SideScrollbarsProps>(
753 "SideScrollbarsProps",
754 );
755 app.register_props::<component::containers::scroll_box::SideScrollbarsState>(
756 "SideScrollbarsState",
757 );
758 app.register_props::<component::containers::portal_box::PortalsContainer>("PortalsContainer");
759 app.register_props::<component::containers::size_box::SizeBoxProps>("SizeBoxProps");
760 app.register_props::<component::containers::switch_box::SwitchBoxProps>("SwitchBoxProps");
761 app.register_props::<component::containers::tabs_box::TabsBoxProps>("TabsBoxProps");
762 app.register_props::<component::containers::tabs_box::TabPlateProps>("TabPlateProps");
763 app.register_props::<component::containers::tooltip_box::TooltipState>("TooltipState");
764 app.register_props::<component::containers::variant_box::VariantBoxProps>("VariantBoxProps");
765 app.register_props::<component::containers::vertical_box::VerticalBoxProps>("VerticalBoxProps");
766 app.register_props::<component::containers::wrap_box::WrapBoxProps>("WrapBoxProps");
767 app.register_props::<component::image_box::ImageBoxProps>("ImageBoxProps");
768 app.register_props::<component::interactive::button::ButtonProps>("ButtonProps");
769 app.register_props::<component::interactive::button::ButtonNotifyProps>("ButtonNotifyProps");
770 app.register_props::<component::interactive::input_field::TextInputMode>("TextInputMode");
771 app.register_props::<component::interactive::input_field::TextInputProps>("TextInputProps");
772 app.register_props::<component::interactive::input_field::TextInputState>("TextInputState");
773 app.register_props::<component::interactive::input_field::TextInputNotifyProps>(
774 "TextInputNotifyProps",
775 );
776 app.register_props::<component::interactive::input_field::TextInputControlNotifyProps>(
777 "TextInputControlNotifyProps",
778 );
779 app.register_props::<component::interactive::options_view::OptionsViewMode>("OptionsViewMode");
780 app.register_props::<component::interactive::options_view::OptionsViewProps>(
781 "OptionsViewProps",
782 );
783 app.register_props::<component::interactive::slider_view::SliderViewProps>("SliderViewProps");
784 app.register_props::<component::interactive::navigation::NavItemActive>("NavItemActive");
785 app.register_props::<component::interactive::navigation::NavTrackingActive>(
786 "NavTrackingActive",
787 );
788 app.register_props::<component::interactive::navigation::NavContainerActive>(
789 "NavContainerActive",
790 );
791 app.register_props::<component::interactive::navigation::NavJumpLooped>("NavJumpLooped");
792 app.register_props::<component::interactive::navigation::NavJumpMapProps>("NavJumpMapProps");
793 app.register_props::<component::interactive::scroll_view::ScrollViewState>("ScrollViewState");
794 app.register_props::<component::interactive::scroll_view::ScrollViewRange>("ScrollViewRange");
795 app.register_props::<component::interactive::scroll_view::ScrollViewNotifyProps>(
796 "ScrollViewNotifyProps",
797 );
798 app.register_props::<component::MessageForwardProps>("MessageForwardProps");
799 app.register_props::<component::WidgetAlpha>("WidgetAlpha");
800 app.register_props::<component::space_box::SpaceBoxProps>("SpaceBoxProps");
801 app.register_props::<component::text_box::TextBoxProps>("TextBoxProps");
802 app.register_props::<unit::content::ContentBoxItemLayout>("ContentBoxItemLayout");
803 app.register_props::<unit::flex::FlexBoxItemLayout>("FlexBoxItemLayout");
804 app.register_props::<unit::grid::GridBoxItemLayout>("GridBoxItemLayout");
805
806 app.register_component(
807 "area_box",
808 FnWidget::pointer(component::containers::area_box::area_box),
809 );
810 app.register_component(
811 "anchor_box",
812 FnWidget::pointer(component::containers::anchor_box::anchor_box),
813 );
814 app.register_component(
815 "pivot_box",
816 FnWidget::pointer(component::containers::anchor_box::pivot_box),
817 );
818 app.register_component(
819 "nav_content_box",
820 FnWidget::pointer(component::containers::content_box::nav_content_box),
821 );
822 app.register_component(
823 "content_box",
824 FnWidget::pointer(component::containers::content_box::content_box),
825 );
826 app.register_component(
827 "nav_flex_box",
828 FnWidget::pointer(component::containers::flex_box::nav_flex_box),
829 );
830 app.register_component(
831 "flex_box",
832 FnWidget::pointer(component::containers::flex_box::flex_box),
833 );
834 app.register_component(
835 "nav_grid_box",
836 FnWidget::pointer(component::containers::grid_box::nav_grid_box),
837 );
838 app.register_component(
839 "grid_box",
840 FnWidget::pointer(component::containers::grid_box::grid_box),
841 );
842 app.register_component(
843 "nav_horizontal_box",
844 FnWidget::pointer(component::containers::horizontal_box::nav_horizontal_box),
845 );
846 app.register_component(
847 "horizontal_box",
848 FnWidget::pointer(component::containers::horizontal_box::horizontal_box),
849 );
850 app.register_component(
851 "nav_scroll_box",
852 FnWidget::pointer(component::containers::scroll_box::nav_scroll_box),
853 );
854 app.register_component(
855 "nav_scroll_box_side_scrollbars",
856 FnWidget::pointer(component::containers::scroll_box::nav_scroll_box_side_scrollbars),
857 );
858 app.register_component(
859 "portal_box",
860 FnWidget::pointer(component::containers::portal_box::portal_box),
861 );
862 app.register_component(
863 "size_box",
864 FnWidget::pointer(component::containers::size_box::size_box),
865 );
866 app.register_component(
867 "nav_switch_box",
868 FnWidget::pointer(component::containers::switch_box::nav_switch_box),
869 );
870 app.register_component(
871 "switch_box",
872 FnWidget::pointer(component::containers::switch_box::switch_box),
873 );
874 app.register_component(
875 "nav_tabs_box",
876 FnWidget::pointer(component::containers::tabs_box::nav_tabs_box),
877 );
878 app.register_component(
879 "tooltip_box",
880 FnWidget::pointer(component::containers::tooltip_box::tooltip_box),
881 );
882 app.register_component(
883 "portals_tooltip_box",
884 FnWidget::pointer(component::containers::tooltip_box::portals_tooltip_box),
885 );
886 app.register_component(
887 "variant_box",
888 FnWidget::pointer(component::containers::variant_box::variant_box),
889 );
890 app.register_component(
891 "nav_vertical_box",
892 FnWidget::pointer(component::containers::vertical_box::nav_vertical_box),
893 );
894 app.register_component(
895 "vertical_box",
896 FnWidget::pointer(component::containers::vertical_box::vertical_box),
897 );
898 app.register_component(
899 "wrap_box",
900 FnWidget::pointer(component::containers::wrap_box::wrap_box),
901 );
902 app.register_component(
903 "button",
904 FnWidget::pointer(component::interactive::button::button),
905 );
906 app.register_component(
907 "tracked_button",
908 FnWidget::pointer(component::interactive::button::tracked_button),
909 );
910 app.register_component(
911 "self_tracked_button",
912 FnWidget::pointer(component::interactive::button::self_tracked_button),
913 );
914 app.register_component(
915 "text_input",
916 FnWidget::pointer(component::interactive::input_field::text_input),
917 );
918 app.register_component(
919 "input_field",
920 FnWidget::pointer(component::interactive::input_field::input_field),
921 );
922 app.register_component(
923 "options_view",
924 FnWidget::pointer(component::interactive::options_view::options_view),
925 );
926 app.register_component(
927 "slider_view",
928 FnWidget::pointer(component::interactive::slider_view::slider_view),
929 );
930 app.register_component(
931 "navigation_barrier",
932 FnWidget::pointer(component::interactive::navigation::navigation_barrier),
933 );
934 app.register_component(
935 "tracking",
936 FnWidget::pointer(component::interactive::navigation::tracking),
937 );
938 app.register_component(
939 "self_tracking",
940 FnWidget::pointer(component::interactive::navigation::self_tracking),
941 );
942 app.register_component(
943 "space_box",
944 FnWidget::pointer(component::space_box::space_box),
945 );
946 app.register_component(
947 "image_box",
948 FnWidget::pointer(component::image_box::image_box),
949 );
950 app.register_component("text_box", FnWidget::pointer(component::text_box::text_box));
951}
952
953#[macro_export]
970macro_rules! make_widget {
971 ($type_id:path) => {{
972 let processor = $type_id;
973 let type_name = stringify!($type_id);
974 $crate::widget::component::WidgetComponent::new(
975 $crate::widget::FnWidget::pointer(processor),
976 type_name,
977 )
978 }};
979}
980
981#[macro_export]
1016macro_rules! unpack_named_slots {
1017 ($map:expr => $name:ident) => {
1018 #[allow(unused_mut)]
1019 let mut $name = {
1020 let mut map = $map;
1021 match map.remove(stringify!($name)) {
1022 Some(widget) => widget,
1023 None => $crate::widget::node::WidgetNode::None,
1024 }
1025 };
1026 };
1027 ($map:expr => { $($name:ident),+ }) => {
1028 #[allow(unused_mut)]
1029 let ( $( mut $name ),+ ) = {
1030 let mut map = $map;
1031 (
1032 $(
1033 {
1034 match map.remove(stringify!($name)) {
1035 Some(widget) => widget,
1036 None => $crate::widget::node::WidgetNode::None,
1037 }
1038 }
1039 ),+
1040 )
1041 };
1042 };
1043}
1044
1045#[cfg(test)]
1046mod tests {
1047 use super::*;
1048
1049 #[test]
1050 fn test_widget_id() {
1051 let id = WidgetId::empty();
1052 assert_eq!(id.type_name(), "");
1053 assert_eq!(id.path(), "");
1054 assert_eq!(id.depth(), 0);
1055
1056 let id = WidgetId::new("type", &["parent".into(), "me".into()]);
1057 assert_eq!(id.to_string(), "type:parent/me".to_owned());
1058 assert_eq!(id.type_name(), "type");
1059 assert_eq!(id.parts().next().unwrap(), "parent");
1060 assert_eq!(id.key(), "me");
1061 assert_eq!(id.clone(), id);
1062
1063 let a = WidgetId::from_str("a:root/a").unwrap();
1064 let b = WidgetId::from_str("b:root/b").unwrap();
1065 let mut common = WidgetIdCommon::default();
1066 assert_eq!(common.path(), None);
1067 common.include(&a);
1068 assert_eq!(common.path(), Some("root/a"));
1069 let mut common = WidgetIdCommon::default();
1070 common.include(&b);
1071 assert_eq!(common.path(), Some("root/b"));
1072 common.include(&a);
1073 assert_eq!(common.path(), Some("root"));
1074
1075 let id = WidgetId::from_str("type:parent/me").unwrap();
1076 assert_eq!(&*id, "type:parent/me");
1077 assert_eq!(id.path(), "parent/me");
1078 let id = id.pop();
1079 assert_eq!(&*id, "type:parent");
1080 assert_eq!(id.path(), "parent");
1081 let id = id.pop();
1082 assert_eq!(&*id, "type:");
1083 assert_eq!(id.path(), "");
1084 let id = id.push("parent");
1085 assert_eq!(&*id, "type:parent");
1086 assert_eq!(id.path(), "parent");
1087 let id = id.push("me");
1088 assert_eq!(&*id, "type:parent/me");
1089 assert_eq!(id.path(), "parent/me");
1090 assert_eq!(id.key(), "me");
1091 assert_eq!(id.meta(), "");
1092 let id = id.push("with?meta");
1093 assert_eq!(&*id, "type:parent/me/with?meta");
1094 assert_eq!(id.path(), "parent/me/with?meta");
1095 assert_eq!(id.key(), "with");
1096 assert_eq!(id.meta(), "meta");
1097
1098 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1099 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1100 assert_eq!(a.distance_to(&b), Ok(0));
1101 assert!(!a.is_subset_of(&b));
1102 assert!(!a.is_superset_of(&b));
1103 let a = WidgetId::from_str("a:root/a/b").unwrap();
1104 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1105 assert_eq!(a.distance_to(&b), Ok(-1));
1106 assert!(a.is_subset_of(&b));
1107 assert!(!a.is_superset_of(&b));
1108 let a = WidgetId::from_str("a:root/a").unwrap();
1109 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1110 assert_eq!(a.distance_to(&b), Ok(-2));
1111 assert!(a.is_subset_of(&b));
1112 assert!(!a.is_superset_of(&b));
1113 let a = WidgetId::from_str("a:root").unwrap();
1114 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1115 assert_eq!(a.distance_to(&b), Ok(-3));
1116 assert!(a.is_subset_of(&b));
1117 assert!(!a.is_superset_of(&b));
1118 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1119 let b = WidgetId::from_str("b:root/a/b").unwrap();
1120 assert_eq!(a.distance_to(&b), Ok(1));
1121 assert!(!a.is_subset_of(&b));
1122 assert!(a.is_superset_of(&b));
1123 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1124 let b = WidgetId::from_str("b:root/a").unwrap();
1125 assert_eq!(a.distance_to(&b), Ok(2));
1126 assert!(!a.is_subset_of(&b));
1127 assert!(a.is_superset_of(&b));
1128 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1129 let b = WidgetId::from_str("b:root").unwrap();
1130 assert_eq!(a.distance_to(&b), Ok(3));
1131 assert!(!a.is_subset_of(&b));
1132 assert!(a.is_superset_of(&b));
1133 let a = WidgetId::from_str("a:root/a/b/x").unwrap();
1134 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1135 assert_eq!(a.distance_to(&b), Err(-1));
1136 assert!(!a.is_subset_of(&b));
1137 assert!(!a.is_superset_of(&b));
1138 let a = WidgetId::from_str("a:root/a/x/y").unwrap();
1139 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1140 assert_eq!(a.distance_to(&b), Err(-2));
1141 assert!(!a.is_subset_of(&b));
1142 assert!(!a.is_superset_of(&b));
1143 let a = WidgetId::from_str("a:root/x/y/z").unwrap();
1144 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1145 assert_eq!(a.distance_to(&b), Err(-3));
1146 assert!(!a.is_subset_of(&b));
1147 assert!(!a.is_superset_of(&b));
1148 }
1149}