Skip to main content

dear_implot/context/
token.rs

1use super::callbacks::PlotScopeGuard;
2use super::core::PlotContextBinding;
3use crate::sys;
4use dear_imgui_rs::{DrawListMut, Ui};
5use std::marker::PhantomData;
6use std::rc::Rc;
7
8/// Token that represents an active plot
9///
10/// The plot will be automatically ended when this token is dropped.
11pub struct PlotToken<'ui> {
12    binding: PlotContextBinding,
13    ui: &'ui Ui,
14    _scope: PlotScopeGuard,
15    _lifetime: PhantomData<&'ui ()>,
16}
17
18impl<'ui> PlotToken<'ui> {
19    /// Create a new PlotToken (internal use only)
20    pub(crate) fn new(binding: PlotContextBinding, ui: &'ui Ui) -> Self {
21        Self {
22            binding,
23            ui,
24            _scope: PlotScopeGuard::new(),
25            _lifetime: PhantomData,
26        }
27    }
28
29    /// Manually end the plot
30    ///
31    /// This is called automatically when the token is dropped,
32    /// but you can call it manually if needed.
33    pub fn end(self) {
34        // The actual ending happens in Drop
35    }
36
37    /// Get the active plot draw list as a frame-bound wrapper.
38    pub fn plot_draw_list(&self) -> Option<DrawListMut<'_>> {
39        self.binding
40            .with_bound_context("dear-implot: PlotToken", || {
41                let draw_list = unsafe { sys::ImPlot_GetPlotDrawList() };
42                if draw_list.is_null() {
43                    None
44                } else {
45                    Some(unsafe { DrawListMut::from_raw_mut(self.ui, draw_list) })
46                }
47            })
48    }
49
50    /// Push a plot clip rect on this active plot.
51    ///
52    /// The returned token pops the clip rect when dropped and cannot outlive
53    /// the active plot token it was created from.
54    pub fn push_plot_clip_rect(&self, expand: f32) -> PlotClipRectToken<'_> {
55        assert!(
56            expand.is_finite(),
57            "PlotToken::push_plot_clip_rect() expand must be finite"
58        );
59        self.binding
60            .with_bound_context("dear-implot: PlotToken", || {
61                unsafe { sys::ImPlot_PushPlotClipRect(expand) };
62                PlotClipRectToken {
63                    binding: self.binding.clone(),
64                    was_popped: false,
65                    _lifetime: PhantomData,
66                    _not_send_or_sync: PhantomData,
67                }
68            })
69    }
70}
71
72impl<'ui> Drop for PlotToken<'ui> {
73    fn drop(&mut self) {
74        let _ = self.binding.try_with_bound_context(|| unsafe {
75            sys::ImPlot_EndPlot();
76        });
77    }
78}
79
80/// Token for a pushed ImPlot plot clip rect.
81#[must_use]
82pub struct PlotClipRectToken<'plot> {
83    binding: PlotContextBinding,
84    was_popped: bool,
85    _lifetime: PhantomData<&'plot ()>,
86    _not_send_or_sync: PhantomData<Rc<()>>,
87}
88
89impl PlotClipRectToken<'_> {
90    /// Pop the plot clip rect immediately instead of waiting for drop.
91    pub fn pop(mut self) {
92        self.pop_inner();
93    }
94
95    /// Pop the plot clip rect immediately instead of waiting for drop.
96    pub fn end(mut self) {
97        self.pop_inner();
98    }
99
100    fn pop_inner(&mut self) {
101        if self.was_popped {
102            panic!("Attempted to pop an ImPlot plot clip rect token twice.");
103        }
104        self.binding
105            .with_bound_context("dear-implot: PlotClipRectToken", || {
106                unsafe { sys::ImPlot_PopPlotClipRect() };
107            });
108        self.was_popped = true;
109    }
110}
111
112impl Drop for PlotClipRectToken<'_> {
113    fn drop(&mut self) {
114        if !self.was_popped {
115            let _ = self
116                .binding
117                .try_with_bound_context(|| unsafe { sys::ImPlot_PopPlotClipRect() });
118            self.was_popped = true;
119        }
120    }
121}