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