1use std::{
2 fmt,
3 hash::{Hash, Hasher},
4 sync::atomic::{AtomicUsize, Ordering},
5 cmp::Ordering as CmpOrdering,
6 iter::FromIterator,
7 ffi::c_void,
8};
9use crate::{
10 callbacks::{
11 Callback, CallbackType,
12 IFrameCallback, IFrameCallbackType,
13 RefAny,
14 },
15 app_resources::{ImageId, TextId},
16 id_tree::{Arena, NodeDataContainer},
17};
18#[cfg(feature = "opengl")]
19use crate::callbacks::{GlCallback, GlCallbackType};
20use azul_css::{NodeTypePath, CssProperty};
21pub use crate::id_tree::{NodeHierarchy, Node, NodeId};
22
23static TAG_ID: AtomicUsize = AtomicUsize::new(1);
24
25#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
27pub struct TagId(pub u64);
28
29impl ::std::fmt::Display for TagId {
30 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
31 write!(f, "ScrollTagId({})", self.0)
32 }
33}
34
35impl ::std::fmt::Debug for TagId {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 write!(f, "{}", self)
38 }
39}
40
41
42#[derive(Copy, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
44pub struct ScrollTagId(pub TagId);
45
46impl ::std::fmt::Display for ScrollTagId {
47 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
48 write!(f, "ScrollTagId({})", (self.0).0)
49 }
50}
51
52impl ::std::fmt::Debug for ScrollTagId {
53 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
54 write!(f, "{}", self)
55 }
56}
57
58impl TagId {
59 pub fn new() -> Self {
60 TagId(TAG_ID.fetch_add(1, Ordering::SeqCst) as u64)
61 }
62 pub fn reset() {
63 TAG_ID.swap(1, Ordering::SeqCst);
64 }
65}
66
67impl ScrollTagId {
68 pub fn new() -> ScrollTagId {
69 ScrollTagId(TagId::new())
70 }
71}
72
73static DOM_ID: AtomicUsize = AtomicUsize::new(1);
74
75#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
78pub struct DomId {
79 id: usize,
81 parent: Option<(Box<DomId>, NodeId)>,
85}
86
87impl DomId {
88
89 pub const ROOT_ID: DomId = Self { id: 0, parent: None };
91
92 #[inline(always)]
94 pub fn new(parent: Option<(DomId, NodeId)>) -> DomId {
95 DomId {
96 id: DOM_ID.fetch_add(1, Ordering::SeqCst),
97 parent: parent.map(|(p, node_id)| (Box::new(p), node_id)),
98 }
99 }
100
101 #[inline(always)]
103 pub fn reset() {
104 DOM_ID.swap(0, Ordering::SeqCst);
105 }
106
107 #[inline(always)]
109 pub fn is_root(&self) -> bool {
110 *self == Self::ROOT_ID
111 }
112}
113
114#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
116pub struct DomHash(pub u64);
117
118#[derive(Debug, Clone, PartialEq, Hash, Eq)]
120pub enum NodeType {
121 Div,
123 Body,
125 Label(DomString),
127 Text(TextId),
129 Image(ImageId),
132 #[cfg(feature = "opengl")]
136 GlTexture((GlCallback, RefAny)),
137 IFrame((IFrameCallback, RefAny)),
139}
140
141impl NodeType {
142 fn get_text_content(&self) -> Option<String> {
143 use self::NodeType::*;
144 match self {
145 Div | Body => None,
146 Label(s) => Some(format!("{}", s)),
147 Image(id) => Some(format!("image({:?})", id)),
148 Text(t) => Some(format!("textid({:?})", t)),
149 #[cfg(feature = "opengl")]
150 GlTexture(g) => Some(format!("gltexture({:?})", g)),
151 IFrame(i) => Some(format!("iframe({:?})", i)),
152 }
153 }
154}
155
156impl NodeType {
157 #[inline]
158 pub fn get_path(&self) -> NodeTypePath {
159 use self::NodeType::*;
160 match self {
161 Div => NodeTypePath::Div,
162 Body => NodeTypePath::Body,
163 Label(_) | Text(_) => NodeTypePath::P,
164 Image(_) => NodeTypePath::Img,
165 #[cfg(feature = "opengl")]
166 GlTexture(_) => NodeTypePath::Texture,
167 IFrame(_) => NodeTypePath::IFrame,
168 }
169 }
170}
171
172#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
174pub enum On {
175 MouseOver,
177 MouseDown,
180 LeftMouseDown,
183 MiddleMouseDown,
186 RightMouseDown,
189 MouseUp,
191 LeftMouseUp,
194 MiddleMouseUp,
197 RightMouseUp,
200 MouseEnter,
202 MouseLeave,
204 Scroll,
206 TextInput,
209 VirtualKeyDown,
216 VirtualKeyUp,
218 HoveredFile,
220 DroppedFile,
222 HoveredFileCancelled,
224 FocusReceived,
226 FocusLost,
228}
229
230#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
242pub enum EventFilter {
243 Hover(HoverEventFilter),
246 Not(NotEventFilter),
251 Focus(FocusEventFilter),
253 Window(WindowEventFilter),
263}
264
265macro_rules! get_single_enum_type {
281 ($fn_name:ident, $enum_name:ident::$variant:ident($return_type:ty)) => (
282 pub fn $fn_name(&self) -> Option<$return_type> {
283 use self::$enum_name::*;
284 match self {
285 $variant(e) => Some(*e),
286 _ => None,
287 }
288 }
289 )
290}
291
292impl EventFilter {
293 get_single_enum_type!(as_hover_event_filter, EventFilter::Hover(HoverEventFilter));
294 get_single_enum_type!(as_focus_event_filter, EventFilter::Focus(FocusEventFilter));
295 get_single_enum_type!(as_not_event_filter, EventFilter::Not(NotEventFilter));
296 get_single_enum_type!(as_window_event_filter, EventFilter::Window(WindowEventFilter));
297}
298
299impl From<On> for EventFilter {
300 fn from(input: On) -> EventFilter {
301 use self::On::*;
302 match input {
303 MouseOver => EventFilter::Hover(HoverEventFilter::MouseOver),
304 MouseDown => EventFilter::Hover(HoverEventFilter::MouseDown),
305 LeftMouseDown => EventFilter::Hover(HoverEventFilter::LeftMouseDown),
306 MiddleMouseDown => EventFilter::Hover(HoverEventFilter::MiddleMouseDown),
307 RightMouseDown => EventFilter::Hover(HoverEventFilter::RightMouseDown),
308 MouseUp => EventFilter::Hover(HoverEventFilter::MouseUp),
309 LeftMouseUp => EventFilter::Hover(HoverEventFilter::LeftMouseUp),
310 MiddleMouseUp => EventFilter::Hover(HoverEventFilter::MiddleMouseUp),
311 RightMouseUp => EventFilter::Hover(HoverEventFilter::RightMouseUp),
312
313 MouseEnter => EventFilter::Hover(HoverEventFilter::MouseEnter),
314 MouseLeave => EventFilter::Hover(HoverEventFilter::MouseLeave),
315 Scroll => EventFilter::Hover(HoverEventFilter::Scroll),
316 TextInput => EventFilter::Focus(FocusEventFilter::TextInput), VirtualKeyDown => EventFilter::Window(WindowEventFilter::VirtualKeyDown), VirtualKeyUp => EventFilter::Window(WindowEventFilter::VirtualKeyUp), HoveredFile => EventFilter::Hover(HoverEventFilter::HoveredFile),
320 DroppedFile => EventFilter::Hover(HoverEventFilter::DroppedFile),
321 HoveredFileCancelled => EventFilter::Hover(HoverEventFilter::HoveredFileCancelled),
322 FocusReceived => EventFilter::Focus(FocusEventFilter::FocusReceived), FocusLost => EventFilter::Focus(FocusEventFilter::FocusLost), }
325 }
326}
327
328#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
330pub enum HoverEventFilter {
331 MouseOver,
332 MouseDown,
333 LeftMouseDown,
334 RightMouseDown,
335 MiddleMouseDown,
336 MouseUp,
337 LeftMouseUp,
338 RightMouseUp,
339 MiddleMouseUp,
340 MouseEnter,
341 MouseLeave,
342 Scroll,
343 ScrollStart,
344 ScrollEnd,
345 TextInput,
346 VirtualKeyDown,
347 VirtualKeyUp,
348 HoveredFile,
349 DroppedFile,
350 HoveredFileCancelled,
351}
352
353impl HoverEventFilter {
354 pub fn to_focus_event_filter(&self) -> Option<FocusEventFilter> {
355 use self::HoverEventFilter::*;
356 match self {
357 MouseOver => Some(FocusEventFilter::MouseOver),
358 MouseDown => Some(FocusEventFilter::MouseDown),
359 LeftMouseDown => Some(FocusEventFilter::LeftMouseDown),
360 RightMouseDown => Some(FocusEventFilter::RightMouseDown),
361 MiddleMouseDown => Some(FocusEventFilter::MiddleMouseDown),
362 MouseUp => Some(FocusEventFilter::MouseUp),
363 LeftMouseUp => Some(FocusEventFilter::LeftMouseUp),
364 RightMouseUp => Some(FocusEventFilter::RightMouseUp),
365 MiddleMouseUp => Some(FocusEventFilter::MiddleMouseUp),
366 MouseEnter => Some(FocusEventFilter::MouseEnter),
367 MouseLeave => Some(FocusEventFilter::MouseLeave),
368 Scroll => Some(FocusEventFilter::Scroll),
369 ScrollStart => Some(FocusEventFilter::ScrollStart),
370 ScrollEnd => Some(FocusEventFilter::ScrollEnd),
371 TextInput => Some(FocusEventFilter::TextInput),
372 VirtualKeyDown => Some(FocusEventFilter::VirtualKeyDown),
373 VirtualKeyUp => Some(FocusEventFilter::VirtualKeyDown),
374 HoveredFile => None,
375 DroppedFile => None,
376 HoveredFileCancelled => None,
377 }
378 }
379}
380
381#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
385pub enum NotEventFilter {
386 Hover(HoverEventFilter),
387 Focus(FocusEventFilter),
388}
389
390#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
395pub enum FocusEventFilter {
396 MouseOver,
397 MouseDown,
398 LeftMouseDown,
399 RightMouseDown,
400 MiddleMouseDown,
401 MouseUp,
402 LeftMouseUp,
403 RightMouseUp,
404 MiddleMouseUp,
405 MouseEnter,
406 MouseLeave,
407 Scroll,
408 ScrollStart,
409 ScrollEnd,
410 TextInput,
411 VirtualKeyDown,
412 VirtualKeyUp,
413 FocusReceived,
414 FocusLost,
415}
416
417#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
420pub enum WindowEventFilter {
421 MouseOver,
422 MouseDown,
423 LeftMouseDown,
424 RightMouseDown,
425 MiddleMouseDown,
426 MouseUp,
427 LeftMouseUp,
428 RightMouseUp,
429 MiddleMouseUp,
430 MouseEnter,
431 MouseLeave,
432 Scroll,
433 ScrollStart,
434 ScrollEnd,
435 TextInput,
436 VirtualKeyDown,
437 VirtualKeyUp,
438 HoveredFile,
439 DroppedFile,
440 HoveredFileCancelled,
441}
442
443impl WindowEventFilter {
444 pub fn to_hover_event_filter(&self) -> Option<HoverEventFilter> {
445 use self::WindowEventFilter::*;
446 match self {
447 MouseOver => Some(HoverEventFilter::MouseOver),
448 MouseDown => Some(HoverEventFilter::MouseDown),
449 LeftMouseDown => Some(HoverEventFilter::LeftMouseDown),
450 RightMouseDown => Some(HoverEventFilter::RightMouseDown),
451 MiddleMouseDown => Some(HoverEventFilter::MiddleMouseDown),
452 MouseUp => Some(HoverEventFilter::MouseUp),
453 LeftMouseUp => Some(HoverEventFilter::LeftMouseUp),
454 RightMouseUp => Some(HoverEventFilter::RightMouseUp),
455 MiddleMouseUp => Some(HoverEventFilter::MiddleMouseUp),
456 Scroll => Some(HoverEventFilter::Scroll),
457 ScrollStart => Some(HoverEventFilter::ScrollStart),
458 ScrollEnd => Some(HoverEventFilter::ScrollEnd),
459 TextInput => Some(HoverEventFilter::TextInput),
460 VirtualKeyDown => Some(HoverEventFilter::VirtualKeyDown),
461 VirtualKeyUp => Some(HoverEventFilter::VirtualKeyDown),
462 HoveredFile => Some(HoverEventFilter::HoveredFile),
463 DroppedFile => Some(HoverEventFilter::DroppedFile),
464 HoveredFileCancelled => Some(HoverEventFilter::HoveredFileCancelled),
465 MouseEnter => None,
468 MouseLeave => None,
469 }
470 }
471}
472
473pub struct NodeData {
475 node_type: NodeType,
477 ids: Vec<DomString>,
479 classes: Vec<DomString>,
481 callbacks: Vec<(EventFilter, (Callback, RefAny))>,
483 dynamic_css_overrides: Vec<(DomString, CssProperty)>,
499 is_draggable: bool,
503 tab_index: Option<TabIndex>,
507}
508
509#[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd, Hash)]
510pub enum TabIndex {
511 Auto,
517 OverrideInParent(usize),
534 NoKeyboardFocus,
537}
538
539impl TabIndex {
540 pub fn get_index(&self) -> isize {
542 use self::TabIndex::*;
543 match self {
544 Auto => 0,
545 OverrideInParent(x) => *x as isize,
546 NoKeyboardFocus => -1,
547 }
548 }
549}
550impl Default for TabIndex {
551 fn default() -> Self {
552 TabIndex::Auto
553 }
554}
555
556impl PartialEq for NodeData {
557 fn eq(&self, other: &Self) -> bool {
558 self.node_type == other.node_type &&
559 self.ids == other.ids &&
560 self.classes == other.classes &&
561 self.callbacks == other.callbacks &&
562 self.dynamic_css_overrides == other.dynamic_css_overrides &&
563 self.is_draggable == other.is_draggable &&
564 self.tab_index == other.tab_index
565 }
566}
567
568impl Eq for NodeData { }
569
570impl Default for NodeData {
571 fn default() -> Self {
572 NodeData::new(NodeType::Div)
573 }
574}
575
576impl Hash for NodeData {
577 fn hash<H: Hasher>(&self, state: &mut H) {
578 self.node_type.hash(state);
579 for id in &self.ids {
580 id.hash(state);
581 }
582 for class in &self.classes {
583 class.hash(state);
584 }
585 for callback in &self.callbacks {
586 callback.hash(state);
587 }
588 for dynamic_css_override in &self.dynamic_css_overrides {
589 dynamic_css_override.hash(state);
590 }
591 self.is_draggable.hash(state);
592 self.tab_index.hash(state);
593 }
594}
595
596impl Clone for NodeData {
597 fn clone(&self) -> Self {
598 Self {
599 node_type: self.node_type.clone(),
600 ids: self.ids.clone(),
601 classes: self.classes.clone(),
602 callbacks: self.callbacks.clone(),
603 dynamic_css_overrides: self.dynamic_css_overrides.clone(),
604 is_draggable: self.is_draggable.clone(),
605 tab_index: self.tab_index.clone(),
606 }
607 }
608}
609
610impl fmt::Display for NodeData {
611 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
612
613 let html_type = self.node_type.get_path();
614 let attributes_string = node_data_to_string(&self);
615
616 match self.node_type.get_text_content() {
617 Some(content) => write!(f, "<{}{}>{}</{}>", html_type, attributes_string, content, html_type),
618 None => write!(f, "<{}{}/>", html_type, attributes_string)
619 }
620 }
621}
622
623impl NodeData {
624 pub fn debug_print_start(&self, close_self: bool) -> String {
625 let html_type = self.node_type.get_path();
626 let attributes_string = node_data_to_string(&self);
627 format!("<{}{}{}>", html_type, attributes_string, if close_self { " /" } else { "" })
628 }
629
630 pub fn debug_print_end(&self) -> String {
631 let html_type = self.node_type.get_path();
632 format!("</{}>", html_type)
633 }
634}
635
636fn node_data_to_string(node_data: &NodeData) -> String {
637
638 let id_string = if node_data.ids.is_empty() {
639 String::new()
640 } else {
641 format!(" id=\"{}\"", node_data.ids.iter().map(|s| s.as_str().to_string()).collect::<Vec<String>>().join(" "))
642 };
643
644 let class_string = if node_data.classes.is_empty() {
645 String::new()
646 } else {
647 format!(" class=\"{}\"", node_data.classes.iter().map(|s| s.as_str().to_string()).collect::<Vec<String>>().join(" "))
648 };
649
650 let draggable = if node_data.is_draggable {
651 format!(" draggable=\"true\"")
652 } else {
653 String::new()
654 };
655
656 let tabindex = if let Some(tab_index) = node_data.tab_index {
657 format!(" tabindex=\"{}\"", tab_index.get_index())
658 } else {
659 String::new()
660 };
661
662 let callbacks = if node_data.callbacks.is_empty() {
663 String::new()
664 } else {
665 format!(" callbacks=\"{}\"", node_data.callbacks.iter().map(|(evt, cb)| format!("({:?}={:?})", evt, cb)).collect::<Vec<String>>().join(" "))
666 };
667
668 let css_overrides = if node_data.dynamic_css_overrides.is_empty() {
669 String::new()
670 } else {
671 format!(" css-overrides=\"{}\"", node_data.dynamic_css_overrides.iter().map(|(id, prop)| format!("{}={:?};", id, prop)).collect::<Vec<String>>().join(" "))
672 };
673
674 format!("{}{}{}{}{}{}", id_string, class_string, tabindex, draggable, callbacks, css_overrides)
675}
676
677impl fmt::Debug for NodeData {
678 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
679 write!(f, "NodeData {{")?;
680 write!(f, "\tnode_type: {:?}", self.node_type)?;
681
682 if !self.ids.is_empty() { write!(f, "\tids: {:?}", self.ids)?; }
683 if !self.classes.is_empty() { write!(f, "\tclasses: {:?}", self.classes)?; }
684 if !self.callbacks.is_empty() { write!(f, "\tcallbacks: {:?}", self.callbacks)?; }
685 if !self.dynamic_css_overrides.is_empty() { write!(f, "\tdynamic_css_overrides: {:?}", self.dynamic_css_overrides)?; }
686 if self.is_draggable { write!(f, "\tis_draggable: {:?}", self.is_draggable)?; }
687 if let Some(t) = self.tab_index { write!(f, "\ttab_index: {:?}", t)?; }
688 write!(f, "}}")?;
689 Ok(())
690 }
691}
692
693impl NodeData {
694
695 #[inline]
697 pub const fn new(node_type: NodeType) -> Self {
698 Self {
699 node_type,
700 ids: Vec::new(),
701 classes: Vec::new(),
702 callbacks: Vec::new(),
703 dynamic_css_overrides: Vec::new(),
704 is_draggable: false,
705 tab_index: None,
706 }
707 }
708
709 #[inline]
711 pub fn is_node_type(&self, searched_type: NodeType) -> bool {
712 self.node_type == searched_type
713 }
714
715 pub fn has_id(&self, id: &str) -> bool {
717 self.ids.iter().any(|self_id| self_id.equals_str(id))
718 }
719
720 pub fn has_class(&self, class: &str) -> bool {
722 self.classes.iter().any(|self_class| self_class.equals_str(class))
723 }
724
725 pub fn calculate_node_data_hash(&self) -> DomHash {
726
727 use std::collections::hash_map::DefaultHasher as HashAlgorithm;
728
729 let mut hasher = HashAlgorithm::default();
730 self.hash(&mut hasher);
731
732 DomHash(hasher.finish())
733 }
734
735 #[inline(always)]
737 pub const fn body() -> Self {
738 Self::new(NodeType::Body)
739 }
740
741 #[inline(always)]
743 pub const fn div() -> Self {
744 Self::new(NodeType::Div)
745 }
746
747 #[inline(always)]
749 pub fn label<S: Into<DomString>>(value: S) -> Self {
750 Self::new(NodeType::Label(value.into()))
751 }
752
753 #[inline(always)]
755 pub const fn text_id(text_id: TextId) -> Self {
756 Self::new(NodeType::Text(text_id))
757 }
758
759 #[inline(always)]
761 pub const fn image(image: ImageId) -> Self {
762 Self::new(NodeType::Image(image))
763 }
764
765 #[inline(always)]
767 #[cfg(feature = "opengl")]
768 pub fn gl_texture(callback: GlCallbackType, ptr: RefAny) -> Self {
769 Self::new(NodeType::GlTexture((GlCallback(callback), ptr)))
770 }
771
772 #[inline(always)]
774 pub fn iframe(callback: IFrameCallbackType, ptr: RefAny) -> Self {
775 Self::new(NodeType::IFrame((IFrameCallback(callback), ptr)))
776 }
777
778 #[inline(always)]
782 pub const fn get_node_type(&self) -> &NodeType { &self.node_type }
783 #[inline(always)]
784 pub const fn get_ids(&self) -> &Vec<DomString> { &self.ids }
785 #[inline(always)]
786 pub const fn get_classes(&self) -> &Vec<DomString> { &self.classes }
787 #[inline(always)]
788 pub const fn get_callbacks(&self) -> &Vec<(EventFilter, (Callback, RefAny))> { &self.callbacks }
789 #[inline(always)]
790 pub const fn get_dynamic_css_overrides(&self) -> &Vec<(DomString, CssProperty)> { &self.dynamic_css_overrides }
791 #[inline(always)]
792 pub const fn get_is_draggable(&self) -> bool { self.is_draggable }
793 #[inline(always)]
794 pub const fn get_tab_index(&self) -> Option<TabIndex> { self.tab_index }
795
796 #[inline(always)]
797 pub fn set_node_type(&mut self, node_type: NodeType) { self.node_type = node_type; }
798 #[inline(always)]
799 pub fn set_ids(&mut self, ids: Vec<DomString>) { self.ids = ids; }
800 #[inline(always)]
801 pub fn set_classes(&mut self, classes: Vec<DomString>) { self.classes = classes; }
802 #[inline(always)]
803 pub fn set_callbacks(&mut self, callbacks: Vec<(EventFilter, (Callback, RefAny))>) { self.callbacks = callbacks; }
804 #[inline(always)]
805 pub fn set_dynamic_css_overrides(&mut self, dynamic_css_overrides: Vec<(DomString, CssProperty)>) { self.dynamic_css_overrides = dynamic_css_overrides; }
806 #[inline(always)]
807 pub fn set_is_draggable(&mut self, is_draggable: bool) { self.is_draggable = is_draggable; }
808 #[inline(always)]
809 pub fn set_tab_index(&mut self, tab_index: Option<TabIndex>) { self.tab_index = tab_index; }
810
811 #[inline(always)]
812 pub fn with_node_type(self, node_type: NodeType) -> Self { Self { node_type, .. self } }
813 #[inline(always)]
814 pub fn with_ids(self, ids: Vec<DomString>) -> Self { Self { ids, .. self } }
815 #[inline(always)]
816 pub fn with_classes(self, classes: Vec<DomString>) -> Self { Self { classes, .. self } }
817 #[inline(always)]
818 pub fn with_callbacks(self, callbacks: Vec<(EventFilter, (Callback, RefAny))>) -> Self { Self { callbacks, .. self } }
819 #[inline(always)]
820 pub fn with_dynamic_css_overrides(self, dynamic_css_overrides: Vec<(DomString, CssProperty)>) -> Self { Self { dynamic_css_overrides, .. self } }
821 #[inline(always)]
822 pub fn is_draggable(self, is_draggable: bool) -> Self { Self { is_draggable, .. self } }
823 #[inline(always)]
824 pub fn with_tab_index(self, tab_index: Option<TabIndex>) -> Self { Self { tab_index, .. self } }
825}
826
827#[derive(Clone)]
832pub enum DomString {
833 Static(&'static str),
834 Heap(String),
835}
836
837impl Eq for DomString { }
838
839impl PartialEq for DomString {
840 fn eq(&self, other: &Self) -> bool {
841 self.as_str() == other.as_str()
842 }
843}
844
845impl PartialOrd for DomString {
846 fn partial_cmp(&self, other: &Self) -> Option<CmpOrdering> {
847 Some(self.as_str().cmp(other.as_str()))
848 }
849}
850
851impl Ord for DomString {
852 fn cmp(&self, other: &Self) -> CmpOrdering {
853 self.as_str().cmp(other.as_str())
854 }
855}
856
857impl Hash for DomString {
858 fn hash<H: Hasher>(&self, state: &mut H) {
859 self.as_str().hash(state);
860 }
861}
862
863impl DomString {
864
865 pub fn equals_str(&self, target: &str) -> bool {
866 use self::DomString::*;
867 match &self {
868 Static(s) => *s == target,
869 Heap(h) => h == target,
870 }
871 }
872
873 pub fn as_str(&self) -> &str {
874 use self::DomString::*;
875 match &self {
876 Static(s) => s,
877 Heap(h) => h,
878 }
879 }
880}
881
882impl fmt::Debug for DomString {
883 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
884 use self::DomString::*;
885 match &self {
886 Static(s) => write!(f, "\"{}\"", s),
887 Heap(h) => write!(f, "\"{}\"", h),
888 }
889 }
890}
891
892impl fmt::Display for DomString {
893 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
894 use self::DomString::*;
895 match &self {
896 Static(s) => write!(f, "{}", s),
897 Heap(h) => write!(f, "{}", h),
898 }
899 }
900}
901
902impl From<String> for DomString {
903 fn from(e: String) -> Self {
904 DomString::Heap(e)
905 }
906}
907
908impl From<&'static str> for DomString {
909 fn from(e: &'static str) -> Self {
910 DomString::Static(e)
911 }
912}
913
914pub struct Dom {
916 pub root: NodeData,
917 pub children: Vec<Dom>,
918 estimated_total_children: usize,
921}
922
923#[no_mangle] #[repr(C)] pub struct DomPtr { pub ptr: *mut c_void }
925
926impl Clone for Dom {
927 fn clone(&self) -> Self {
928 Dom {
929 root: self.root.clone(),
930 children: self.children.clone(),
931 estimated_total_children: self.estimated_total_children,
932 }
933 }
934}
935
936fn compare_dom(a: &Dom, b: &Dom) -> bool {
937 a.root == b.root &&
938 a.estimated_total_children == b.estimated_total_children &&
939 a.children.len() == b.children.len() &&
940 a.children.iter().zip(b.children.iter()).all(|(a, b)| compare_dom(a, b))
941}
942
943impl PartialEq for Dom {
944 fn eq(&self, rhs: &Self) -> bool {
945 compare_dom(self, rhs)
946 }
947}
948
949impl Eq for Dom { }
950
951fn print_dom(d: &Dom, f: &mut fmt::Formatter) -> fmt::Result {
952 write!(f, "Dom {{\r\n")?;
953 write!(f, "\troot: {:#?}", d.root)?;
954 write!(f, "\testimated_total_children: {:#?}", d.estimated_total_children)?;
955 write!(f, "\tchildren: [")?;
956 for c in &d.children {
957 print_dom(c, f)?;
958 }
959 write!(f, "\t]")?;
960 write!(f, "}}")?;
961 Ok(())
962}
963
964impl fmt::Debug for Dom {
965 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
966 print_dom(self, f)
967 }
968}
969
970impl FromIterator<Dom> for Dom {
971 fn from_iter<I: IntoIterator<Item=Dom>>(iter: I) -> Self {
972
973 let mut estimated_total_children = 0;
974 let children = iter.into_iter().map(|c| {
975 estimated_total_children += c.estimated_total_children + 1;
976 c
977 }).collect();
978
979 Dom {
980 root: NodeData::div(),
981 children,
982 estimated_total_children,
983 }
984 }
985}
986
987impl FromIterator<NodeData> for Dom {
988 fn from_iter<I: IntoIterator<Item=NodeData>>(iter: I) -> Self {
989
990 let children = iter.into_iter().map(|c| Dom { root: c, children: Vec::new(), estimated_total_children: 0 }).collect::<Vec<_>>();
991 let estimated_total_children = children.len();
992
993 Dom {
994 root: NodeData::div(),
995 children,
996 estimated_total_children,
997 }
998 }
999}
1000
1001impl FromIterator<NodeType> for Dom {
1002 fn from_iter<I: IntoIterator<Item=NodeType>>(iter: I) -> Self {
1003 iter.into_iter().map(|i| NodeData { node_type: i, .. Default::default() }).collect()
1004 }
1005}
1006
1007pub(crate) fn convert_dom_into_compact_dom(dom: Dom) -> CompactDom {
1008
1009 let default_node_data = NodeData::div();
1011
1012 let mut arena = Arena {
1013 node_hierarchy: NodeHierarchy { internal: vec![Node::ROOT; dom.estimated_total_children + 1] },
1014 node_data: NodeDataContainer { internal: vec![default_node_data; dom.estimated_total_children + 1] },
1015 };
1016
1017 let root_node_id = NodeId::ZERO;
1018 let mut cur_node_id = 0;
1019 let root_node = Node {
1020 parent: None,
1021 previous_sibling: None,
1022 next_sibling: None,
1023 first_child: if dom.children.is_empty() { None } else { Some(root_node_id + 1) },
1024 last_child: if dom.children.is_empty() { None } else { Some(root_node_id + dom.estimated_total_children) },
1025 };
1026
1027 convert_dom_into_compact_dom_internal(dom, &mut arena, root_node_id, root_node, &mut cur_node_id);
1028
1029 CompactDom {
1030 arena,
1031 root: root_node_id,
1032 }
1033}
1034
1035fn convert_dom_into_compact_dom_internal(
1037 dom: Dom,
1038 arena: &mut Arena<NodeData>,
1039 parent_node_id: NodeId,
1040 node: Node,
1041 cur_node_id: &mut usize
1042) {
1043
1044 arena.node_hierarchy[parent_node_id] = node;
1055 arena.node_data[parent_node_id] = dom.root;
1056 *cur_node_id += 1;
1057
1058 let mut previous_sibling_id = None;
1059 let children_len = dom.children.len();
1060 for (child_index, child_dom) in dom.children.into_iter().enumerate() {
1061 let child_node_id = NodeId::new(*cur_node_id);
1062 let is_last_child = (child_index + 1) == children_len;
1063 let child_dom_is_empty = child_dom.children.is_empty();
1064 let child_node = Node {
1065 parent: Some(parent_node_id),
1066 previous_sibling: previous_sibling_id,
1067 next_sibling: if is_last_child { None } else { Some(child_node_id + child_dom.estimated_total_children + 1) },
1068 first_child: if child_dom_is_empty { None } else { Some(child_node_id + 1) },
1069 last_child: if child_dom_is_empty { None } else { Some(child_node_id + child_dom.estimated_total_children) },
1070 };
1071 previous_sibling_id = Some(child_node_id);
1072 convert_dom_into_compact_dom_internal(child_dom, arena, child_node_id, child_node, cur_node_id);
1074 }
1075}
1076
1077#[test]
1078fn test_compact_dom_conversion() {
1079
1080 use crate::dom::DomString::Static;
1081
1082 struct Dummy;
1083
1084 let dom: Dom<Dummy> = Dom::body()
1085 .with_child(Dom::div().with_class("class1"))
1086 .with_child(Dom::div().with_class("class1")
1087 .with_child(Dom::div().with_id("child_2"))
1088 )
1089 .with_child(Dom::div().with_class("class1"));
1090
1091 let expected_dom: CompactDom<Dummy> = CompactDom {
1092 root: NodeId::ZERO,
1093 arena: Arena {
1094 node_hierarchy: NodeHierarchy { internal: vec![
1095 Node {
1096 parent: None,
1097 previous_sibling: None,
1098 next_sibling: None,
1099 first_child: Some(NodeId::new(1)),
1100 last_child: Some(NodeId::new(4)),
1101 },
1102 Node {
1103 parent: Some(NodeId::new(0)),
1104 previous_sibling: None,
1105 next_sibling: Some(NodeId::new(2)),
1106 first_child: None,
1107 last_child: None,
1108 },
1109 Node {
1110 parent: Some(NodeId::new(0)),
1111 previous_sibling: Some(NodeId::new(1)),
1112 next_sibling: Some(NodeId::new(4)),
1113 first_child: Some(NodeId::new(3)),
1114 last_child: Some(NodeId::new(3)),
1115 },
1116 Node {
1117 parent: Some(NodeId::new(2)),
1118 previous_sibling: None,
1119 next_sibling: None,
1120 first_child: None,
1121 last_child: None,
1122 },
1123 Node {
1124 parent: Some(NodeId::new(0)),
1125 previous_sibling: Some(NodeId::new(2)),
1126 next_sibling: None,
1127 first_child: None,
1128 last_child: None,
1129 },
1130 ]},
1131 node_data: NodeDataContainer { internal: vec![
1132 NodeData::body(),
1133 NodeData::div().with_classes(vec![Static("class1")]),
1134 NodeData::div().with_classes(vec![Static("class1")]),
1135 NodeData::div().with_ids(vec![Static("child_2")]),
1136 NodeData::div().with_classes(vec![Static("class1")]),
1137 ]},
1138 },
1139 };
1140
1141 let got_dom = convert_dom_into_compact_dom(dom);
1142 if got_dom != expected_dom {
1143 panic!("{}", format!("expected compact dom: ----\r\n{:#?}\r\n\r\ngot compact dom: ----\r\n{:#?}\r\n", expected_dom, got_dom));
1144 }
1145}
1146
1147pub struct CompactDom {
1149 pub arena: Arena<NodeData>,
1150 pub root: NodeId,
1151}
1152
1153impl From<Dom> for CompactDom {
1154 fn from(dom: Dom) -> Self {
1155 convert_dom_into_compact_dom(dom)
1156 }
1157}
1158
1159impl CompactDom {
1160 #[inline(always)]
1162 pub fn len(&self) -> usize {
1163 self.arena.len()
1164 }
1165}
1166
1167impl Clone for CompactDom {
1168 fn clone(&self) -> Self {
1169 CompactDom {
1170 arena: self.arena.clone(),
1171 root: self.root,
1172 }
1173 }
1174}
1175
1176impl PartialEq for CompactDom {
1177 fn eq(&self, rhs: &Self) -> bool {
1178 self.arena == rhs.arena &&
1179 self.root == rhs.root
1180 }
1181}
1182
1183impl Eq for CompactDom { }
1184
1185impl fmt::Debug for CompactDom {
1186 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1187 write!(f, "CompactDom {{ arena: {:?}, root: {:?} }}", self.arena, self.root)
1188 }
1189}
1190
1191impl Dom {
1192
1193 #[inline]
1196 pub const fn new(node_type: NodeType) -> Self {
1197 Self {
1198 root: NodeData::new(node_type),
1199 children: Vec::new(),
1200 estimated_total_children: 0,
1201 }
1202 }
1203
1204 #[inline]
1206 pub fn with_capacity(node_type: NodeType, cap: usize) -> Self {
1207 Self {
1208 root: NodeData::new(node_type),
1209 children: Vec::with_capacity(cap),
1210 estimated_total_children: 0,
1211 }
1212 }
1213
1214 #[inline(always)]
1216 pub const fn div() -> Self {
1217 Self::new(NodeType::Div)
1218 }
1219
1220 #[inline(always)]
1222 pub const fn body() -> Self {
1223 Self::new(NodeType::Body)
1224 }
1225
1226 #[inline(always)]
1228 pub fn label<S: Into<DomString>>(value: S) -> Self {
1229 Self::new(NodeType::Label(value.into()))
1230 }
1231
1232 #[inline(always)]
1234 pub const fn text_id(text_id: TextId) -> Self {
1235 Self::new(NodeType::Text(text_id))
1236 }
1237
1238 #[inline(always)]
1240 pub const fn image(image: ImageId) -> Self {
1241 Self::new(NodeType::Image(image))
1242 }
1243
1244 #[inline(always)]
1246 #[cfg(feature = "opengl")]
1247 pub fn gl_texture<I: Into<RefAny>>(callback: GlCallbackType, ptr: I) -> Self {
1248 Self::new(NodeType::GlTexture((GlCallback(callback), ptr.into())))
1249 }
1250
1251 #[inline(always)]
1253 pub fn iframe<I: Into<RefAny>>(callback: IFrameCallbackType, ptr: I) -> Self {
1254 Self::new(NodeType::IFrame((IFrameCallback(callback), ptr.into())))
1255 }
1256
1257 #[inline]
1259 pub fn add_child(&mut self, child: Self) {
1260 self.estimated_total_children += child.estimated_total_children;
1261 self.estimated_total_children += 1;
1262 self.children.push(child);
1263 }
1264
1265 #[inline]
1267 pub fn with_id<S: Into<DomString>>(mut self, id: S) -> Self {
1268 self.add_id(id);
1269 self
1270 }
1271
1272 #[inline]
1274 pub fn with_class<S: Into<DomString>>(mut self, class: S) -> Self {
1275 self.add_class(class);
1276 self
1277 }
1278
1279 #[inline]
1281 pub fn with_callback<O: Into<EventFilter>>(mut self, on: O, callback: CallbackType, ptr: RefAny) -> Self {
1282 self.add_callback(on, callback, ptr);
1283 self
1284 }
1285
1286 #[inline]
1287 pub fn with_child(mut self, child: Self) -> Self {
1288 self.add_child(child);
1289 self
1290 }
1291
1292 #[inline]
1293 pub fn with_css_override<S: Into<DomString>>(mut self, id: S, property: CssProperty) -> Self {
1294 self.add_css_override(id, property);
1295 self
1296 }
1297
1298 #[inline]
1299 pub fn with_tab_index(mut self, tab_index: TabIndex) -> Self {
1300 self.set_tab_index(tab_index);
1301 self
1302 }
1303
1304 #[inline]
1305 pub fn is_draggable(mut self, draggable: bool) -> Self {
1306 self.set_draggable(draggable);
1307 self
1308 }
1309
1310 #[inline]
1311 pub fn add_id<S: Into<DomString>>(&mut self, id: S) {
1312 self.root.ids.push(id.into());
1313 }
1314
1315 #[inline]
1316 pub fn add_class<S: Into<DomString>>(&mut self, class: S) {
1317 self.root.classes.push(class.into());
1318 }
1319
1320 #[inline]
1321 pub fn add_callback<O: Into<EventFilter>>(&mut self, on: O, callback: CallbackType, ptr: RefAny) {
1322 self.root.callbacks.push((on.into(), (Callback(callback), ptr)));
1323 }
1324
1325 #[inline]
1326 pub fn add_css_override<S: Into<DomString>, P: Into<CssProperty>>(&mut self, override_id: S, property: P) {
1327 self.root.dynamic_css_overrides.push((override_id.into(), property.into()));
1328 }
1329
1330 #[inline]
1331 pub fn set_tab_index(&mut self, tab_index: TabIndex) {
1332 self.root.tab_index = Some(tab_index);
1333 }
1334
1335 #[inline]
1336 pub fn set_draggable(&mut self, draggable: bool) {
1337 self.root.is_draggable = draggable;
1338 }
1339
1340 pub fn get_html_string(&self) -> String {
1355 let mut output = String::new();
1356 get_html_string_inner(self, &mut output, 0);
1357 output.trim().to_string()
1358 }
1359}
1360
1361fn get_html_string_inner(dom: &Dom, output: &mut String, indent: usize) {
1362 let tabs = String::from(" ").repeat(indent);
1363
1364 let content = dom.root.node_type.get_text_content();
1365 let print_self_closing_tag = dom.children.is_empty() && content.is_none();
1366
1367 output.push_str("\r\n");
1368 output.push_str(&tabs);
1369 output.push_str(&dom.root.debug_print_start(print_self_closing_tag));
1370
1371 if let Some(content) = &content {
1372 output.push_str("\r\n");
1373 output.push_str(&tabs);
1374 output.push_str(&" ");
1375 output.push_str(content);
1376 }
1377
1378 if !print_self_closing_tag {
1379
1380 for c in &dom.children {
1381 get_html_string_inner(c, output, indent + 1);
1382 }
1383
1384 output.push_str("\r\n");
1385 output.push_str(&tabs);
1386 output.push_str(&dom.root.debug_print_end());
1387 }
1388}
1389
1390#[test]
1391fn test_dom_sibling_1() {
1392
1393 struct TestLayout;
1394
1395 let dom: Dom<TestLayout> =
1396 Dom::div()
1397 .with_child(
1398 Dom::div()
1399 .with_id("sibling-1")
1400 .with_child(Dom::div()
1401 .with_id("sibling-1-child-1")))
1402 .with_child(Dom::div()
1403 .with_id("sibling-2")
1404 .with_child(Dom::div()
1405 .with_id("sibling-2-child-1")));
1406
1407 let dom = convert_dom_into_compact_dom(dom);
1408
1409 let arena = &dom.arena;
1410
1411 assert_eq!(NodeId::new(0), dom.root);
1412
1413 assert_eq!(vec![DomString::Static("sibling-1")],
1414 arena.node_data[
1415 arena.node_hierarchy[dom.root]
1416 .first_child.expect("root has no first child")
1417 ].ids);
1418
1419 assert_eq!(vec![DomString::Static("sibling-2")],
1420 arena.node_data[
1421 arena.node_hierarchy[
1422 arena.node_hierarchy[dom.root]
1423 .first_child.expect("root has no first child")
1424 ].next_sibling.expect("root has no second sibling")
1425 ].ids);
1426
1427 assert_eq!(vec![DomString::Static("sibling-1-child-1")],
1428 arena.node_data[
1429 arena.node_hierarchy[
1430 arena.node_hierarchy[dom.root]
1431 .first_child.expect("root has no first child")
1432 ].first_child.expect("first child has no first child")
1433 ].ids);
1434
1435 assert_eq!(vec![DomString::Static("sibling-2-child-1")],
1436 arena.node_data[
1437 arena.node_hierarchy[
1438 arena.node_hierarchy[
1439 arena.node_hierarchy[dom.root]
1440 .first_child.expect("root has no first child")
1441 ].next_sibling.expect("first child has no second sibling")
1442 ].first_child.expect("second sibling has no first child")
1443 ].ids);
1444}
1445
1446#[test]
1447fn test_dom_from_iter_1() {
1448
1449 use crate::id_tree::Node;
1450
1451 struct TestLayout;
1452
1453 let dom: Dom<TestLayout> = (0..5).map(|e| NodeData::new(NodeType::Label(format!("{}", e + 1).into()))).collect();
1454 let dom = convert_dom_into_compact_dom(dom);
1455
1456 let arena = &dom.arena;
1457
1458 assert_eq!(arena.len(), 6);
1468
1469 assert_eq!(arena.node_hierarchy.get(NodeId::new(0)), Some(&Node {
1471 parent: None,
1472 previous_sibling: None,
1473 next_sibling: None,
1474 first_child: Some(NodeId::new(1)),
1475 last_child: Some(NodeId::new(5)),
1476 }));
1477 assert_eq!(arena.node_data.get(NodeId::new(0)), Some(&NodeData::new(NodeType::Div)));
1478
1479 assert_eq!(arena.node_hierarchy.get(NodeId::new(arena.node_hierarchy.len() - 1)), Some(&Node {
1480 parent: Some(NodeId::new(0)),
1481 previous_sibling: Some(NodeId::new(4)),
1482 next_sibling: None,
1483 first_child: None,
1484 last_child: None,
1485 }));
1486
1487 assert_eq!(arena.node_data.get(NodeId::new(arena.node_data.len() - 1)), Some(&NodeData {
1488 node_type: NodeType::Label(DomString::Heap(String::from("5"))),
1489 .. Default::default()
1490 }));
1491}
1492
1493#[test]
1495fn test_zero_size_dom() {
1496
1497 struct TestLayout;
1498
1499 let null_dom: Dom<TestLayout> = (0..0).map(|_| NodeData::default()).collect();
1500 let null_dom = convert_dom_into_compact_dom(null_dom);
1501
1502 assert!(null_dom.arena.len() == 1);
1503}