1
- use std:: collections:: HashSet ;
2
-
3
1
use egui:: {
4
2
Id , Ui ,
5
3
collapsing_header:: { CollapsingState , paint_default_icon} ,
@@ -20,6 +18,8 @@ use crate::{
20
18
} ;
21
19
22
20
pub ( crate ) struct JsonTreeNode < ' a , ' b , T : ToJsonTreeValue > {
21
+ /// The Id of the entire tree that this node is part of.
22
+ tree_id : Id ,
23
23
value : & ' a T ,
24
24
parent : Option < JsonPointerSegment < ' a > > ,
25
25
make_persistent_id : & ' b dyn Fn ( & [ JsonPointerSegment ] ) -> Id ,
@@ -36,36 +36,29 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
36
36
let style = tree. config . style . unwrap_or_default ( ) ;
37
37
let default_expand = tree. config . default_expand . unwrap_or_default ( ) ;
38
38
39
- let mut reset_path_ids = HashSet :: new ( ) ;
40
-
41
39
let ( inner_default_expand, search_term) = match default_expand {
42
40
DefaultExpand :: All => ( InnerDefaultExpand :: All , None ) ,
43
41
DefaultExpand :: None => ( InnerDefaultExpand :: None , None ) ,
44
42
DefaultExpand :: ToLevel ( l) => ( InnerDefaultExpand :: ToLevel ( l) , None ) ,
43
+ DefaultExpand :: SearchResults ( "" ) => ( InnerDefaultExpand :: None , None ) ,
44
+ DefaultExpand :: SearchResultsOrAll ( "" ) => ( InnerDefaultExpand :: All , None ) ,
45
45
DefaultExpand :: SearchResults ( search_str)
46
46
| DefaultExpand :: SearchResultsOrAll ( search_str) => {
47
- // Important: when searching for anything (even an empty string), we must always traverse the entire JSON value to
48
- // capture all reset_path_ids so all the open/closed states can be reset to respect the new search term when it changes.
49
47
let search_term = SearchTerm :: new ( search_str) ;
50
48
let search_match_path_ids = search_term. find_matching_paths_in (
51
49
tree. value ,
52
50
style. abbreviate_root ,
53
51
& make_persistent_id,
54
- & mut reset_path_ids,
55
52
) ;
56
- let inner_expand =
57
- if matches ! ( default_expand, DefaultExpand :: SearchResultsOrAll ( "" ) ) {
58
- InnerDefaultExpand :: All
59
- } else {
60
- InnerDefaultExpand :: Paths ( search_match_path_ids)
61
- } ;
62
- ( inner_expand, Some ( search_term) )
53
+ (
54
+ InnerDefaultExpand :: Paths ( search_match_path_ids) ,
55
+ Some ( search_term) ,
56
+ )
63
57
}
64
58
} ;
65
59
66
- let mut renderer = tree. config . renderer ;
67
-
68
60
let node = JsonTreeNode {
61
+ tree_id,
69
62
value : tree. value ,
70
63
parent : None ,
71
64
make_persistent_id : & make_persistent_id,
@@ -76,47 +69,39 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
76
69
} ,
77
70
} ;
78
71
79
- // Wrap in a vertical layout in case this tree is placed directly in a horizontal layout,
80
- // which does not allow indent layouts as direct children.
81
- ui. vertical ( |ui| {
82
- // Centres the collapsing header icon.
83
- ui. spacing_mut ( ) . interact_size . y = node. config . style . resolve_font_id ( ui) . size ;
84
-
85
- node. show_impl ( ui, & mut vec ! [ ] , & mut reset_path_ids, & mut renderer) ;
86
- } ) ;
87
-
88
- let should_reset_expanded = if tree. config . auto_reset_expanded {
89
- let default_expand_hash_id = ResetExpandedHashId ( egui:: util:: hash ( default_expand) ) ;
90
- let default_expand_changed = ui. ctx ( ) . data_mut ( |d| {
72
+ let should_reset_expanded = ui. ctx ( ) . data_mut ( |d| {
73
+ if tree. config . auto_reset_expanded {
74
+ let default_expand_hash_id = ResetExpandedHashId ( egui:: util:: hash ( default_expand) ) ;
91
75
let default_expand_changed =
92
76
d. get_temp :: < ResetExpandedHashId > ( tree. id ) != Some ( default_expand_hash_id) ;
93
77
if default_expand_changed {
94
78
d. insert_temp ( tree. id , default_expand_hash_id) ;
79
+ d. insert_temp ( tree. id , ShouldResetExpanded ) ;
95
80
}
96
- default_expand_changed
97
- } ) ;
98
- default_expand_changed
99
- } else {
100
- false
101
- } ;
81
+ }
82
+ d. remove_temp :: < ShouldResetExpanded > ( tree_id) . is_some ( )
83
+ } ) ;
102
84
103
- let response = JsonTreeResponse {
104
- collapsing_state_ids : reset_path_ids,
105
- } ;
85
+ let mut renderer = tree. config . renderer ;
106
86
107
- if should_reset_expanded {
108
- response. reset_expanded ( ui) ;
109
- }
87
+ // Wrap in a vertical layout in case this tree is placed directly in a horizontal layout,
88
+ // which does not allow indent layouts as direct children.
89
+ ui. vertical ( |ui| {
90
+ // Centres the collapsing header icon.
91
+ ui. spacing_mut ( ) . interact_size . y = node. config . style . resolve_font_id ( ui) . size ;
92
+
93
+ node. show_impl ( ui, & mut vec ! [ ] , & mut renderer, should_reset_expanded) ;
94
+ } ) ;
110
95
111
- response
96
+ JsonTreeResponse { tree_id }
112
97
}
113
98
114
99
fn show_impl (
115
100
self ,
116
101
ui : & mut Ui ,
117
102
path_segments : & ' b mut Vec < JsonPointerSegment < ' a > > ,
118
- reset_path_ids : & ' b mut HashSet < Id > ,
119
103
renderer : & ' b mut JsonTreeRenderer < ' a , T > ,
104
+ should_reset_expanded : bool ,
120
105
) {
121
106
match self . value . to_json_tree_value ( ) {
122
107
JsonTreeValue :: Base ( value, display_value, value_type) => {
@@ -168,10 +153,10 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
168
153
self . show_expandable (
169
154
ui,
170
155
path_segments,
171
- reset_path_ids,
172
156
renderer,
173
157
entries,
174
158
expandable_type,
159
+ should_reset_expanded,
175
160
) ;
176
161
}
177
162
} ;
@@ -181,10 +166,10 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
181
166
self ,
182
167
ui : & mut Ui ,
183
168
path_segments : & ' b mut Vec < JsonPointerSegment < ' a > > ,
184
- reset_path_ids : & ' b mut HashSet < Id > ,
185
169
renderer : & ' b mut JsonTreeRenderer < ' a , T > ,
186
170
entries : Vec < ( JsonPointerSegment < ' a > , & ' a T ) > ,
187
171
expandable_type : ExpandableType ,
172
+ should_reset_expanded : bool ,
188
173
) {
189
174
let JsonTreeNodeConfig {
190
175
inner_default_expand,
@@ -198,7 +183,6 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
198
183
} ;
199
184
200
185
let path_id = ( self . make_persistent_id ) ( path_segments) ;
201
- reset_path_ids. insert ( path_id) ;
202
186
203
187
let default_open = match & inner_default_expand {
204
188
InnerDefaultExpand :: All => true ,
@@ -212,6 +196,9 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
212
196
} ;
213
197
214
198
let mut state = CollapsingState :: load_with_default_open ( ui. ctx ( ) , path_id, default_open) ;
199
+ if should_reset_expanded {
200
+ state. set_open ( default_open) ;
201
+ }
215
202
let is_expanded = state. is_open ( ) ;
216
203
217
204
let header_res = ui. horizontal_wrapped ( |ui| {
@@ -417,13 +404,14 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
417
404
418
405
let mut add_nested_tree = |ui : & mut Ui | {
419
406
let nested_tree = JsonTreeNode {
407
+ tree_id : self . tree_id ,
420
408
value : elem,
421
409
parent : Some ( property) ,
422
410
make_persistent_id : self . make_persistent_id ,
423
411
config : self . config ,
424
412
} ;
425
413
426
- nested_tree. show_impl ( ui, path_segments, reset_path_ids , renderer ) ;
414
+ nested_tree. show_impl ( ui, path_segments, renderer , should_reset_expanded ) ;
427
415
} ;
428
416
429
417
if is_expandable && !toggle_buttons_hidden {
@@ -463,14 +451,9 @@ impl<'a, 'b, T: ToJsonTreeValue> JsonTreeNode<'a, 'b, T> {
463
451
} ,
464
452
) ;
465
453
} ) ;
466
-
467
- if renderer. render_hook . is_some ( ) {
468
- // show_body_indented will store the CollapsingState,
469
- // but since the subsequent render call above could also mutate the state in the render hook,
470
- // we must store it again.
471
- state. store ( ui. ctx ( ) ) ;
472
- }
473
454
}
455
+ // Ensure we store any change to the state if we reset expanded or if the render hook mutated it.
456
+ state. store ( ui. ctx ( ) ) ;
474
457
}
475
458
}
476
459
@@ -484,3 +467,8 @@ struct JsonTreeNodeConfig {
484
467
/// Avoids potential conflicts in case a `u64` happened to be stored against the same tree Id.
485
468
#[ derive( Debug , Clone , Copy , PartialEq , Eq ) ]
486
469
struct ResetExpandedHashId ( u64 ) ;
470
+
471
+ /// Stored in `egui`'s `IdTypeMap` to indicate that the tree should reset its expanded arrays/objects before rendering on a given frame.
472
+ /// Avoids potential conflicts in case a `bool` happened to be stored against the same tree Id.
473
+ #[ derive( Clone , Copy , PartialEq , Eq , Default ) ]
474
+ pub ( crate ) struct ShouldResetExpanded ;
0 commit comments