diff --git a/Cargo.lock b/Cargo.lock index 24affbbd..30cf1eed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1278,7 +1278,7 @@ dependencies = [ "ahash", "egui", "enum-map", - "itertools", + "itertools 0.15.0", "log", "profiling", ] @@ -1331,6 +1331,7 @@ dependencies = [ "emath", "env_logger", "log", + "nohash-hasher", "performance_lines", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index 660a9426..adbdb583 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ env_logger = { version = "0.11.8", default-features = false, features = [ ] } image = { version = "0.25", default-features = false } log = "0.4" +nohash-hasher = "0.2.0" serde = { version = "1", features = ["derive"] } wasm-bindgen-futures = "0.4" web-sys = "0.3.83" diff --git a/egui_plot/Cargo.toml b/egui_plot/Cargo.toml index 8c38a0e7..bcc1640f 100644 --- a/egui_plot/Cargo.toml +++ b/egui_plot/Cargo.toml @@ -39,6 +39,7 @@ egui = { workspace = true, default-features = false } emath = { workspace = true, default-features = false } ahash.workspace = true +nohash-hasher.workspace = true #! ### Optional dependencies ## Enable this when generating docs. diff --git a/egui_plot/src/item_id.rs b/egui_plot/src/item_id.rs new file mode 100644 index 00000000..1d1a4962 --- /dev/null +++ b/egui_plot/src/item_id.rs @@ -0,0 +1,64 @@ +//! Identifiers for the items within a plot. + +use core::hash::Hash; +use core::num::NonZeroU64; + +/// The seeds used when hashing an [`ItemId`] source. +/// +/// Fixed so that the same source always produces the same [`ItemId`], +/// which is what lets [`crate::PlotMemory`] be persisted between runs. +const HASH_SEEDS: (u64, u64, u64, u64) = (9, 10, 11, 12); + +/// An `ItemIdSet` is a `HashSet` that skips hashing, +/// since an [`ItemId`] already is a high-entropy hash. +pub type ItemIdSet = nohash_hasher::IntSet; + +/// An `ItemIdMap` is a `HashMap` that skips hashing, +/// since an [`ItemId`] already is a high-entropy hash. +pub type ItemIdMap = nohash_hasher::IntMap; + +/// Identifies a [`crate::PlotItem`] within a plot. +/// +/// An `ItemId` only has to be unique within the plot it is used in. +/// +/// By default each item derives its `ItemId` from the name it was created with, +/// but you can set one explicitly, e.g. with [`crate::Line::id`]. Do that when +/// the name changes between frames, or when several items share a name. +/// +/// This is niche-optimized, so that `Option` is the same size as `ItemId`. +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct ItemId(NonZeroU64); + +impl nohash_hasher::IsEnabled for ItemId {} + +impl ItemId { + /// Hash any source (e.g. a string, an integer, or a tuple of those) into an [`ItemId`]. + /// + /// Prefer a tuple over formatting a string: + /// + /// ``` + /// # use egui_plot::ItemId; + /// # let (row, column) = (0, 0); + /// let good = ItemId::new(("my_cell", row, column)); // No allocation + /// let bad = ItemId::new(format!("my_cell {row} {column}")); // Allocates + /// # let _ = (good, bad); + /// ``` + pub fn new(source: impl Hash) -> Self { + let (a, b, c, d) = HASH_SEEDS; + let hash = ahash::RandomState::with_seeds(a, b, c, d).hash_one(source); + Self(NonZeroU64::new(hash).unwrap_or(NonZeroU64::MIN)) // The hash was exactly zero (very bad luck) + } + + /// The inner value, which is a high-entropy hash. + #[inline(always)] + pub fn value(self) -> u64 { + self.0.get() + } +} + +impl core::fmt::Debug for ItemId { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "ItemId({:04X})", self.value() as u16) + } +} diff --git a/egui_plot/src/items/arrows.rs b/egui_plot/src/items/arrows.rs index 75954404..685c054e 100644 --- a/egui_plot/src/items/arrows.rs +++ b/egui_plot/src/items/arrows.rs @@ -1,7 +1,7 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::Ui; @@ -10,6 +10,7 @@ use emath::Rot2; use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::data::PlotPoints; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -71,13 +72,15 @@ impl<'a> Arrows<'a> { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/bar_chart.rs b/egui_plot/src/items/bar_chart.rs index 65415c22..a78caeea 100644 --- a/egui_plot/src/items/bar_chart.rs +++ b/egui_plot/src/items/bar_chart.rs @@ -1,8 +1,8 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; use egui::CornerRadius; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::Ui; @@ -17,6 +17,7 @@ use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; use crate::colors::highlighted_color; use crate::cursor::Cursor; +use crate::item_id::ItemId; use crate::items::ClosestElem; use crate::items::PlotConfig; use crate::items::PlotGeometry; @@ -161,13 +162,15 @@ impl BarChart { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/box_plot.rs b/egui_plot/src/items/box_plot.rs index c5de980d..5290f4fd 100644 --- a/egui_plot/src/items/box_plot.rs +++ b/egui_plot/src/items/box_plot.rs @@ -1,8 +1,8 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; use egui::CornerRadius; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::Ui; @@ -16,6 +16,7 @@ use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; use crate::colors::highlighted_color; use crate::cursor::Cursor; +use crate::item_id::ItemId; use crate::items::ClosestElem; use crate::items::PlotConfig; use crate::items::PlotGeometry; @@ -127,13 +128,15 @@ impl BoxPlot { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/filled_area.rs b/egui_plot/src/items/filled_area.rs index c4ec2593..06e11181 100644 --- a/egui_plot/src/items/filled_area.rs +++ b/egui_plot/src/items/filled_area.rs @@ -1,8 +1,8 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use std::sync::Arc; use egui::Color32; -use egui::Id; use egui::Mesh; use egui::Pos2; use egui::Shape; @@ -14,6 +14,7 @@ use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; use crate::colors::DEFAULT_FILL_ALPHA; use crate::data::PlotPoints; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -115,10 +116,12 @@ impl FilledArea { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. + /// + /// The id only has to be unique within the plot. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/line.rs b/egui_plot/src/items/line.rs index cffb8609..042e39ca 100644 --- a/egui_plot/src/items/line.rs +++ b/egui_plot/src/items/line.rs @@ -1,7 +1,7 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::Ui; @@ -13,6 +13,7 @@ use crate::aesthetics::LineStyle; use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -97,13 +98,15 @@ impl HLine { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } @@ -232,13 +235,15 @@ impl VLine { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. + /// + /// The id only has to be unique within the plot. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/mod.rs b/egui_plot/src/items/mod.rs index 95d381bb..e88040de 100644 --- a/egui_plot/src/items/mod.rs +++ b/egui_plot/src/items/mod.rs @@ -8,7 +8,6 @@ use std::ops::RangeInclusive; use egui::Align2; use egui::Color32; -use egui::Id; use egui::PopupAnchor; use egui::Pos2; use egui::Shape; @@ -22,6 +21,7 @@ use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; use crate::cursor::Cursor; +use crate::item_id::ItemId; pub use crate::items::arrows::Arrows; pub use crate::items::bar_chart::Bar; pub use crate::items::bar_chart::BarChart; @@ -61,7 +61,7 @@ mod text; #[derive(Clone, Debug, PartialEq, Eq)] pub struct PlotItemBase { name: String, - id: Id, + id: ItemId, highlight: bool, allow_hover: bool, } @@ -69,7 +69,7 @@ pub struct PlotItemBase { impl PlotItemBase { /// Create a new plot item base with the given name. pub fn new(name: String) -> Self { - let id = Id::new(&name); + let id = ItemId::new(&name); Self { name, id, @@ -141,8 +141,10 @@ pub trait PlotItem { /// Returns a mutable reference to the base data of the plot item. fn base_mut(&mut self) -> &mut PlotItemBase; - /// Returns the ID of the plot item. - fn id(&self) -> Id { + /// Returns the [`ItemId`] of the plot item. + /// + /// This only identifies the item within its plot. + fn id(&self) -> ItemId { self.base().id } diff --git a/egui_plot/src/items/plot_image.rs b/egui_plot/src/items/plot_image.rs index 1dc7a9b6..6c07a57c 100644 --- a/egui_plot/src/items/plot_image.rs +++ b/egui_plot/src/items/plot_image.rs @@ -1,8 +1,8 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; use egui::CornerRadius; -use egui::Id; use egui::ImageOptions; use egui::Shape; use egui::Stroke; @@ -16,6 +16,7 @@ use emath::pos2; use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -114,13 +115,15 @@ impl PlotImage { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/points.rs b/egui_plot/src/items/points.rs index faa523ac..62e88968 100644 --- a/egui_plot/src/items/points.rs +++ b/egui_plot/src/items/points.rs @@ -1,7 +1,7 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::Ui; @@ -15,6 +15,7 @@ use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; use crate::data::PlotPoints; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -100,13 +101,15 @@ impl<'a> Points<'a> { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/polygon.rs b/egui_plot/src/items/polygon.rs index 84164c78..7fe1f88a 100644 --- a/egui_plot/src/items/polygon.rs +++ b/egui_plot/src/items/polygon.rs @@ -1,7 +1,7 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::Ui; @@ -12,6 +12,7 @@ use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::colors::DEFAULT_FILL_ALPHA; use crate::data::PlotPoints; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -96,13 +97,15 @@ impl<'a> Polygon<'a> { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/series.rs b/egui_plot/src/items/series.rs index f77ce8ba..0335b734 100644 --- a/egui_plot/src/items/series.rs +++ b/egui_plot/src/items/series.rs @@ -1,8 +1,8 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use std::sync::Arc; use egui::Color32; -use egui::Id; use egui::Mesh; use egui::Rgba; use egui::Shape; @@ -21,6 +21,7 @@ use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; use crate::colors::DEFAULT_FILL_ALPHA; use crate::data::PlotPoints; +use crate::item_id::ItemId; use crate::items::ClosestElem; use crate::items::PlotGeometry; use crate::items::PlotItem; @@ -147,13 +148,15 @@ impl<'a> Line<'a> { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/items/text.rs b/egui_plot/src/items/text.rs index c506104a..b4d626de 100644 --- a/egui_plot/src/items/text.rs +++ b/egui_plot/src/items/text.rs @@ -1,7 +1,7 @@ +use core::hash::Hash; use std::ops::RangeInclusive; use egui::Color32; -use egui::Id; use egui::Shape; use egui::Stroke; use egui::TextStyle; @@ -13,6 +13,7 @@ use emath::Align2; use crate::axis::PlotTransform; use crate::bounds::PlotBounds; use crate::bounds::PlotPoint; +use crate::item_id::ItemId; use crate::items::PlotGeometry; use crate::items::PlotItem; use crate::items::PlotItemBase; @@ -74,13 +75,15 @@ impl Text { self } - /// Sets the id of this plot item. + /// Sets the [`ItemId`] of this plot item. /// - /// By default the id is determined from the name passed to [`Self::new`], + /// The id only has to be unique within the plot. + /// + /// By default the id is derived from the name passed to [`Self::new`], /// but it can be explicitly set to a different value. #[inline] - pub fn id(mut self, id: impl Into) -> Self { - self.base_mut().id = id.into(); + pub fn id(mut self, id: impl Hash) -> Self { + self.base_mut().id = ItemId::new(id); self } } diff --git a/egui_plot/src/lib.rs b/egui_plot/src/lib.rs index ac7c4033..608cf584 100644 --- a/egui_plot/src/lib.rs +++ b/egui_plot/src/lib.rs @@ -15,6 +15,7 @@ mod colors; mod cursor; mod data; mod grid; +mod item_id; mod items; mod label; mod math; @@ -40,6 +41,9 @@ pub use crate::grid::GridInput; pub use crate::grid::GridMark; pub use crate::grid::log_grid_spacer; pub use crate::grid::uniform_grid_spacer; +pub use crate::item_id::ItemId; +pub use crate::item_id::ItemIdMap; +pub use crate::item_id::ItemIdSet; pub use crate::items::Arrows; pub use crate::items::Bar; pub use crate::items::BarChart; diff --git a/egui_plot/src/memory.rs b/egui_plot/src/memory.rs index b0b0aa0e..f47003d0 100644 --- a/egui_plot/src/memory.rs +++ b/egui_plot/src/memory.rs @@ -7,6 +7,8 @@ use egui::Vec2b; use crate::axis::PlotTransform; use crate::bounds::PlotBounds; +use crate::item_id::ItemId; +use crate::item_id::ItemIdSet; /// Information about the plot that has to persist between frames. #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -19,10 +21,10 @@ pub struct PlotMemory { pub auto_bounds: Vec2b, /// Hovered legend item if any. - pub hovered_legend_item: Option, + pub hovered_legend_item: Option, /// Which items _not_ to show? - pub hidden_items: ahash::HashSet, + pub hidden_items: ItemIdSet, /// The transform from last frame. pub(crate) transform: PlotTransform, diff --git a/egui_plot/src/overlays/legend.rs b/egui_plot/src/overlays/legend.rs index f0da5975..3396e295 100644 --- a/egui_plot/src/overlays/legend.rs +++ b/egui_plot/src/overlays/legend.rs @@ -4,7 +4,6 @@ use egui::Align; use egui::Color32; use egui::Direction; use egui::Frame; -use egui::Id; use egui::Layout; use egui::PointerButton; use egui::Rect; @@ -21,6 +20,9 @@ use egui::epaint::CircleShape; use egui::pos2; use egui::vec2; +use crate::item_id::ItemId; +use crate::item_id::ItemIdMap; +use crate::item_id::ItemIdSet; use crate::items::PlotItem; use crate::placement::Corner; @@ -41,7 +43,7 @@ pub enum LegendGrouping { #[default] ByName, - /// Each item gets its own legend entry, keyed by its unique [`Id`]. + /// Each item gets its own legend entry, keyed by its [`ItemId`]. ById, } @@ -59,7 +61,7 @@ pub struct Legend { color_conflict_handling: ColorConflictHandling, /// Used for overriding the `hidden_items` set in [`LegendWidget`]. - hidden_items: Option>, + hidden_items: Option, } impl Default for Legend { @@ -112,7 +114,7 @@ impl Legend { #[inline] pub fn hidden_items(mut self, hidden_items: I) -> Self where - I: IntoIterator, + I: IntoIterator, { self.hidden_items = Some(hidden_items.into_iter().collect()); self @@ -139,7 +141,7 @@ impl Legend { /// /// With [`LegendGrouping::ByName`], items sharing the same name are /// merged into a single legend entry. With [`LegendGrouping::ById`], - /// each item gets its own entry keyed by its unique [`Id`]. + /// each item gets its own entry keyed by its [`ItemId`]. #[inline] pub fn grouping(mut self, grouping: LegendGrouping) -> Self { self.grouping = grouping; @@ -149,7 +151,7 @@ impl Legend { #[derive(Clone)] struct LegendEntry { - id: Id, + id: ItemId, name: String, color: Color32, checked: bool, @@ -157,7 +159,7 @@ struct LegendEntry { } impl LegendEntry { - fn new(id: Id, name: String, color: Color32, checked: bool) -> Self { + fn new(id: ItemId, name: String, color: Color32, checked: bool) -> Self { Self { id, name, @@ -247,21 +249,21 @@ impl LegendWidget { rect: Rect, config: Legend, items: &[Box], - hidden_items: &ahash::HashSet, // Existing hidden items in the plot memory. + hidden_items: &ItemIdSet, // Existing hidden items in the plot memory. ) -> Option { // If `config.hidden_items` is not `None`, it is used. let hidden_items = config.hidden_items.as_ref().unwrap_or(hidden_items); // Collect the legend entries. With `ByName` grouping, items sharing the // same name are merged into a single checkbox. With `ById` grouping, - // items sharing the same `Id` are merged instead. When colors conflict + // items sharing the same `ItemId` are merged instead. When colors conflict // within a merged entry, `color_conflict_handling` decides which color // to show. let mut entries: Vec = Vec::new(); - let mut seen: ahash::HashMap = ahash::HashMap::default(); + let mut seen: ItemIdMap = ItemIdMap::default(); for item in items.iter().filter(|item| !item.name().is_empty()) { let dedup_key = match config.grouping { - LegendGrouping::ByName => Id::new(item.name()), + LegendGrouping::ByName => ItemId::new(item.name()), LegendGrouping::ById => item.id(), }; @@ -292,7 +294,7 @@ impl LegendWidget { } // Get the names of the hidden items. - pub fn hidden_items(&self) -> ahash::HashSet { + pub fn hidden_items(&self) -> ItemIdSet { self.entries .iter() .filter_map(|entry| (!entry.checked).then_some(entry.id)) @@ -300,7 +302,7 @@ impl LegendWidget { } // Get the name of the hovered items. - pub fn hovered_item(&self) -> Option { + pub fn hovered_item(&self) -> Option { self.entries.iter().find_map(|entry| entry.hovered.then_some(entry.id)) } } @@ -388,7 +390,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: &Id, entries: &mut [LegendEntry]) { +fn handle_focus_on_legend_item(clicked_entry: &ItemId, entries: &mut [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/plot.rs b/egui_plot/src/plot.rs index b7687e32..b4cb76b1 100644 --- a/egui_plot/src/plot.rs +++ b/egui_plot/src/plot.rs @@ -42,6 +42,7 @@ use crate::cursor::PlotFrameCursors; use crate::grid::GridInput; use crate::grid::GridMark; use crate::grid::GridSpacer; +use crate::item_id::ItemId; use crate::items; use crate::items::PlotItem; use crate::items::Span; @@ -977,7 +978,7 @@ impl<'a> Plot<'a> { legend: Option, ui: &mut Ui, mem: &mut PlotMemory, - hovered_plot_item: &mut Option, + hovered_plot_item: &mut Option, ) { if let Some(mut legend) = legend { ui.add(&mut legend); @@ -1323,7 +1324,7 @@ impl<'a> Plot<'a> { plot_id: Id, transform: &PlotTransform, show_xy: Vec2b, - ) -> (Vec, Vec, Option) { + ) -> (Vec, Vec, Option) { let mut child_ui = ui.new_child( egui::UiBuilder::new() .max_rect(*transform.frame()) @@ -1555,7 +1556,7 @@ impl<'a> Plot<'a> { plot_ui: &PlotUi<'_>, transform: &PlotTransform, show_xy: Vec2b, - ) -> (Vec, Option) { + ) -> (Vec, Option) { if !show_xy.any() { return (Vec::new(), None); } @@ -1877,7 +1878,7 @@ pub struct PlotResponse { /// This is `None` if either no item was hovered. /// A plot item can be hovered either by hovering its representation in the /// plot (line, marker, etc.) or by hovering the item in the legend. - pub hovered_plot_item: Option, + pub hovered_plot_item: Option, } /// Provides methods to interact with a plot while building it. It is the single diff --git a/examples/interaction/src/app.rs b/examples/interaction/src/app.rs index 47927143..bd3cc151 100644 --- a/examples/interaction/src/app.rs +++ b/examples/interaction/src/app.rs @@ -1,5 +1,6 @@ use eframe::egui; use eframe::egui::Response; +use egui_plot::ItemId; use egui_plot::Line; use egui_plot::Plot; use egui_plot::PlotPoint; @@ -13,7 +14,7 @@ pub struct InteractionExample { last_pointer_coordinate: Option, last_pointer_drag_delta: egui::Vec2, last_hovered: bool, - last_hovered_item: Option, + last_hovered_item: Option, } impl InteractionExample { @@ -83,9 +84,9 @@ impl InteractionExample { ); ui.label(format!("pointer coordinate drag delta: {coordinate_text}")); - let hovered_item = if self.last_hovered_item == Some(egui::Id::new("sin")) { + let hovered_item = if self.last_hovered_item == Some(ItemId::new("sin")) { "red sin" - } else if self.last_hovered_item == Some(egui::Id::new("cos")) { + } else if self.last_hovered_item == Some(ItemId::new("cos")) { "blue cos" } else { "none" diff --git a/examples/markers/src/app.rs b/examples/markers/src/app.rs index 975acd95..16a10a5d 100644 --- a/examples/markers/src/app.rs +++ b/examples/markers/src/app.rs @@ -40,7 +40,7 @@ impl MarkerDemo { [6.0, 0.5 + y_offset], ], ) - .id(format!("marker_{i}")) + .id(("marker", i)) .name(format!("{marker:?}")) .filled(self.fill_markers) .radius(self.marker_radius)