Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add insertion_order and color_conflict_handling to Legend #45

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 52 additions & 11 deletions egui_plot/src/legend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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))]
Expand All @@ -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<ahash::HashSet<String>>,
}
Expand All @@ -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,
}
}
Expand Down Expand Up @@ -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.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as opposed to what?
maybe this should also be an enum?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The legends currently get sorted due to the b-tree.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe add another doc comment here

#[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)]
Expand Down Expand Up @@ -180,7 +210,7 @@ impl LegendEntry {
#[derive(Clone)]
pub(super) struct LegendWidget {
rect: Rect,
entries: BTreeMap<String, LegendEntry>,
entries: Vec<(String, LegendEntry)>,
config: Legend,
}

Expand All @@ -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<String, LegendEntry> = BTreeMap::new();
let mut keys: BTreeMap<String, usize> = 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(|| {
Expand All @@ -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,
})
}
Expand Down Expand Up @@ -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<String, LegendEntry>,
) {
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()
Expand Down
2 changes: 1 addition & 1 deletion egui_plot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down
Loading