1pub mod component;
4pub mod context;
5pub mod node;
6pub mod unit;
7pub mod utils;
8
9use crate::{
10 Prefab, PropsData,
11 application::Application,
12 props::PropsData,
13 widget::{
14 context::{WidgetContext, WidgetMountOrChangeContext, WidgetUnmountContext},
15 node::WidgetNode,
16 },
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 .map(move |(key, meta)| &self.id[key.start..meta.end])
322 }
323
324 #[inline]
325 pub fn parts_key_meta(&self) -> impl Iterator<Item = (&str, &str)> + '_ {
326 self.parts
327 .iter()
328 .cloned()
329 .map(move |(key, meta)| (&self.id[key], &self.id[meta]))
330 }
331
332 #[inline]
333 pub fn rparts(&self) -> impl Iterator<Item = &str> + '_ {
334 self.parts
335 .iter()
336 .rev()
337 .map(move |(key, meta)| &self.id[key.start..meta.end])
338 }
339
340 #[inline]
341 pub fn rparts_key_meta(&self) -> impl Iterator<Item = (&str, &str)> + '_ {
342 self.parts
343 .iter()
344 .rev()
345 .cloned()
346 .map(move |(key, meta)| (&self.id[key], &self.id[meta]))
347 }
348
349 pub fn hashed_value(&self) -> u64 {
350 let mut hasher = DefaultHasher::new();
351 self.hash(&mut hasher);
352 hasher.finish()
353 }
354}
355
356impl Hash for WidgetId {
357 fn hash<H: Hasher>(&self, state: &mut H) {
358 self.id.hash(state);
359 }
360}
361
362impl PartialEq for WidgetId {
363 fn eq(&self, other: &Self) -> bool {
364 self.id == other.id
365 }
366}
367
368impl Eq for WidgetId {}
369
370impl Deref for WidgetId {
371 type Target = str;
372
373 fn deref(&self) -> &Self::Target {
374 &self.id
375 }
376}
377
378impl AsRef<str> for WidgetId {
379 fn as_ref(&self) -> &str {
380 &self.id
381 }
382}
383
384impl FromStr for WidgetId {
385 type Err = ();
386
387 fn from_str(s: &str) -> Result<Self, Self::Err> {
388 if let Some(index) = s.find(':') {
389 let type_name = s[..index].to_owned();
390 let rest = &s[(index + b":".len())..];
391 let path = rest.split('/').map(Cow::Borrowed).collect::<Vec<_>>();
392 Ok(Self::new(&type_name, &path))
393 } else {
394 Err(())
395 }
396 }
397}
398
399impl std::fmt::Debug for WidgetId {
400 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
401 f.debug_struct("WidgetId")
402 .field("id", &self.id)
403 .field("type_name", &&self.id[self.type_name.clone()])
404 .field(
405 "parts",
406 &self
407 .parts
408 .iter()
409 .map(|(key, meta)| (&self.id[key.to_owned()], &self.id[meta.to_owned()]))
410 .collect::<Vec<_>>(),
411 )
412 .finish()
413 }
414}
415
416impl std::fmt::Display for WidgetId {
417 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
418 f.write_str(self.as_ref())
419 }
420}
421
422impl TryFrom<WidgetIdDef> for WidgetId {
423 type Error = String;
424
425 fn try_from(id: WidgetIdDef) -> Result<Self, Self::Error> {
426 match Self::from_str(&id.0) {
427 Ok(id) => Ok(id),
428 Err(_) => Err(format!("Could not parse id: `{}`", id.0)),
429 }
430 }
431}
432
433#[derive(Debug, Clone, PartialEq, Eq)]
434pub struct WidgetIdCommon {
435 id: Option<WidgetId>,
436 count: usize,
437}
438
439impl Default for WidgetIdCommon {
440 fn default() -> Self {
441 Self {
442 id: None,
443 count: usize::MAX,
444 }
445 }
446}
447
448impl WidgetIdCommon {
449 pub fn new(id: WidgetId) -> Self {
450 Self {
451 count: id.depth(),
452 id: Some(id),
453 }
454 }
455
456 pub fn include(&mut self, id: &WidgetId) -> &mut Self {
457 if self.id.is_none() {
458 self.id = Some(id.to_owned());
459 self.count = id.depth();
460 return self;
461 }
462 if let Some(source) = self.id.as_ref() {
463 for index in 0..self.count.min(id.depth()) {
464 if source.part(index) != id.part(index) {
465 self.count = index;
466 return self;
467 }
468 }
469 }
470 self
471 }
472
473 pub fn include_other(&mut self, other: &Self) -> &mut Self {
474 if let Some(id) = other.id.as_ref() {
475 if self.id.is_none() {
476 self.id = Some(id.to_owned());
477 self.count = other.count;
478 return self;
479 }
480 if let Some(source) = self.id.as_ref() {
481 for index in 0..self.count.min(other.count) {
482 if source.part(index) != id.part(index) {
483 self.count = index;
484 return self;
485 }
486 }
487 }
488 }
489 self
490 }
491
492 pub fn is_valid(&self) -> bool {
493 self.id.is_some()
494 }
495
496 pub fn parts(&self) -> Option<impl Iterator<Item = &str>> {
497 self.id
498 .as_ref()
499 .map(|id| (0..self.count).map_while(move |index| id.part(index)))
500 }
501
502 pub fn path(&self) -> Option<&str> {
503 self.id
504 .as_ref()
505 .map(|id| id.range(0, self.count))
506 .filter(|id| !id.is_empty())
507 }
508}
509
510impl<'a> FromIterator<&'a WidgetId> for WidgetIdCommon {
511 fn from_iter<T: IntoIterator<Item = &'a WidgetId>>(iter: T) -> Self {
512 let mut result = Self::default();
513 for id in iter {
514 result.include(id);
515 }
516 result
517 }
518}
519
520#[derive(Debug, Default, Clone, Serialize, Deserialize)]
521pub struct WidgetRefDef(pub Option<WidgetId>);
522
523impl From<WidgetRef> for WidgetRefDef {
524 fn from(data: WidgetRef) -> Self {
525 match data.0.read() {
526 Ok(data) => Self(data.clone()),
527 Err(_) => Default::default(),
528 }
529 }
530}
531
532#[derive(PropsData, Debug, Default, Clone, Serialize, Deserialize)]
533#[serde(from = "WidgetRefDef")]
534#[serde(into = "WidgetRefDef")]
535pub struct WidgetRef(#[serde(skip)] Arc<RwLock<Option<WidgetId>>>);
536
537impl WidgetRef {
538 pub fn new(id: WidgetId) -> Self {
539 Self(Arc::new(RwLock::new(Some(id))))
540 }
541
542 pub(crate) fn write(&mut self, id: WidgetId) {
543 if let Ok(mut data) = self.0.write() {
544 *data = Some(id);
545 }
546 }
547
548 pub fn read(&self) -> Option<WidgetId> {
549 if let Ok(data) = self.0.read() {
550 data.clone()
551 } else {
552 None
553 }
554 }
555
556 pub fn exists(&self) -> bool {
557 self.0
558 .read()
559 .ok()
560 .map(|data| data.is_some())
561 .unwrap_or_default()
562 }
563}
564
565impl PartialEq for WidgetRef {
566 fn eq(&self, other: &Self) -> bool {
567 Arc::ptr_eq(&self.0, &other.0)
568 }
569}
570
571impl From<WidgetRefDef> for WidgetRef {
572 fn from(data: WidgetRefDef) -> Self {
573 WidgetRef(Arc::new(RwLock::new(data.0)))
574 }
575}
576
577impl std::fmt::Display for WidgetRef {
578 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
579 if let Ok(id) = self.0.read() {
580 if let Some(id) = id.as_ref() {
581 write!(f, "{id}")
582 } else {
583 Ok(())
584 }
585 } else {
586 Ok(())
587 }
588 }
589}
590
591#[derive(PropsData, Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
592pub enum WidgetIdOrRef {
593 #[default]
594 None,
595 Id(WidgetId),
596 Ref(WidgetRef),
597}
598
599impl WidgetIdOrRef {
600 #[inline]
601 pub fn new_ref() -> Self {
602 Self::Ref(WidgetRef::default())
603 }
604
605 #[inline]
606 pub fn is_none(&self) -> bool {
607 matches!(self, Self::None)
608 }
609
610 #[inline]
611 pub fn is_some(&self) -> bool {
612 !self.is_none()
613 }
614
615 pub fn read(&self) -> Option<WidgetId> {
616 match self {
617 Self::None => None,
618 Self::Id(id) => Some(id.to_owned()),
619 Self::Ref(idref) => idref.read(),
620 }
621 }
622}
623
624impl From<()> for WidgetIdOrRef {
625 fn from(_: ()) -> Self {
626 Self::None
627 }
628}
629
630impl From<WidgetId> for WidgetIdOrRef {
631 fn from(v: WidgetId) -> Self {
632 Self::Id(v)
633 }
634}
635
636impl From<WidgetRef> for WidgetIdOrRef {
637 fn from(v: WidgetRef) -> Self {
638 Self::Ref(v)
639 }
640}
641
642impl std::fmt::Display for WidgetIdOrRef {
643 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
644 match self {
645 Self::None => Ok(()),
646 Self::Id(id) => write!(f, "{id}"),
647 Self::Ref(id) => write!(f, "{id}"),
648 }
649 }
650}
651
652#[derive(Clone)]
653pub enum FnWidget {
654 Pointer(fn(WidgetContext) -> WidgetNode),
655 Closure(Arc<dyn Fn(WidgetContext) -> WidgetNode + Send + Sync>),
656}
657
658impl FnWidget {
659 pub fn pointer(value: fn(WidgetContext) -> WidgetNode) -> Self {
660 Self::Pointer(value)
661 }
662
663 pub fn closure(value: impl Fn(WidgetContext) -> WidgetNode + Send + Sync + 'static) -> Self {
664 Self::Closure(Arc::new(value))
665 }
666
667 pub fn call(&self, context: WidgetContext) -> WidgetNode {
668 match self {
669 Self::Pointer(value) => value(context),
670 Self::Closure(value) => value(context),
671 }
672 }
673}
674
675#[derive(Default)]
676pub struct WidgetLifeCycle {
677 #[allow(clippy::type_complexity)]
678 mount: Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
679 #[allow(clippy::type_complexity)]
680 change: Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
681 #[allow(clippy::type_complexity)]
682 unmount: Vec<Box<dyn FnMut(WidgetUnmountContext) + Send + Sync>>,
683}
684
685impl WidgetLifeCycle {
686 pub fn mount<F>(&mut self, f: F)
687 where
688 F: 'static + FnMut(WidgetMountOrChangeContext) + Send + Sync,
689 {
690 self.mount.push(Box::new(f));
691 }
692
693 pub fn change<F>(&mut self, f: F)
694 where
695 F: 'static + FnMut(WidgetMountOrChangeContext) + Send + Sync,
696 {
697 self.change.push(Box::new(f));
698 }
699
700 pub fn unmount<F>(&mut self, f: F)
701 where
702 F: 'static + FnMut(WidgetUnmountContext) + Send + Sync,
703 {
704 self.unmount.push(Box::new(f));
705 }
706
707 #[allow(clippy::type_complexity)]
708 pub fn unwrap(
709 self,
710 ) -> (
711 Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
712 Vec<Box<dyn FnMut(WidgetMountOrChangeContext) + Send + Sync>>,
713 Vec<Box<dyn FnMut(WidgetUnmountContext) + Send + Sync>>,
714 ) {
715 let Self {
716 mount,
717 change,
718 unmount,
719 } = self;
720 (mount, change, unmount)
721 }
722}
723
724pub fn none_widget(_: WidgetContext) -> WidgetNode {
725 Default::default()
726}
727
728pub fn setup(app: &mut Application) {
729 app.register_props::<()>("()");
730 app.register_props::<i8>("i8");
731 app.register_props::<i16>("i16");
732 app.register_props::<i32>("i32");
733 app.register_props::<i64>("i64");
734 app.register_props::<i128>("i128");
735 app.register_props::<u8>("u8");
736 app.register_props::<u16>("u16");
737 app.register_props::<u32>("u32");
738 app.register_props::<u64>("u64");
739 app.register_props::<u128>("u128");
740 app.register_props::<f32>("f32");
741 app.register_props::<f64>("f64");
742 app.register_props::<bool>("bool");
743 app.register_props::<String>("String");
744 app.register_props::<component::containers::anchor_box::AnchorProps>("AnchorProps");
745 app.register_props::<component::containers::anchor_box::PivotBoxProps>("PivotBoxProps");
746 app.register_props::<component::containers::content_box::ContentBoxProps>("ContentBoxProps");
747 app.register_props::<component::containers::flex_box::FlexBoxProps>("FlexBoxProps");
748 app.register_props::<component::containers::float_box::FloatBoxProps>("FloatBoxProps");
749 app.register_props::<component::containers::float_box::FloatBoxNotifyProps>(
750 "FloatBoxNotifyProps",
751 );
752 app.register_props::<component::containers::float_box::FloatBoxState>("FloatBoxState");
753 app.register_props::<component::containers::grid_box::GridBoxProps>("GridBoxProps");
754 app.register_props::<component::containers::horizontal_box::HorizontalBoxProps>(
755 "HorizontalBoxProps",
756 );
757 app.register_props::<component::containers::hidden_box::HiddenBoxProps>("HiddenBoxProps");
758 app.register_props::<component::containers::scroll_box::ScrollBoxOwner>("ScrollBoxOwner");
759 app.register_props::<component::containers::scroll_box::SideScrollbarsProps>(
760 "SideScrollbarsProps",
761 );
762 app.register_props::<component::containers::scroll_box::SideScrollbarsState>(
763 "SideScrollbarsState",
764 );
765 app.register_props::<component::containers::portal_box::PortalsContainer>("PortalsContainer");
766 app.register_props::<component::containers::responsive_box::MediaQueryExpression>(
767 "MediaQueryExpression",
768 );
769 app.register_props::<component::containers::responsive_box::ResponsiveBoxState>(
770 "ResponsiveBoxState",
771 );
772 app.register_props::<component::containers::size_box::SizeBoxProps>("SizeBoxProps");
773 app.register_props::<component::containers::switch_box::SwitchBoxProps>("SwitchBoxProps");
774 app.register_props::<component::containers::tabs_box::TabsBoxProps>("TabsBoxProps");
775 app.register_props::<component::containers::tabs_box::TabPlateProps>("TabPlateProps");
776 app.register_props::<component::containers::tooltip_box::TooltipState>("TooltipState");
777 app.register_props::<component::containers::variant_box::VariantBoxProps>("VariantBoxProps");
778 app.register_props::<component::containers::vertical_box::VerticalBoxProps>("VerticalBoxProps");
779 app.register_props::<component::containers::wrap_box::WrapBoxProps>("WrapBoxProps");
780 app.register_props::<component::image_box::ImageBoxProps>("ImageBoxProps");
781 app.register_props::<component::interactive::button::ButtonProps>("ButtonProps");
782 app.register_props::<component::interactive::button::ButtonNotifyProps>("ButtonNotifyProps");
783 app.register_props::<component::interactive::input_field::TextInputMode>("TextInputMode");
784 app.register_props::<component::interactive::input_field::TextInputProps>("TextInputProps");
785 app.register_props::<component::interactive::input_field::TextInputState>("TextInputState");
786 app.register_props::<component::interactive::input_field::TextInputNotifyProps>(
787 "TextInputNotifyProps",
788 );
789 app.register_props::<component::interactive::input_field::TextInputControlNotifyProps>(
790 "TextInputControlNotifyProps",
791 );
792 app.register_props::<component::interactive::options_view::OptionsViewMode>("OptionsViewMode");
793 app.register_props::<component::interactive::options_view::OptionsViewProps>(
794 "OptionsViewProps",
795 );
796 app.register_props::<component::interactive::slider_view::SliderViewProps>("SliderViewProps");
797 app.register_props::<component::interactive::navigation::NavItemActive>("NavItemActive");
798 app.register_props::<component::interactive::navigation::NavTrackingActive>(
799 "NavTrackingActive",
800 );
801 app.register_props::<component::interactive::navigation::NavContainerActive>(
802 "NavContainerActive",
803 );
804 app.register_props::<component::interactive::navigation::NavJumpLooped>("NavJumpLooped");
805 app.register_props::<component::interactive::navigation::NavJumpMapProps>("NavJumpMapProps");
806 app.register_props::<component::interactive::scroll_view::ScrollViewState>("ScrollViewState");
807 app.register_props::<component::interactive::scroll_view::ScrollViewRange>("ScrollViewRange");
808 app.register_props::<component::interactive::scroll_view::ScrollViewNotifyProps>(
809 "ScrollViewNotifyProps",
810 );
811 app.register_props::<component::MessageForwardProps>("MessageForwardProps");
812 app.register_props::<component::WidgetAlpha>("WidgetAlpha");
813 app.register_props::<component::space_box::SpaceBoxProps>("SpaceBoxProps");
814 app.register_props::<component::text_box::TextBoxProps>("TextBoxProps");
815 app.register_props::<unit::content::ContentBoxItemLayout>("ContentBoxItemLayout");
816 app.register_props::<unit::flex::FlexBoxItemLayout>("FlexBoxItemLayout");
817 app.register_props::<unit::grid::GridBoxItemLayout>("GridBoxItemLayout");
818
819 app.register_component("none_widget", FnWidget::pointer(none_widget));
820 app.register_component(
821 "area_box",
822 FnWidget::pointer(component::containers::area_box::area_box),
823 );
824 app.register_component(
825 "anchor_box",
826 FnWidget::pointer(component::containers::anchor_box::anchor_box),
827 );
828 app.register_component(
829 "pivot_box",
830 FnWidget::pointer(component::containers::anchor_box::pivot_box),
831 );
832 app.register_component(
833 "nav_content_box",
834 FnWidget::pointer(component::containers::content_box::nav_content_box),
835 );
836 app.register_component(
837 "content_box",
838 FnWidget::pointer(component::containers::content_box::content_box),
839 );
840 app.register_component(
841 "nav_flex_box",
842 FnWidget::pointer(component::containers::flex_box::nav_flex_box),
843 );
844 app.register_component(
845 "flex_box",
846 FnWidget::pointer(component::containers::flex_box::flex_box),
847 );
848 app.register_component(
849 "float_box",
850 FnWidget::pointer(component::containers::float_box::float_box),
851 );
852 app.register_component(
853 "nav_grid_box",
854 FnWidget::pointer(component::containers::grid_box::nav_grid_box),
855 );
856 app.register_component(
857 "grid_box",
858 FnWidget::pointer(component::containers::grid_box::grid_box),
859 );
860 app.register_component(
861 "nav_horizontal_box",
862 FnWidget::pointer(component::containers::horizontal_box::nav_horizontal_box),
863 );
864 app.register_component(
865 "horizontal_box",
866 FnWidget::pointer(component::containers::horizontal_box::horizontal_box),
867 );
868 app.register_component(
869 "nav_scroll_box",
870 FnWidget::pointer(component::containers::scroll_box::nav_scroll_box),
871 );
872 app.register_component(
873 "nav_scroll_box_side_scrollbars",
874 FnWidget::pointer(component::containers::scroll_box::nav_scroll_box_side_scrollbars),
875 );
876 app.register_component(
877 "portal_box",
878 FnWidget::pointer(component::containers::portal_box::portal_box),
879 );
880 app.register_component(
881 "responsive_box",
882 FnWidget::pointer(component::containers::responsive_box::responsive_box),
883 );
884 app.register_component(
885 "responsive_props_box",
886 FnWidget::pointer(component::containers::responsive_box::responsive_props_box),
887 );
888 app.register_component(
889 "size_box",
890 FnWidget::pointer(component::containers::size_box::size_box),
891 );
892 app.register_component(
893 "nav_switch_box",
894 FnWidget::pointer(component::containers::switch_box::nav_switch_box),
895 );
896 app.register_component(
897 "switch_box",
898 FnWidget::pointer(component::containers::switch_box::switch_box),
899 );
900 app.register_component(
901 "nav_tabs_box",
902 FnWidget::pointer(component::containers::tabs_box::nav_tabs_box),
903 );
904 app.register_component(
905 "tooltip_box",
906 FnWidget::pointer(component::containers::tooltip_box::tooltip_box),
907 );
908 app.register_component(
909 "portals_tooltip_box",
910 FnWidget::pointer(component::containers::tooltip_box::portals_tooltip_box),
911 );
912 app.register_component(
913 "variant_box",
914 FnWidget::pointer(component::containers::variant_box::variant_box),
915 );
916 app.register_component(
917 "nav_vertical_box",
918 FnWidget::pointer(component::containers::vertical_box::nav_vertical_box),
919 );
920 app.register_component(
921 "vertical_box",
922 FnWidget::pointer(component::containers::vertical_box::vertical_box),
923 );
924 app.register_component(
925 "wrap_box",
926 FnWidget::pointer(component::containers::wrap_box::wrap_box),
927 );
928 app.register_component(
929 "button",
930 FnWidget::pointer(component::interactive::button::button),
931 );
932 app.register_component(
933 "tracked_button",
934 FnWidget::pointer(component::interactive::button::tracked_button),
935 );
936 app.register_component(
937 "self_tracked_button",
938 FnWidget::pointer(component::interactive::button::self_tracked_button),
939 );
940 app.register_component(
941 "text_input",
942 FnWidget::pointer(component::interactive::input_field::text_input),
943 );
944 app.register_component(
945 "input_field",
946 FnWidget::pointer(component::interactive::input_field::input_field),
947 );
948 app.register_component(
949 "options_view",
950 FnWidget::pointer(component::interactive::options_view::options_view),
951 );
952 app.register_component(
953 "slider_view",
954 FnWidget::pointer(component::interactive::slider_view::slider_view),
955 );
956 app.register_component(
957 "navigation_barrier",
958 FnWidget::pointer(component::interactive::navigation::navigation_barrier),
959 );
960 app.register_component(
961 "tracking",
962 FnWidget::pointer(component::interactive::navigation::tracking),
963 );
964 app.register_component(
965 "self_tracking",
966 FnWidget::pointer(component::interactive::navigation::self_tracking),
967 );
968 app.register_component(
969 "space_box",
970 FnWidget::pointer(component::space_box::space_box),
971 );
972 app.register_component(
973 "image_box",
974 FnWidget::pointer(component::image_box::image_box),
975 );
976 app.register_component("text_box", FnWidget::pointer(component::text_box::text_box));
977}
978
979#[macro_export]
986macro_rules! make_widget {
987 ($type_id:path) => {{
988 let processor = $type_id;
989 let type_name = stringify!($type_id);
990 $crate::widget::component::WidgetComponent::new(
991 $crate::widget::FnWidget::pointer(processor),
992 type_name,
993 )
994 }};
995}
996
997#[macro_export]
999macro_rules! unpack_named_slots {
1000 ($map:expr => $name:ident) => {
1001 #[allow(unused_mut)]
1002 let mut $name = {
1003 let mut map = $map;
1004 match map.remove(stringify!($name)) {
1005 Some(widget) => widget,
1006 None => $crate::widget::node::WidgetNode::None,
1007 }
1008 };
1009 };
1010 ($map:expr => { $($name:ident),+ }) => {
1011 #[allow(unused_mut)]
1012 let ( $( mut $name ),+ ) = {
1013 let mut map = $map;
1014 (
1015 $(
1016 {
1017 match map.remove(stringify!($name)) {
1018 Some(widget) => widget,
1019 None => $crate::widget::node::WidgetNode::None,
1020 }
1021 }
1022 ),+
1023 )
1024 };
1025 };
1026}
1027
1028#[cfg(test)]
1029mod tests {
1030 use super::*;
1031
1032 #[test]
1033 fn test_widget_id() {
1034 let id = WidgetId::empty();
1035 assert_eq!(id.type_name(), "");
1036 assert_eq!(id.path(), "");
1037 assert_eq!(id.depth(), 0);
1038
1039 let id = WidgetId::new("type", &["parent".into(), "me".into()]);
1040 assert_eq!(id.to_string(), "type:parent/me".to_owned());
1041 assert_eq!(id.type_name(), "type");
1042 assert_eq!(id.parts().next().unwrap(), "parent");
1043 assert_eq!(id.key(), "me");
1044 assert_eq!(id.clone(), id);
1045
1046 let a = WidgetId::from_str("a:root/a").unwrap();
1047 let b = WidgetId::from_str("b:root/b").unwrap();
1048 let mut common = WidgetIdCommon::default();
1049 assert_eq!(common.path(), None);
1050 common.include(&a);
1051 assert_eq!(common.path(), Some("root/a"));
1052 let mut common = WidgetIdCommon::default();
1053 common.include(&b);
1054 assert_eq!(common.path(), Some("root/b"));
1055 common.include(&a);
1056 assert_eq!(common.path(), Some("root"));
1057
1058 let id = WidgetId::from_str("type:parent/me").unwrap();
1059 assert_eq!(&*id, "type:parent/me");
1060 assert_eq!(id.path(), "parent/me");
1061 let id = id.pop();
1062 assert_eq!(&*id, "type:parent");
1063 assert_eq!(id.path(), "parent");
1064 let id = id.pop();
1065 assert_eq!(&*id, "type:");
1066 assert_eq!(id.path(), "");
1067 let id = id.push("parent");
1068 assert_eq!(&*id, "type:parent");
1069 assert_eq!(id.path(), "parent");
1070 let id = id.push("me");
1071 assert_eq!(&*id, "type:parent/me");
1072 assert_eq!(id.path(), "parent/me");
1073 assert_eq!(id.key(), "me");
1074 assert_eq!(id.meta(), "");
1075 let id = id.push("with?meta");
1076 assert_eq!(&*id, "type:parent/me/with?meta");
1077 assert_eq!(id.path(), "parent/me/with?meta");
1078 assert_eq!(id.key(), "with");
1079 assert_eq!(id.meta(), "meta");
1080
1081 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1082 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1083 assert_eq!(a.distance_to(&b), Ok(0));
1084 assert!(!a.is_subset_of(&b));
1085 assert!(!a.is_superset_of(&b));
1086 let a = WidgetId::from_str("a:root/a/b").unwrap();
1087 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1088 assert_eq!(a.distance_to(&b), Ok(-1));
1089 assert!(a.is_subset_of(&b));
1090 assert!(!a.is_superset_of(&b));
1091 let a = WidgetId::from_str("a:root/a").unwrap();
1092 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1093 assert_eq!(a.distance_to(&b), Ok(-2));
1094 assert!(a.is_subset_of(&b));
1095 assert!(!a.is_superset_of(&b));
1096 let a = WidgetId::from_str("a:root").unwrap();
1097 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1098 assert_eq!(a.distance_to(&b), Ok(-3));
1099 assert!(a.is_subset_of(&b));
1100 assert!(!a.is_superset_of(&b));
1101 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1102 let b = WidgetId::from_str("b:root/a/b").unwrap();
1103 assert_eq!(a.distance_to(&b), Ok(1));
1104 assert!(!a.is_subset_of(&b));
1105 assert!(a.is_superset_of(&b));
1106 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1107 let b = WidgetId::from_str("b:root/a").unwrap();
1108 assert_eq!(a.distance_to(&b), Ok(2));
1109 assert!(!a.is_subset_of(&b));
1110 assert!(a.is_superset_of(&b));
1111 let a = WidgetId::from_str("a:root/a/b/c").unwrap();
1112 let b = WidgetId::from_str("b:root").unwrap();
1113 assert_eq!(a.distance_to(&b), Ok(3));
1114 assert!(!a.is_subset_of(&b));
1115 assert!(a.is_superset_of(&b));
1116 let a = WidgetId::from_str("a:root/a/b/x").unwrap();
1117 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1118 assert_eq!(a.distance_to(&b), Err(-1));
1119 assert!(!a.is_subset_of(&b));
1120 assert!(!a.is_superset_of(&b));
1121 let a = WidgetId::from_str("a:root/a/x/y").unwrap();
1122 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1123 assert_eq!(a.distance_to(&b), Err(-2));
1124 assert!(!a.is_subset_of(&b));
1125 assert!(!a.is_superset_of(&b));
1126 let a = WidgetId::from_str("a:root/x/y/z").unwrap();
1127 let b = WidgetId::from_str("b:root/a/b/c").unwrap();
1128 assert_eq!(a.distance_to(&b), Err(-3));
1129 assert!(!a.is_subset_of(&b));
1130 assert!(!a.is_superset_of(&b));
1131 }
1132}