1
+ use std:: sync:: Arc ;
2
+
1
3
use egui:: { Frame , Margin } ;
2
4
3
5
use re_ui:: { SyntaxHighlighting , UiExt as _, syntax_highlighting:: SyntaxHighlightedBuilder } ;
4
6
7
+ use super :: { ComparisonOperator , Filter , FilterOperation } ;
5
8
use crate :: TableBlueprint ;
6
- use crate :: filters:: { ComparisonOperator , Filter , FilterOperation } ;
7
9
8
10
/// Action to take based on the user interaction.
9
11
#[ derive( Debug , Clone , Copy , Default , PartialEq , Eq ) ]
@@ -112,27 +114,56 @@ impl FilterState {
112
114
right : 16 ,
113
115
} )
114
116
. show ( ui, |ui| {
115
- ui. horizontal ( |ui| {
116
- let active_index = self . active_filter . take ( ) ;
117
+ let active_index = self . active_filter . take ( ) ;
118
+ let mut remove_idx = None ;
119
+
120
+ // TODO(#11194): ideally, egui would allow wrapping `Frame` widget itself. Remove
121
+ // this when it does.
122
+ let prepared_uis = self
123
+ . filters
124
+ . iter ( )
125
+ . map ( |filter| filter. prepare_ui ( ui) )
126
+ . collect :: < Vec < _ > > ( ) ;
127
+ let item_spacing = ui. style ( ) . spacing . item_spacing . x ;
128
+ let available_width = ui. available_width ( ) ;
129
+ let mut rows = vec1:: vec1![ vec![ ] ] ;
130
+ let mut current_left_position = 0.0 ;
131
+ for ( index, prepared_ui) in prepared_uis. iter ( ) . enumerate ( ) {
132
+ if current_left_position > 0.0
133
+ && current_left_position + prepared_ui. desired_width ( ) > available_width
134
+ {
135
+ rows. push ( vec ! [ ] ) ;
136
+ current_left_position = 0.0 ;
137
+ }
138
+
139
+ rows. last_mut ( ) . push ( index) ;
140
+ current_left_position += prepared_ui. desired_width ( ) + item_spacing;
141
+ }
142
+
143
+ for row in rows {
144
+ ui. horizontal ( |ui| {
145
+ for index in row {
146
+ let filter_id = ui. make_persistent_id ( index) ;
147
+ let filter = & mut self . filters [ index] ;
148
+ let prepared_ui = & prepared_uis[ index] ;
117
149
118
- let mut remove_idx = None ;
119
- for ( index, filter) in self . filters . iter_mut ( ) . enumerate ( ) {
120
- let filter_id = ui. make_persistent_id ( index) ;
121
- let result = filter. ui ( ui, filter_id, Some ( index) == active_index) ;
150
+ let result =
151
+ filter. ui ( ui, prepared_ui, filter_id, Some ( index) == active_index) ;
122
152
123
- action = action. merge ( result. filter_action ) ;
153
+ action = action. merge ( result. filter_action ) ;
124
154
125
- if result. should_delete_filter {
126
- remove_idx = Some ( index) ;
155
+ if result. should_delete_filter {
156
+ remove_idx = Some ( index) ;
157
+ }
127
158
}
128
- }
159
+ } ) ;
160
+ }
129
161
130
- if let Some ( remove_idx) = remove_idx {
131
- self . active_filter = None ;
132
- self . filters . remove ( remove_idx) ;
133
- should_commit = true ;
134
- }
135
- } ) ;
162
+ if let Some ( remove_idx) = remove_idx {
163
+ self . active_filter = None ;
164
+ self . filters . remove ( remove_idx) ;
165
+ should_commit = true ;
166
+ }
136
167
} ) ;
137
168
138
169
action
@@ -145,31 +176,64 @@ struct DisplayFilterUiResult {
145
176
should_delete_filter : bool ,
146
177
}
147
178
179
+ // TODO(#11194): used by the manual wrapping code. Remove when no longer needed.
180
+ struct FilterPreparedUi {
181
+ frame : Frame ,
182
+ galley : Arc < egui:: Galley > ,
183
+ desired_width : f32 ,
184
+ }
185
+
186
+ impl FilterPreparedUi {
187
+ fn desired_width ( & self ) -> f32 {
188
+ self . desired_width
189
+ }
190
+ }
191
+
148
192
impl Filter {
193
+ /// Prepare the UI for this filter
194
+ fn prepare_ui ( & self , ui : & egui:: Ui ) -> FilterPreparedUi {
195
+ let layout_job = SyntaxHighlightedBuilder :: new ( )
196
+ . with_body ( & self . column_name )
197
+ . with_keyword ( " " )
198
+ . with ( & self . operation )
199
+ . into_job ( ui. style ( ) ) ;
200
+
201
+ let galley = ui. fonts ( |f| f. layout_job ( layout_job) ) ;
202
+
203
+ let frame = Frame :: new ( )
204
+ . inner_margin ( Margin :: symmetric ( 4 , 4 ) )
205
+ . stroke ( ui. tokens ( ) . table_filter_frame_stroke )
206
+ . corner_radius ( 2.0 ) ;
207
+
208
+ let desired_width = galley. size ( ) . x
209
+ + ui. style ( ) . spacing . item_spacing . x
210
+ + ui. tokens ( ) . small_icon_size . x
211
+ + frame. total_margin ( ) . sum ( ) . x ;
212
+
213
+ FilterPreparedUi {
214
+ frame,
215
+ galley,
216
+ desired_width,
217
+ }
218
+ }
219
+
149
220
/// UI for a single filter.
150
221
#[ must_use]
151
222
fn ui (
152
223
& mut self ,
153
224
ui : & mut egui:: Ui ,
225
+ prepared_ui : & FilterPreparedUi ,
154
226
filter_id : egui:: Id ,
155
227
activate_filter : bool ,
156
228
) -> DisplayFilterUiResult {
157
229
let mut should_delete_filter = false ;
158
230
let mut action_due_to_filter_deletion = FilterUiAction :: None ;
159
231
160
- let response = Frame :: new ( )
161
- . inner_margin ( Margin :: symmetric ( 4 , 4 ) )
162
- . stroke ( ui. tokens ( ) . table_filter_frame_stroke )
163
- . corner_radius ( 2.0 )
232
+ let response = prepared_ui
233
+ . frame
164
234
. show ( ui, |ui| {
165
- let widget_text = SyntaxHighlightedBuilder :: new ( )
166
- . with_body ( & self . column_name )
167
- . with_keyword ( " " )
168
- . with ( & self . operation )
169
- . into_widget_text ( ui. style ( ) ) ;
170
-
171
235
let text_response = ui. add (
172
- egui:: Label :: new ( widget_text )
236
+ egui:: Label :: new ( Arc :: clone ( & prepared_ui . galley ) )
173
237
. selectable ( false )
174
238
. sense ( egui:: Sense :: click ( ) ) ,
175
239
) ;
@@ -473,4 +537,47 @@ mod tests {
473
537
harness. snapshot ( format ! ( "popup_ui_{test_name}" ) ) ;
474
538
}
475
539
}
540
+
541
+ #[ test]
542
+ fn test_filter_wrapping ( ) {
543
+ let filters = vec ! [
544
+ Filter :: new(
545
+ "some:column:name" ,
546
+ FilterOperation :: StringContains ( "some query string" . to_owned( ) ) ,
547
+ ) ,
548
+ Filter :: new(
549
+ "other:column:name" ,
550
+ FilterOperation :: StringContains ( "hello" . to_owned( ) ) ,
551
+ ) ,
552
+ Filter :: new(
553
+ "short:name" ,
554
+ FilterOperation :: StringContains ( "world" . to_owned( ) ) ,
555
+ ) ,
556
+ Filter :: new(
557
+ "looooog:name" ,
558
+ FilterOperation :: StringContains ( "some more querying text here" . to_owned( ) ) ,
559
+ ) ,
560
+ Filter :: new(
561
+ "world" ,
562
+ FilterOperation :: StringContains ( ":wave:" . to_owned( ) ) ,
563
+ ) ,
564
+ ] ;
565
+
566
+ let mut filters = FilterState {
567
+ filters,
568
+ active_filter : None ,
569
+ } ;
570
+
571
+ let mut harness = egui_kittest:: Harness :: builder ( )
572
+ . with_size ( egui:: Vec2 :: new ( 700.0 , 500.0 ) )
573
+ . build_ui ( |ui| {
574
+ re_ui:: apply_style_and_install_loaders ( ui. ctx ( ) ) ;
575
+
576
+ filters. filter_bar_ui ( ui, & mut TableBlueprint :: default ( ) ) ;
577
+ } ) ;
578
+
579
+ harness. run ( ) ;
580
+
581
+ harness. snapshot ( "filter_wrapping" ) ;
582
+ }
476
583
}
0 commit comments