Skip to main content

dear_implot/context/
token.rs

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