Skip to main content

dear_implot/plots/
dummy.rs

1//! Dummy plot implementation
2
3use super::{
4    PlotData, PlotDataLayout, PlotError, PlotItemStyle, plot_spec_with_style,
5    with_plot_str_or_empty,
6};
7use crate::{DummyFlags, ItemFlags, sys};
8
9/// Builder for dummy plots
10///
11/// Dummy plots add a legend entry without plotting any actual data.
12/// This is useful for creating custom legend entries or placeholders.
13pub struct DummyPlot<'a> {
14    label: &'a str,
15    style: PlotItemStyle,
16    flags: DummyFlags,
17    item_flags: ItemFlags,
18}
19
20impl<'a> super::PlotItemStyled for DummyPlot<'a> {
21    fn style_mut(&mut self) -> &mut PlotItemStyle {
22        &mut self.style
23    }
24}
25
26impl<'a> DummyPlot<'a> {
27    /// Create a new dummy plot with the given label
28    pub fn new(label: &'a str) -> Self {
29        Self {
30            label,
31            style: PlotItemStyle::default(),
32            flags: DummyFlags::NONE,
33            item_flags: ItemFlags::NONE,
34        }
35    }
36
37    /// Set ImPlotSpec-backed style overrides for this dummy plot.
38    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
39        self.style = style;
40        self
41    }
42
43    /// Set dummy flags for customization
44    pub fn with_flags(mut self, flags: DummyFlags) -> Self {
45        self.flags = flags;
46        self
47    }
48
49    /// Set common item flags for this plot item (applies to all plot types)
50    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
51        self.item_flags = flags;
52        self
53    }
54
55    /// Validate the plot data
56    pub fn validate(&self) -> Result<(), PlotError> {
57        if self.label.is_empty() {
58            return Err(PlotError::InvalidData("Label cannot be empty".to_string()));
59        }
60        Ok(())
61    }
62
63    /// Plot the dummy entry
64    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
65        let _guard = plot_ui.bind();
66        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
67            let spec = plot_spec_with_style(
68                self.style,
69                self.flags.bits() | self.item_flags.bits(),
70                PlotDataLayout::DEFAULT,
71            );
72            sys::ImPlot_PlotDummy(label_ptr, spec);
73        })
74    }
75}
76
77impl<'a> PlotData for DummyPlot<'a> {
78    fn label(&self) -> &str {
79        self.label
80    }
81
82    fn data_len(&self) -> usize {
83        0 // Dummy plots have no actual data
84    }
85}
86
87/// Multiple dummy plots for creating legend sections
88pub struct MultiDummyPlot<'a> {
89    labels: Vec<&'a str>,
90    style: PlotItemStyle,
91    flags: DummyFlags,
92    item_flags: ItemFlags,
93}
94
95impl<'a> super::PlotItemStyled for MultiDummyPlot<'a> {
96    fn style_mut(&mut self) -> &mut PlotItemStyle {
97        &mut self.style
98    }
99}
100
101impl<'a> MultiDummyPlot<'a> {
102    /// Create multiple dummy plots
103    pub fn new(labels: Vec<&'a str>) -> Self {
104        Self {
105            labels,
106            style: PlotItemStyle::default(),
107            flags: DummyFlags::NONE,
108            item_flags: ItemFlags::NONE,
109        }
110    }
111
112    /// Set ImPlotSpec-backed style overrides for all dummy entries.
113    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
114        self.style = style;
115        self
116    }
117
118    /// Set dummy flags for all entries
119    pub fn with_flags(mut self, flags: DummyFlags) -> Self {
120        self.flags = flags;
121        self
122    }
123
124    /// Set common item flags for all dummy entries
125    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
126        self.item_flags = flags;
127        self
128    }
129
130    /// Validate the plot data
131    pub fn validate(&self) -> Result<(), PlotError> {
132        if self.labels.is_empty() {
133            return Err(PlotError::EmptyData);
134        }
135
136        for (i, &label) in self.labels.iter().enumerate() {
137            if label.is_empty() {
138                return Err(PlotError::InvalidData(format!(
139                    "Label at index {} cannot be empty",
140                    i
141                )));
142            }
143        }
144
145        Ok(())
146    }
147
148    /// Plot all dummy entries
149    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
150        for &label in &self.labels {
151            let dummy_plot = DummyPlot::new(label)
152                .with_style(self.style)
153                .with_flags(self.flags)
154                .with_item_flags(self.item_flags);
155            dummy_plot.plot(plot_ui);
156        }
157    }
158}
159
160impl<'a> PlotData for MultiDummyPlot<'a> {
161    fn label(&self) -> &str {
162        "MultiDummy"
163    }
164
165    fn data_len(&self) -> usize {
166        self.labels.len()
167    }
168}
169
170/// Legend separator using dummy plots
171pub struct LegendSeparator<'a> {
172    label: &'a str,
173    style: PlotItemStyle,
174}
175
176impl<'a> super::PlotItemStyled for LegendSeparator<'a> {
177    fn style_mut(&mut self) -> &mut PlotItemStyle {
178        &mut self.style
179    }
180}
181
182impl<'a> LegendSeparator<'a> {
183    /// Create a legend separator with optional label
184    pub fn new(label: &'a str) -> Self {
185        Self {
186            label,
187            style: PlotItemStyle::default(),
188        }
189    }
190
191    /// Create an empty separator
192    pub fn empty() -> Self {
193        Self {
194            label: "---",
195            style: PlotItemStyle::default(),
196        }
197    }
198
199    /// Set ImPlotSpec-backed style overrides for this legend separator.
200    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
201        self.style = style;
202        self
203    }
204
205    /// Plot the separator
206    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
207        let dummy_plot = DummyPlot::new(self.label).with_style(self.style);
208        dummy_plot.plot(plot_ui);
209    }
210}
211
212/// Legend header using dummy plots
213pub struct LegendHeader<'a> {
214    title: &'a str,
215    style: PlotItemStyle,
216}
217
218impl<'a> super::PlotItemStyled for LegendHeader<'a> {
219    fn style_mut(&mut self) -> &mut PlotItemStyle {
220        &mut self.style
221    }
222}
223
224impl<'a> LegendHeader<'a> {
225    /// Create a legend header
226    pub fn new(title: &'a str) -> Self {
227        Self {
228            title,
229            style: PlotItemStyle::default(),
230        }
231    }
232
233    /// Set ImPlotSpec-backed style overrides for this legend header.
234    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
235        self.style = style;
236        self
237    }
238
239    /// Plot the header
240    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
241        let dummy_plot = DummyPlot::new(self.title).with_style(self.style);
242        dummy_plot.plot(plot_ui);
243    }
244}
245
246/// Custom legend entry builder
247pub struct CustomLegendEntry<'a> {
248    label: &'a str,
249    style: PlotItemStyle,
250    flags: DummyFlags,
251    item_flags: ItemFlags,
252}
253
254impl<'a> super::PlotItemStyled for CustomLegendEntry<'a> {
255    fn style_mut(&mut self) -> &mut PlotItemStyle {
256        &mut self.style
257    }
258}
259
260impl<'a> CustomLegendEntry<'a> {
261    /// Create a custom legend entry
262    pub fn new(label: &'a str) -> Self {
263        Self {
264            label,
265            style: PlotItemStyle::default(),
266            flags: DummyFlags::NONE,
267            item_flags: ItemFlags::NONE,
268        }
269    }
270
271    /// Set ImPlotSpec-backed style overrides for this legend entry.
272    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
273        self.style = style;
274        self
275    }
276
277    /// Set flags for the entry
278    pub fn with_flags(mut self, flags: DummyFlags) -> Self {
279        self.flags = flags;
280        self
281    }
282
283    /// Set common item flags for the entry
284    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
285        self.item_flags = flags;
286        self
287    }
288
289    /// Plot the custom entry
290    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
291        let dummy_plot = DummyPlot::new(self.label)
292            .with_style(self.style)
293            .with_flags(self.flags)
294            .with_item_flags(self.item_flags);
295        dummy_plot.plot(plot_ui);
296    }
297}
298
299/// Legend group for organizing related entries
300pub struct LegendGroup<'a> {
301    title: &'a str,
302    entries: Vec<&'a str>,
303    style: PlotItemStyle,
304    add_separator: bool,
305}
306
307impl<'a> super::PlotItemStyled for LegendGroup<'a> {
308    fn style_mut(&mut self) -> &mut PlotItemStyle {
309        &mut self.style
310    }
311}
312
313impl<'a> LegendGroup<'a> {
314    /// Create a new legend group
315    pub fn new(title: &'a str, entries: Vec<&'a str>) -> Self {
316        Self {
317            title,
318            entries,
319            style: PlotItemStyle::default(),
320            add_separator: true,
321        }
322    }
323
324    /// Set ImPlotSpec-backed style overrides for this legend group.
325    pub fn with_style(mut self, style: PlotItemStyle) -> Self {
326        self.style = style;
327        self
328    }
329
330    /// Disable separator after the group
331    pub fn no_separator(mut self) -> Self {
332        self.add_separator = false;
333        self
334    }
335
336    /// Plot the legend group
337    pub fn plot(self, plot_ui: &crate::PlotUi<'_>) {
338        // Plot the header
339        LegendHeader::new(self.title)
340            .with_style(self.style)
341            .plot(plot_ui);
342
343        // Plot all entries
344        for &entry in &self.entries {
345            DummyPlot::new(entry).with_style(self.style).plot(plot_ui);
346        }
347
348        // Add separator if requested
349        if self.add_separator && !self.entries.is_empty() {
350            LegendSeparator::empty()
351                .with_style(self.style)
352                .plot(plot_ui);
353        }
354    }
355}
356
357/// Convenience functions for common dummy plot patterns
358impl<'a> DummyPlot<'a> {
359    /// Create a separator dummy plot
360    pub fn separator() -> DummyPlot<'static> {
361        DummyPlot::new("---")
362    }
363
364    /// Create a spacer dummy plot
365    pub fn spacer() -> DummyPlot<'static> {
366        DummyPlot::new(" ")
367    }
368
369    /// Create a header dummy plot
370    pub fn header(title: &'a str) -> DummyPlot<'a> {
371        DummyPlot::new(title)
372    }
373}
374
375/// Macro for creating multiple dummy plots easily
376#[macro_export]
377macro_rules! dummy_plots {
378    ($($label:expr),* $(,)?) => {
379        {
380            let labels = vec![$($label),*];
381            $crate::plots::dummy::MultiDummyPlot::new(labels)
382        }
383    };
384}
385
386/// Macro for creating a legend group
387#[macro_export]
388macro_rules! legend_group {
389    ($title:expr, $($entry:expr),* $(,)?) => {
390        {
391            let entries = vec![$($entry),*];
392            $crate::plots::dummy::LegendGroup::new($title, entries)
393        }
394    };
395}