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}