diff --git a/egui_plot/src/legend.rs b/egui_plot/src/legend.rs index 8e81752..2c78808 100644 --- a/egui_plot/src/legend.rs +++ b/egui_plot/src/legend.rs @@ -30,6 +30,15 @@ impl Corner { } } +/// How to handle multiple conflicting color for a legend item. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub enum ColorConflictHandling { + PickFirst, + PickLast, + RemoveColor, +} + /// The configuration for a plot legend. #[derive(Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -38,6 +47,9 @@ pub struct Legend { pub background_alpha: f32, pub position: Corner, + insertion_order: bool, + color_conflict_handling: ColorConflictHandling, + /// Used for overriding the `hidden_items` set in [`LegendWidget`]. hidden_items: Option>, } @@ -48,7 +60,8 @@ impl Default for Legend { text_style: TextStyle::Body, background_alpha: 0.75, position: Corner::RightTop, - + insertion_order: false, + color_conflict_handling: ColorConflictHandling::RemoveColor, hidden_items: None, } } @@ -86,6 +99,23 @@ impl Legend { self.hidden_items = Some(hidden_items.into_iter().collect()); self } + + /// Specifies if the legend item order should be the inserted order. + #[inline] + pub fn insertion_order(mut self, insertion_order: bool) -> Self { + self.insertion_order = insertion_order; + self + } + + /// Specifies how to handle conflicting colors for an item. + #[inline] + pub fn color_conflict_handling( + mut self, + color_conflict_handling: ColorConflictHandling, + ) -> Self { + self.color_conflict_handling = color_conflict_handling; + self + } } #[derive(Clone)] @@ -180,7 +210,7 @@ impl LegendEntry { #[derive(Clone)] pub(super) struct LegendWidget { rect: Rect, - entries: BTreeMap, + entries: Vec<(String, LegendEntry)>, config: Legend, } @@ -198,17 +228,31 @@ impl LegendWidget { // Collect the legend entries. If multiple items have the same name, they share a // checkbox. If their colors don't match, we pick a neutral color for the checkbox. - let mut entries: BTreeMap = BTreeMap::new(); + let mut keys: BTreeMap = BTreeMap::new(); + let mut entries: BTreeMap<(usize, String), LegendEntry> = BTreeMap::new(); items .iter() .filter(|item| !item.name().is_empty()) .for_each(|item| { + let next_entry = entries.len(); + let key = if config.insertion_order { + *keys.entry(item.name().to_owned()).or_insert(next_entry) + } else { + // Use the same key if we don't want insertion order + 0 + }; entries - .entry(item.name().to_owned()) + .entry((key, item.name().to_owned())) .and_modify(|entry| { if entry.color != item.color() { - // Multiple items with different colors - entry.color = Color32::TRANSPARENT; + match config.color_conflict_handling { + ColorConflictHandling::PickFirst => (), + ColorConflictHandling::PickLast => entry.color = item.color(), + ColorConflictHandling::RemoveColor => { + // Multiple items with different colors + entry.color = Color32::TRANSPARENT; + } + } } }) .or_insert_with(|| { @@ -219,7 +263,7 @@ impl LegendWidget { }); (!entries.is_empty()).then_some(Self { rect, - entries, + entries: entries.into_iter().map(|((_, k), v)| (k, v)).collect(), config, }) } @@ -313,10 +357,7 @@ fn handle_interaction_on_legend_item(response: &Response, entry: &mut LegendEntr } /// Handle alt-click interaction (which may affect all entries). -fn handle_focus_on_legend_item( - clicked_entry_name: &str, - entries: &mut BTreeMap, -) { +fn handle_focus_on_legend_item(clicked_entry_name: &str, entries: &mut [(String, LegendEntry)]) { // if all other items are already hidden, we show everything let is_focus_item_only_visible = entries .iter() diff --git a/egui_plot/src/lib.rs b/egui_plot/src/lib.rs index 50b12cf..fd6e05b 100644 --- a/egui_plot/src/lib.rs +++ b/egui_plot/src/lib.rs @@ -32,7 +32,7 @@ pub use crate::{ MarkerShape, Orientation, PlotConfig, PlotGeometry, PlotImage, PlotItem, PlotPoint, PlotPoints, Points, Polygon, Text, VLine, }, - legend::{Corner, Legend}, + legend::{ColorConflictHandling, Corner, Legend}, memory::PlotMemory, plot_ui::PlotUi, transform::{PlotBounds, PlotTransform},