1     type TreeDataItem @abstract @mut =
2         var obs name = ""
3         var obs is_open = false
4         val items : obs/List<TreeDataItem>
5     
6     type TreeItem =
7         inherit Wrapper
8     
9         var obs foreground = Vector3.new
10        var obs background = Vector3.new
11        var obs is_selected = false
12        var obs offset : u32 = 0
13        val data : TreeDataItem
14    
15    type Tree @mut =
16        inherit Wrapper
17    
18        var obs foreground = Colors.tree_foreground
19        var obs selected_foreground = Colors.tree_selected_foreground
20        var obs selected_background = Colors.tree_selected_background
21        var obs unfocused_background = Colors.tree_unfocused_background
22        var obs selected_item : Option<TreeDataItem> = None
23        var obs selected_tree_item : Option<TreeItem> @[mut private] = None
24    
25        val data : TreeDataItem
26        let data_tree_items = Map<TreeDataItem, TreeItem>.new
27        val on_open @[mut private] = Stream<TreeDataItem>.new
28    
29        let stack = Stack.new
30        content = stack
31    
32        is_focusable = true
33    
34        let hide_selection (tree_item : TreeItem) =
35            tree_item.foreground = foreground
36                      is_selected = false
37    
38        def select (tree_item : TreeItem) =
39            let maybe_prev_item = selected_tree_item
40            if maybe_prev_item <> Some tree_item then
41                selected_item = tree_item.data
42                selected_tree_item = Some tree_item
43    
44                let is_focused_or_not_tree = is_focused
45                let set_foreground = is_focused_or_not_tree
46                if is_focused then
47                    tree_item.foreground = selected_foreground
48    
49                tree_item.is_selected = true
50                          background = if is_focused
51                                       then selected_background
52                                       else unfocused_background
53    
54                if maybe_prev_item ? Some prev_item then
55                    hide_selection prev_item
56    
57    type TreeItem
58        val margin = Margin.new
59        let text_box = TextBox.new
60        let stack = Stack.new
61        let rectangle = Rectangle.new
62    
63        is_selected@atom.bind { rectangle.is_visible = _ }
64        |> push_token
65    
66        data.is_open@atom.bind { stack.is_visible = _ }
67        |> push_token
68    
69        is_clickable = true
70    
71        let splitter = Splitter
72            ratio = 0
73            is_vertical = true
74            Aligner
75                rectangle
76                    color@obs = background@obs
77                margin
78                    left@obs = offset@obs
79                    align_h = AlignH/Left
80                    is_clickable = true
81                    TextBlock
82                        text@obs = data.name@obs
83                        color@obs = foreground@obs
84                text_box
85                    is_visible = false
86            stack
87    
88        content = splitter
89    
90        def edit =
91            text_box.text = data.name
92            margin.is_visible = false
93            text_box.is_visible = true
94            text_box
95    
96        margin.subscribe { _, event -> if event.is_mouse_press then
97            let tree = find_parent self { _ is Tree } as Tree
98            tree.selected_item = data
99            tree.on_open.push data }
100   
101       subscribe { _, event -> if event.is_mouse_press then
102           let tree = find_parent self { _ is Tree } as Tree
103           tree.select self }
104   
105   let init_items (tree : Tree) (maybe_tree_item : Option<TreeItem>) : Unit =
106       let (stack, items, offset) = case maybe_tree_item of
107           None -> (tree.stack, tree.data.items, 0)
108           Some tree_item -> (tree_item.stack,
109                              tree_item.data.items,
110                              tree_item.offset + 30)
111   
112       let token = items
113           |> Obs.map { item ->
114               let tree_item = TreeItem
115                   data = item
116                   foreground = tree.foreground
117                   =offset
118   
119               init_items tree tree_item
120   
121               tree_item }
122   
123           |> bind { index tree_item ->
124                         let data_item = tree_item.data
125                         tree.data_tree_items.add data_item tree_item
126                         stack.items.add index tree_item }
127   
128                   { index tree_item ->
129                         stack.items.remove_at index
130                         let data_item = tree_item.data
131                         tree.data_tree_items.remove data_item
132                         tree_item.discard }
133   
134       case maybe_tree_item of
135           None -> tree.push_token token
136           Some tree_item -> tree_item.push_token token
137   
138   type Tree
139       init_items self None
140   
141       selected_item@atom.bind { maybe_item -> case maybe_item of
142           None -> if selected_tree_item ? Some tree_item then
143               selected_tree_item = None
144               selected_item = None
145               hide_selection tree_item
146   
147           Some item ->
148               let tree_item = data_tree_items[item]
149               select tree_item } |> push_token
150   
151       subscribe { _, event -> if event ? FlagEvent/IsFocused x then
152           if selected_tree_item ? Some selected_item then
153               let (f, b) = if x then
154                   (selected_foreground, selected_background)
155               else
156                   (foreground, unfocused_background)
157   
158               selected_item.foreground = f
159                             background = b }
160