Skip to main content

dear_imgui_rs/widget/misc/
disabled.rs

1use crate::Ui;
2use crate::scope::{NativeScopePop, NativeScopeToken};
3
4// ============================================================================
5// Disabled scope (RAII)
6// ============================================================================
7
8/// Tracks a disabled scope begun with [`Ui::begin_disabled`] and ended on drop.
9///
10/// Disabled scopes share Dear ImGui's item-flag stack with [`Ui::push_item_flag`]. A scope that
11/// transitions into the disabled state also shares `Style.Alpha` restoration order with
12/// [`crate::StyleVar::Alpha`] tokens. Tokens on either affected stack must finish in reverse
13/// creation order and in their originating window `Begin` scope. Prefer [`Ui::with_disabled`] or
14/// [`Ui::with_disabled_if`] when a closure expresses the intended lifetime.
15#[must_use]
16#[doc(alias = "EndDisabled")]
17pub struct DisabledToken<'ui> {
18    scope: NativeScopeToken<'ui>,
19}
20
21impl<'ui> DisabledToken<'ui> {
22    fn new(ui: &'ui Ui, restores_alpha: bool) -> Self {
23        Self {
24            scope: ui.begin_native_scope(
25                NativeScopePop::EndDisabled { restores_alpha },
26                "DisabledToken",
27            ),
28        }
29    }
30
31    /// Ends the disabled scope explicitly.
32    ///
33    /// # Panics
34    ///
35    /// Panics before FFI if a later item-flag or disabled token is active, an Alpha style token
36    /// depends on this scope's saved state, or this token is outside its originating window
37    /// `Begin` scope.
38    pub fn end(self) {
39        // Drop will call EndDisabled
40    }
41}
42
43impl<'ui> Drop for DisabledToken<'ui> {
44    fn drop(&mut self) {
45        self.scope.finish();
46    }
47}
48
49impl Ui {
50    /// Begin a disabled scope for subsequent items.
51    ///
52    /// All following widgets will be disabled (grayed out and non-interactive)
53    /// until the returned token is dropped.
54    #[doc(alias = "BeginDisabled")]
55    pub fn begin_disabled(&self) -> DisabledToken<'_> {
56        self.begin_disabled_with_cond(true)
57    }
58
59    /// Begin a conditionally disabled scope for subsequent items.
60    ///
61    /// If `disabled` is false, this still needs to be paired with the returned
62    /// token being dropped to correctly balance the internal stack.
63    #[doc(alias = "BeginDisabled")]
64    pub fn begin_disabled_with_cond(&self, disabled: bool) -> DisabledToken<'_> {
65        let restores_alpha = self.run_with_bound_context(|| unsafe {
66            let context = self.context_raw();
67            let was_disabled =
68                (*context).CurrentItemFlags & sys::ImGuiItemFlags_Disabled as i32 != 0;
69            sys::igBeginDisabled(disabled);
70            disabled && !was_disabled
71        });
72        DisabledToken::new(self, restores_alpha)
73    }
74
75    /// Runs `f` while subsequent items are disabled.
76    ///
77    /// The disabled scope is ended before a successful closure result is returned and during
78    /// unwinding if `f` panics.
79    #[doc(alias = "BeginDisabled", alias = "EndDisabled")]
80    pub fn with_disabled<R>(&self, f: impl FnOnce() -> R) -> R {
81        self.with_disabled_if(true, f)
82    }
83
84    /// Runs `f` inside a conditionally disabled scope.
85    ///
86    /// Dear ImGui requires a balanced `BeginDisabled`/`EndDisabled` pair even when `disabled` is
87    /// false. This helper preserves that balance before returning or during unwinding.
88    #[doc(alias = "BeginDisabled", alias = "EndDisabled")]
89    pub fn with_disabled_if<R>(&self, disabled: bool, f: impl FnOnce() -> R) -> R {
90        let token = self.begin_disabled_with_cond(disabled);
91        let result = f();
92        drop(token);
93        result
94    }
95}