-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtreetraversal.py
More file actions
132 lines (100 loc) · 5.43 KB
/
treetraversal.py
File metadata and controls
132 lines (100 loc) · 5.43 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from __future__ import annotations
import rx
from rx.core.typing import Disposable
from typing import Any, Dict, List, Optional
from rx import operators as ops
from services import WidgetRegistrationService
import widgetnode
from widgettypes import WidgetTypes
class ShadowNode:
def __init__(self, id: int, renderable: widgetnode.Renderable):
self.id = id
self.renderable = renderable
self.current_props = {}
self.children: List[ShadowNode] = []
self.props_change_subscription: Optional[Disposable] = None
self.children_change_subscription: Optional[Disposable] = None
def to_dict(self):
return {
"id": self.id,
"current_props": self.current_props,
"children": [child.to_dict() for child in self.children]
}
def get_linkable_children(self):
"""Returns a list of child nodes that can be linked in the widget tree."""
out: List[ShadowNode] = []
for child in self.children:
if not child:
continue
if not child.renderable:
continue
if isinstance(child.renderable, widgetnode.WidgetNode):
out.append(child)
elif len(child.children) > 0:
out.extend(child.get_linkable_children())
return out
class ShadowNodeTraversalHelper:
def __init__(self, widget_registration_service: WidgetRegistrationService):
self.widget_registration_service = widget_registration_service
def are_props_equal(self, props1: Dict[str, Any], props2: Dict[str, Any]) -> bool:
return props1 == props2
def subscribe_to_props_helper(self, shadow_node: ShadowNode):
if shadow_node.props_change_subscription:
shadow_node.props_change_subscription.dispose()
if isinstance(shadow_node.renderable, widgetnode.BaseComponent):
component = shadow_node.renderable
shadow_node.props_change_subscription = component.props.pipe(
ops.skip(1)
).subscribe(lambda new_props: self.handle_component_props_change(shadow_node, component, new_props))
elif isinstance(shadow_node.renderable, widgetnode.WidgetNode):
shadow_node.props_change_subscription = shadow_node.renderable.props.pipe(
ops.skip(1)
).subscribe(lambda new_props: self.handle_widget_node_props_change(shadow_node, shadow_node.renderable, new_props))
def handle_widget_node(self, widget: widgetnode.RawChildlessWidgetNodeWithId):
if widget.type == WidgetTypes.Button:
on_click = widget.props["on_click"]
if on_click:
self.widget_registration_service.register_on_click(widget.id, on_click)
pass
else:
print("Button widget must have on_click prop")
def handle_component_props_change(self, shadow_node: ShadowNode, component: widgetnode.BaseComponent, new_props):
if self.are_props_equal(shadow_node.current_props, new_props):
return
shadow_child = component.render()
shadow_node.children = [self.traverse_tree(shadow_child)]
shadow_node.current_props = new_props
linkable_children = shadow_node.get_linkable_children()
self.widget_registration_service.link_children(shadow_node.id, [child.id for child in linkable_children])
def handle_widget_node_props_change(self, shadow_node: ShadowNode, widget_node: widgetnode.WidgetNode, new_props):
self.widget_registration_service.create_widget(
widgetnode.create_raw_childless_widget_node_with_id(shadow_node.id, widget_node)
)
shadow_children = [self.traverse_tree(child) for child in widget_node.children]
shadow_node.children = shadow_children
shadow_node.current_props = new_props
self.widget_registration_service.link_children(shadow_node.id, [child.id for child in shadow_node.children])
def traverse_tree(self, renderable: widgetnode.Renderable) -> ShadowNode:
if isinstance(renderable, widgetnode.BaseComponent):
shadow_child = self.traverse_tree(renderable.render())
id = self.widget_registration_service.get_next_component_id()
shadow_node = ShadowNode(id, renderable)
shadow_node.children = [shadow_child]
shadow_node.current_props = renderable.props.value
self.subscribe_to_props_helper(shadow_node)
return shadow_node
elif isinstance(renderable, widgetnode.WidgetNode):
id = self.widget_registration_service.get_next_widget_id()
raw_node = widgetnode.create_raw_childless_widget_node_with_id(id, renderable)
self.handle_widget_node(raw_node)
self.widget_registration_service.create_widget(raw_node)
shadow_node = ShadowNode(id, renderable)
shadow_node.children = [self.traverse_tree(child) for child in renderable.children.value]
shadow_node.current_props = renderable.props.value
linkable_children = shadow_node.get_linkable_children()
if len(linkable_children) > 0:
self.widget_registration_service.link_children(id, [child.id for child in linkable_children])
self.subscribe_to_props_helper(shadow_node)
return shadow_node
else:
raise Exception("Unrecognised renderable")