1
1
use egui:: { Response , Ui } ;
2
2
use itertools:: Itertools ;
3
3
use re_data_ui:: item_ui:: guess_instance_path_icon;
4
+ use smallvec:: SmallVec ;
4
5
5
6
use re_entity_db:: InstancePath ;
6
7
use re_log_types:: EntityPath ;
@@ -54,14 +55,16 @@ impl<'a> DataResultNodeOrPath<'a> {
54
55
55
56
impl Viewport < ' _ , ' _ > {
56
57
/// Show the blueprint panel tree view.
57
- pub fn tree_ui ( & self , ctx : & ViewerContext < ' _ > , ui : & mut egui:: Ui ) {
58
+ pub fn tree_ui ( & mut self , ctx : & ViewerContext < ' _ > , ui : & mut egui:: Ui ) {
58
59
re_tracing:: profile_function!( ) ;
59
60
60
61
egui:: ScrollArea :: both ( )
61
62
. id_source ( "blueprint_tree_scroll_area" )
62
63
. auto_shrink ( [ true , false ] )
63
64
. show ( ui, |ui| {
64
65
ctx. re_ui . panel_content ( ui, |_, ui| {
66
+ self . state . blueprint_tree_scroll_to_item = self . handle_focused_item ( ctx, ui) ;
67
+
65
68
self . root_container_tree_ui ( ctx, ui) ;
66
69
67
70
let empty_space_response =
@@ -81,6 +84,131 @@ impl Viewport<'_, '_> {
81
84
} ) ;
82
85
}
83
86
87
+ /// Expend all required items and compute which item we should scroll to.
88
+ fn handle_focused_item ( & self , ctx : & ViewerContext < ' _ > , ui : & egui:: Ui ) -> Option < Item > {
89
+ let focused_item = ctx. focused_item . as_ref ( ) ?;
90
+ match focused_item {
91
+ Item :: Container ( container_id) => {
92
+ self . expand_all_contents_until ( ui. ctx ( ) , & Contents :: Container ( * container_id) ) ;
93
+ Some ( focused_item. clone ( ) )
94
+ }
95
+ Item :: SpaceView ( space_view_id) => {
96
+ self . expand_all_contents_until ( ui. ctx ( ) , & Contents :: SpaceView ( * space_view_id) ) ;
97
+ ctx. focused_item . clone ( )
98
+ }
99
+ Item :: DataResult ( space_view_id, instance_path) => {
100
+ self . expand_all_contents_until ( ui. ctx ( ) , & Contents :: SpaceView ( * space_view_id) ) ;
101
+ self . expand_all_data_results_until (
102
+ ctx,
103
+ ui. ctx ( ) ,
104
+ space_view_id,
105
+ & instance_path. entity_path ,
106
+ ) ;
107
+
108
+ ctx. focused_item . clone ( )
109
+ }
110
+ Item :: InstancePath ( instance_path) => {
111
+ let space_view_ids =
112
+ self . list_space_views_with_entity ( ctx, & instance_path. entity_path ) ;
113
+
114
+ // focus on the first matching data result
115
+ let res = space_view_ids
116
+ . first ( )
117
+ . map ( |id| Item :: DataResult ( * id, instance_path. clone ( ) ) ) ;
118
+
119
+ for space_view_id in space_view_ids {
120
+ self . expand_all_contents_until ( ui. ctx ( ) , & Contents :: SpaceView ( space_view_id) ) ;
121
+ self . expand_all_data_results_until (
122
+ ctx,
123
+ ui. ctx ( ) ,
124
+ & space_view_id,
125
+ & instance_path. entity_path ,
126
+ ) ;
127
+ }
128
+
129
+ res
130
+ }
131
+
132
+ Item :: StoreId ( _) | Item :: ComponentPath ( _) => None ,
133
+ }
134
+ }
135
+
136
+ /// Expand all containers until reaching the provided content.
137
+ fn expand_all_contents_until ( & self , egui_ctx : & egui:: Context , focused_contents : & Contents ) {
138
+ //TODO(ab): this could look nicer if `Contents` was declared in re_view_context :)
139
+ let expend_contents = |contents : & Contents | match contents {
140
+ Contents :: Container ( container_id) => CollapseScope :: BlueprintTree
141
+ . container ( * container_id)
142
+ . set_open ( egui_ctx, true ) ,
143
+ Contents :: SpaceView ( space_view_id) => CollapseScope :: BlueprintTree
144
+ . space_view ( * space_view_id)
145
+ . set_open ( egui_ctx, true ) ,
146
+ } ;
147
+
148
+ self . blueprint . visit_contents ( & mut |contents, hierarchy| {
149
+ if contents == focused_contents {
150
+ expend_contents ( contents) ;
151
+ for parent in hierarchy {
152
+ expend_contents ( & Contents :: Container ( * parent) ) ;
153
+ }
154
+ }
155
+ } ) ;
156
+ }
157
+
158
+ /// List all space views that have the provided entity as data result.
159
+ #[ inline]
160
+ fn list_space_views_with_entity (
161
+ & self ,
162
+ ctx : & ViewerContext < ' _ > ,
163
+ entity_path : & EntityPath ,
164
+ ) -> SmallVec < [ SpaceViewId ; 4 ] > {
165
+ let mut space_view_ids = SmallVec :: new ( ) ;
166
+ self . blueprint . visit_contents ( & mut |contents, _| {
167
+ if let Contents :: SpaceView ( space_view_id) = contents {
168
+ let result_tree = & ctx. lookup_query_result ( * space_view_id) . tree ;
169
+ if result_tree. lookup_node_by_path ( entity_path) . is_some ( ) {
170
+ space_view_ids. push ( * space_view_id) ;
171
+ }
172
+ }
173
+ } ) ;
174
+ space_view_ids
175
+ }
176
+
177
+ /// Expand data results of the provided space view all the way to the provided entity.
178
+ #[ allow( clippy:: unused_self) ]
179
+ fn expand_all_data_results_until (
180
+ & self ,
181
+ ctx : & ViewerContext < ' _ > ,
182
+ egui_ctx : & egui:: Context ,
183
+ space_view_id : & SpaceViewId ,
184
+ entity_path : & EntityPath ,
185
+ ) {
186
+ let result_tree = & ctx. lookup_query_result ( * space_view_id) . tree ;
187
+ if result_tree. lookup_node_by_path ( entity_path) . is_some ( ) {
188
+ if let Some ( root_node) = result_tree. root_node ( ) {
189
+ EntityPath :: incremental_walk ( Some ( & root_node. data_result . entity_path ) , entity_path)
190
+ . chain ( std:: iter:: once ( root_node. data_result . entity_path . clone ( ) ) )
191
+ . for_each ( |entity_path| {
192
+ CollapseScope :: BlueprintTree
193
+ . data_result ( * space_view_id, entity_path)
194
+ . set_open ( egui_ctx, true ) ;
195
+ } ) ;
196
+ }
197
+ }
198
+ }
199
+
200
+ /// Check if the provided item should be scrolled to.
201
+ fn scroll_to_me_if_needed ( & self , ui : & egui:: Ui , item : & Item , response : & egui:: Response ) {
202
+ if Some ( item) == self . state . blueprint_tree_scroll_to_item . as_ref ( ) {
203
+ // Scroll only if the entity isn't already visible. This is important because that's what
204
+ // happens when double-clicking an entity _in the blueprint tree_. In such case, it would be
205
+ // annoying to induce a scroll motion.
206
+ if !ui. clip_rect ( ) . contains_rect ( response. rect ) {
207
+ response. scroll_to_me ( Some ( egui:: Align :: Center ) ) ;
208
+ }
209
+ }
210
+ }
211
+
84
212
/// If a group or spaceview has a total of this number of elements, show its subtree by default?
85
213
fn default_open_for_data_result ( group : & DataResultNode ) -> bool {
86
214
let num_children = group. children . len ( ) ;
@@ -145,6 +273,7 @@ impl Viewport<'_, '_> {
145
273
& item_response,
146
274
SelectionUpdateBehavior :: UseSelection ,
147
275
) ;
276
+ self . scroll_to_me_if_needed ( ui, & item, & item_response) ;
148
277
ctx. select_hovered_on_click ( & item_response, item) ;
149
278
150
279
self . handle_root_container_drag_and_drop_interaction (
@@ -218,6 +347,7 @@ impl Viewport<'_, '_> {
218
347
& response,
219
348
SelectionUpdateBehavior :: UseSelection ,
220
349
) ;
350
+ self . scroll_to_me_if_needed ( ui, & item, & response) ;
221
351
ctx. select_hovered_on_click ( & response, item) ;
222
352
223
353
self . blueprint
@@ -304,15 +434,15 @@ impl Viewport<'_, '_> {
304
434
) ;
305
435
306
436
// Show 'projections' if there's any items that weren't part of the tree under origin but are directly included.
307
- // The later is important since `+ image/camera/**` necessarily has `image` and `image/camera` in the data result tree.
437
+ // The latter is important since `+ image/camera/**` necessarily has `image` and `image/camera` in the data result tree.
308
438
let mut projections = Vec :: new ( ) ;
309
439
result_tree. visit ( & mut |node| {
310
440
if node
311
441
. data_result
312
442
. entity_path
313
443
. starts_with ( & space_view. space_origin )
314
444
{
315
- false // If its under the origin, we're not interested, stop recursing.
445
+ false // If it's under the origin, we're not interested, stop recursing.
316
446
} else if node. data_result . tree_prefix_only {
317
447
true // Keep recursing until we find a projection.
318
448
} else {
@@ -351,6 +481,7 @@ impl Viewport<'_, '_> {
351
481
& response,
352
482
SelectionUpdateBehavior :: UseSelection ,
353
483
) ;
484
+ self . scroll_to_me_if_needed ( ui, & item, & response) ;
354
485
ctx. select_hovered_on_click ( & response, item) ;
355
486
356
487
let content = Contents :: SpaceView ( * space_view_id) ;
@@ -474,8 +605,7 @@ impl Viewport<'_, '_> {
474
605
let response = list_item
475
606
. show_collapsing (
476
607
ui,
477
- CollapseScope :: BlueprintTree
478
- . data_result ( space_view. id , node. data_result . entity_path . clone ( ) ) ,
608
+ CollapseScope :: BlueprintTree . data_result ( space_view. id , entity_path. clone ( ) ) ,
479
609
default_open,
480
610
|_, ui| {
481
611
for child in node. children . iter ( ) . sorted_by_key ( |c| {
@@ -526,6 +656,7 @@ impl Viewport<'_, '_> {
526
656
& response,
527
657
SelectionUpdateBehavior :: UseSelection ,
528
658
) ;
659
+ self . scroll_to_me_if_needed ( ui, & item, & response) ;
529
660
ctx. select_hovered_on_click ( & response, item) ;
530
661
}
531
662
0 commit comments