OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 import 'dart:async'; | 5 import 'dart:async'; |
6 import 'dart:html'; | 6 import 'dart:html'; |
| 7 import 'dart:collection'; |
7 import 'dart:math' as Math; | 8 import 'dart:math' as Math; |
8 import 'package:observatory/src/elements/containers/virtual_collection.dart'; | 9 import 'package:observatory/src/elements/containers/virtual_collection.dart'; |
9 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; | 10 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
10 import 'package:observatory/src/elements/helpers/tag.dart'; | 11 import 'package:observatory/src/elements/helpers/tag.dart'; |
11 | 12 |
12 typedef HtmlElement VirtualTreeCreateCallback( | 13 typedef HtmlElement VirtualTreeCreateCallback( |
13 toggle({bool autoToggleSingleChildNodes, bool autoToggleWholeTree})); | 14 toggle({bool autoToggleSingleChildNodes, bool autoToggleWholeTree})); |
14 typedef void VirtualTreeUpdateCallback(HtmlElement el, dynamic item, int depth); | 15 typedef void VirtualTreeUpdateCallback(HtmlElement el, dynamic item, int depth); |
15 typedef Iterable<dynamic> VritualTreeGetChildrenCallback(dynamic value); | 16 typedef Iterable<dynamic> VritualTreeGetChildrenCallback(dynamic value); |
16 | 17 |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 | 83 |
83 bool isExpanded(item) { | 84 bool isExpanded(item) { |
84 return _expanded.contains(item); | 85 return _expanded.contains(item); |
85 } | 86 } |
86 | 87 |
87 void expand(item, | 88 void expand(item, |
88 {bool autoExpandSingleChildNodes: false, | 89 {bool autoExpandSingleChildNodes: false, |
89 bool autoExpandWholeTree: false}) { | 90 bool autoExpandWholeTree: false}) { |
90 if (_expanded.add(item)) _r.dirty(); | 91 if (_expanded.add(item)) _r.dirty(); |
91 if (autoExpandWholeTree) { | 92 if (autoExpandWholeTree) { |
92 for (final child in _children(item)) { | 93 // The tree is potentially very deep, simple recursion can produce a |
93 expand(child, autoExpandWholeTree: true); | 94 // Stack Overflow |
| 95 Queue pendingNodes = new Queue(); |
| 96 pendingNodes.addAll(_children(item)); |
| 97 while (pendingNodes.isNotEmpty) { |
| 98 final item = pendingNodes.removeFirst(); |
| 99 if (_expanded.add(item)) _r.dirty(); |
| 100 pendingNodes.addAll(_children(item)); |
94 } | 101 } |
95 } else if (autoExpandSingleChildNodes) { | 102 } else if (autoExpandSingleChildNodes) { |
96 var children = _children(item); | 103 var children = _children(item); |
97 while (children.length == 1) { | 104 while (children.length == 1) { |
98 _expanded.add(children.first); | 105 _expanded.add(children.first); |
99 children = _children(children.first); | 106 children = _children(children.first); |
100 } | 107 } |
101 } | 108 } |
102 } | 109 } |
103 | 110 |
104 void collapse(item, | 111 void collapse(item, |
105 {bool autoCollapseSingleChildNodes: false, | 112 {bool autoCollapseSingleChildNodes: false, |
106 bool autoCollapseWholeTree: false}) { | 113 bool autoCollapseWholeTree: false}) { |
107 if (_expanded.remove(item)) _r.dirty(); | 114 if (_expanded.remove(item)) _r.dirty(); |
108 if (autoCollapseWholeTree) { | 115 if (autoCollapseWholeTree) { |
109 for (final child in _children(item)) { | 116 // The tree is potentially very deep, simple recursion can produce a |
110 collapse(child, autoCollapseWholeTree: true); | 117 // Stack Overflow |
| 118 Queue pendingNodes = new Queue(); |
| 119 pendingNodes.addAll(_children(item)); |
| 120 while (pendingNodes.isNotEmpty) { |
| 121 final item = pendingNodes.removeFirst(); |
| 122 if (_expanded.remove(item)) _r.dirty(); |
| 123 pendingNodes.addAll(_children(item)); |
111 } | 124 } |
112 } else if (autoCollapseSingleChildNodes) { | 125 } else if (autoCollapseSingleChildNodes) { |
113 var children = _children(item); | 126 var children = _children(item); |
114 while (children.length == 1) { | 127 while (children.length == 1) { |
115 _expanded.remove(children.first); | 128 _expanded.remove(children.first); |
116 children = _children(children.first); | 129 children = _children(children.first); |
117 } | 130 } |
118 } | 131 } |
119 } | 132 } |
120 | 133 |
121 @override | 134 @override |
122 attached() { | 135 attached() { |
123 super.attached(); | 136 super.attached(); |
124 _r.enable(); | 137 _r.enable(); |
125 } | 138 } |
126 | 139 |
127 @override | 140 @override |
128 detached() { | 141 detached() { |
129 super.detached(); | 142 super.detached(); |
130 _r.disable(notify: true); | 143 _r.disable(notify: true); |
131 children = const []; | 144 children = const []; |
132 } | 145 } |
133 | 146 |
134 VirtualCollectionElement _collection; | 147 VirtualCollectionElement _collection; |
135 | 148 |
136 void render() { | 149 void render() { |
137 if (children.length == 0) { | 150 if (children.length == 0) { |
138 children = [_collection]; | 151 children = [_collection]; |
139 } | 152 } |
140 Iterable _toList(item) { | 153 |
141 if (isExpanded(item)) { | 154 final items = []; |
142 Iterable children = _children(item); | 155 final depths = new List.filled(_items.length, 0, growable: true); |
143 if (children.isNotEmpty) { | 156 |
144 return [item]..addAll(children.expand(_toList)); | 157 { |
| 158 final toDo = new Queue(); |
| 159 |
| 160 toDo.addAll(_items); |
| 161 while (toDo.isNotEmpty) { |
| 162 final item = toDo.removeFirst(); |
| 163 |
| 164 items.add(item); |
| 165 if (isExpanded(item)) { |
| 166 final children = _children(item); |
| 167 children |
| 168 .toList(growable: false) |
| 169 .reversed |
| 170 .forEach((c) => toDo.addFirst(c)); |
| 171 final depth = depths[items.length - 1]; |
| 172 depths.insertAll( |
| 173 items.length, new List.filled(children.length, depth + 1)); |
145 } | 174 } |
146 } | 175 } |
147 return [item]; | |
148 } | 176 } |
149 | 177 |
150 _collection.items = _items.expand(_toList); | 178 _depths = depths; |
151 var depth = 0; | 179 _collection.items = items; |
152 Iterable _toDepth(item) { | |
153 if (isExpanded(item)) { | |
154 Iterable children = _children(item); | |
155 if (children.isNotEmpty) { | |
156 depth++; | |
157 return children.expand(_toDepth).toList()..insert(0, --depth); | |
158 } | |
159 } | |
160 return [depth]; | |
161 } | |
162 | |
163 _depths = _items.expand(_toDepth).toList(); | |
164 } | 180 } |
165 } | 181 } |
OLD | NEW |