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