@@ -51,7 +51,7 @@ pub struct Legend {
51
51
color_conflict_handling : ColorConflictHandling ,
52
52
53
53
/// Used for overriding the `hidden_items` set in [`LegendWidget`].
54
- hidden_items : Option < ahash:: HashSet < String > > ,
54
+ hidden_items : Option < ahash:: HashSet < Id > > ,
55
55
}
56
56
57
57
impl Default for Legend {
@@ -94,7 +94,7 @@ impl Legend {
94
94
#[ inline]
95
95
pub fn hidden_items < I > ( mut self , hidden_items : I ) -> Self
96
96
where
97
- I : IntoIterator < Item = String > ,
97
+ I : IntoIterator < Item = Id > ,
98
98
{
99
99
self . hidden_items = Some ( hidden_items. into_iter ( ) . collect ( ) ) ;
100
100
self
@@ -123,33 +123,36 @@ impl Legend {
123
123
124
124
#[ derive( Clone ) ]
125
125
struct LegendEntry {
126
- item_id : Option < Id > ,
126
+ id : Id ,
127
+ name : String ,
127
128
color : Color32 ,
128
129
checked : bool ,
129
130
hovered : bool ,
130
131
}
131
132
132
133
impl LegendEntry {
133
- fn new ( item_id : Option < Id > , color : Color32 , checked : bool ) -> Self {
134
+ fn new ( id : Id , name : String , color : Color32 , checked : bool ) -> Self {
134
135
Self {
135
- item_id,
136
+ id,
137
+ name,
136
138
color,
137
139
checked,
138
140
hovered : false ,
139
141
}
140
142
}
141
143
142
- fn ui ( & self , ui : & mut Ui , text : String , text_style : & TextStyle ) -> Response {
144
+ fn ui ( & self , ui : & mut Ui , text_style : & TextStyle ) -> Response {
143
145
let Self {
144
- item_id : _,
146
+ id : _,
147
+ name,
145
148
color,
146
149
checked,
147
150
hovered : _,
148
151
} = self ;
149
152
150
153
let font_id = text_style. resolve ( ui. style ( ) ) ;
151
154
152
- let galley = ui. fonts ( |f| f. layout_delayed_color ( text , font_id, f32:: INFINITY ) ) ;
155
+ let galley = ui. fonts ( |f| f. layout_delayed_color ( name . clone ( ) , font_id, f32:: INFINITY ) ) ;
153
156
154
157
let icon_size = galley. size ( ) . y ;
155
158
let icon_spacing = icon_size / 5.0 ;
@@ -216,36 +219,26 @@ impl LegendEntry {
216
219
#[ derive( Clone ) ]
217
220
pub ( super ) struct LegendWidget {
218
221
rect : Rect ,
219
- entries : Vec < ( String , LegendEntry ) > ,
222
+ entries : Vec < LegendEntry > ,
220
223
config : Legend ,
221
224
}
222
225
223
- /// A reference to a legend item.
224
- ///
225
- /// Since item ids are optional, we need to keep the name as well, using it for identification if needed.
226
- #[ cfg_attr( feature = "serde" , derive( serde:: Deserialize , serde:: Serialize ) ) ]
227
- #[ derive( Clone , Debug , PartialEq , Eq , Hash ) ]
228
- pub struct LegendItemReference {
229
- pub name : String ,
230
- pub item_id : Option < Id > ,
231
- }
232
-
233
226
impl LegendWidget {
234
227
/// Create a new legend from items, the names of items that are hidden and the style of the
235
228
/// text. Returns `None` if the legend has no entries.
236
229
pub ( super ) fn try_new < ' a > (
237
230
rect : Rect ,
238
231
config : Legend ,
239
232
items : & [ Box < dyn PlotItem + ' a > ] ,
240
- hidden_items : & ahash:: HashSet < String > , // Existing hidden items in the plot memory.
233
+ hidden_items : & ahash:: HashSet < Id > , // Existing hidden items in the plot memory.
241
234
) -> Option < Self > {
242
235
// If `config.hidden_items` is not `None`, it is used.
243
236
let hidden_items = config. hidden_items . as_ref ( ) . unwrap_or ( hidden_items) ;
244
237
245
238
// Collect the legend entries. If multiple items have the same name, they share a
246
239
// checkbox. If their colors don't match, we pick a neutral color for the checkbox.
247
240
let mut keys: BTreeMap < String , usize > = BTreeMap :: new ( ) ;
248
- let mut entries: BTreeMap < ( usize , String ) , LegendEntry > = BTreeMap :: new ( ) ;
241
+ let mut entries: BTreeMap < ( usize , & str ) , LegendEntry > = BTreeMap :: new ( ) ;
249
242
items
250
243
. iter ( )
251
244
. filter ( |item| !item. name ( ) . is_empty ( ) )
@@ -257,8 +250,9 @@ impl LegendWidget {
257
250
// Use the same key if we don't want insertion order
258
251
0
259
252
} ;
253
+
260
254
entries
261
- . entry ( ( key, item. name ( ) . to_owned ( ) ) )
255
+ . entry ( ( key, item. name ( ) ) )
262
256
. and_modify ( |entry| {
263
257
if entry. color != item. color ( ) {
264
258
match config. color_conflict_handling {
@@ -273,35 +267,30 @@ impl LegendWidget {
273
267
} )
274
268
. or_insert_with ( || {
275
269
let color = item. color ( ) ;
276
- let checked = !hidden_items. contains ( item. name ( ) ) ;
277
- LegendEntry :: new ( item. id ( ) , color, checked)
270
+ let checked = !hidden_items. contains ( & item. id ( ) ) ;
271
+ LegendEntry :: new ( item. id ( ) , item . name ( ) . to_owned ( ) , color, checked)
278
272
} ) ;
279
273
} ) ;
280
274
( !entries. is_empty ( ) ) . then_some ( Self {
281
275
rect,
282
- entries : entries. into_iter ( ) . map ( | ( ( _ , k ) , v ) | ( k , v ) ) . collect ( ) ,
276
+ entries : entries. into_values ( ) . collect ( ) ,
283
277
config,
284
278
} )
285
279
}
286
280
287
281
// Get the names of the hidden items.
288
- pub fn hidden_items ( & self ) -> ahash:: HashSet < String > {
282
+ pub fn hidden_items ( & self ) -> ahash:: HashSet < Id > {
289
283
self . entries
290
284
. iter ( )
291
- . filter ( |( _, entry) | !entry. checked )
292
- . map ( |( name, _) | name. clone ( ) )
285
+ . filter_map ( |entry| ( !entry. checked ) . then_some ( entry. id ) )
293
286
. collect ( )
294
287
}
295
288
296
289
// Get the name of the hovered items.
297
- pub fn hovered_item ( & self ) -> Option < LegendItemReference > {
290
+ pub fn hovered_item ( & self ) -> Option < Id > {
298
291
self . entries
299
292
. iter ( )
300
- . find ( |( _, entry) | entry. hovered )
301
- . map ( |( name, entry) | LegendItemReference {
302
- name : name. to_string ( ) ,
303
- item_id : entry. item_id ,
304
- } )
293
+ . find_map ( |entry| entry. hovered . then_some ( entry. id ) )
305
294
}
306
295
}
307
296
@@ -343,14 +332,14 @@ impl Widget for &mut LegendWidget {
343
332
344
333
let response_union = entries
345
334
. iter_mut ( )
346
- . map ( |( name , entry) | {
347
- let response = entry. ui ( ui, name . clone ( ) , & config. text_style ) ;
335
+ . map ( |entry| {
336
+ let response = entry. ui ( ui, & config. text_style ) ;
348
337
349
338
// Handle interactions. Alt-clicking must be deferred to end of loop
350
339
// since it may affect all entries.
351
340
handle_interaction_on_legend_item ( & response, entry) ;
352
341
if response. clicked ( ) && ui. input ( |r| r. modifiers . alt ) {
353
- focus_on_item = Some ( name . clone ( ) ) ;
342
+ focus_on_item = Some ( entry . id ) ;
354
343
}
355
344
356
345
response
@@ -377,14 +366,14 @@ fn handle_interaction_on_legend_item(response: &Response, entry: &mut LegendEntr
377
366
}
378
367
379
368
/// Handle alt-click interaction (which may affect all entries).
380
- fn handle_focus_on_legend_item ( clicked_entry_name : & str , entries : & mut [ ( String , LegendEntry ) ] ) {
369
+ fn handle_focus_on_legend_item ( clicked_entry : & Id , entries : & mut [ LegendEntry ] ) {
381
370
// if all other items are already hidden, we show everything
382
371
let is_focus_item_only_visible = entries
383
372
. iter ( )
384
- . all ( |( name , entry) | !entry. checked || ( clicked_entry_name == name ) ) ;
373
+ . all ( |entry| !entry. checked || ( clicked_entry == & entry . id ) ) ;
385
374
386
375
// either show everything or show only the focus item
387
- for ( name , entry) in entries. iter_mut ( ) {
388
- entry. checked = is_focus_item_only_visible || clicked_entry_name == name ;
376
+ for entry in entries. iter_mut ( ) {
377
+ entry. checked = is_focus_item_only_visible || clicked_entry == & entry . id ;
389
378
}
390
379
}
0 commit comments