Skip to main content

dear_imgui_rs/layout/
stack_layout.rs

1use super::validation::assert_finite_vec2;
2use crate::sys;
3use crate::{Id, Ui};
4use std::ffi::c_void;
5
6create_token!(
7    /// Tracks a stack-layout horizontal group.
8    ///
9    /// This wraps the repository-owned stack layout compatibility shim used by
10    /// the upstream imgui-node-editor blueprints example. It is not part of the
11    /// official Dear ImGui API.
12    pub struct HorizontalStackLayoutToken<'ui>;
13
14    /// Ends the stack-layout horizontal group.
15    drop { unsafe { sys::ImGuiStack_EndHorizontal() } }
16);
17
18create_token!(
19    /// Tracks a stack-layout vertical group.
20    ///
21    /// This wraps the repository-owned stack layout compatibility shim used by
22    /// the upstream imgui-node-editor blueprints example. It is not part of the
23    /// official Dear ImGui API.
24    pub struct VerticalStackLayoutToken<'ui>;
25
26    /// Ends the stack-layout vertical group.
27    drop { unsafe { sys::ImGuiStack_EndVertical() } }
28);
29
30create_token!(
31    /// Tracks a suspended stack layout and resumes it on drop.
32    pub struct StackLayoutSuspensionToken<'ui>;
33
34    /// Resumes a suspended stack layout.
35    drop { unsafe { sys::ImGuiStack_ResumeLayout() } }
36);
37
38/// Identifier accepted by the stack layout compatibility helpers.
39///
40/// The pointer form mirrors the upstream `BeginHorizontal(id.AsPointer())`
41/// usage from `imgui-node-editor`; the pointer is used only as an ID and is not
42/// dereferenced.
43#[derive(Clone, Copy, Debug)]
44pub enum StackLayoutId<'a> {
45    Str(&'a str),
46    Ptr(*const c_void),
47    Int(i32),
48    Raw(Id),
49}
50
51impl<'a> StackLayoutId<'a> {
52    /// Construct an ID from a pointer value.
53    #[inline]
54    pub const fn ptr(ptr: *const c_void) -> Self {
55        Self::Ptr(ptr)
56    }
57
58    /// Construct an ID from a pointer-sized integer, matching upstream
59    /// `NodeId::AsPointer()` / `PinId::AsPointer()` usage.
60    #[inline]
61    pub const fn pointer_value(value: usize) -> Self {
62        Self::Ptr(value as *const c_void)
63    }
64}
65
66impl<'a> From<&'a str> for StackLayoutId<'a> {
67    #[inline]
68    fn from(value: &'a str) -> Self {
69        Self::Str(value)
70    }
71}
72
73impl From<i32> for StackLayoutId<'_> {
74    #[inline]
75    fn from(value: i32) -> Self {
76        Self::Int(value)
77    }
78}
79
80impl From<Id> for StackLayoutId<'_> {
81    #[inline]
82    fn from(value: Id) -> Self {
83        Self::Raw(value)
84    }
85}
86
87impl Ui {
88    /// Starts a stack-layout horizontal group.
89    ///
90    /// This is a compatibility helper for examples and utilities that follow
91    /// `imgui-node-editor`'s blueprint builder. It is backed by a local shim,
92    /// because Dear ImGui itself does not ship `BeginHorizontal`.
93    #[doc(alias = "BeginHorizontal")]
94    pub fn begin_horizontal_stack_layout<'ui, 'id>(
95        &'ui self,
96        id: impl Into<StackLayoutId<'id>>,
97        size: impl Into<[f32; 2]>,
98        align: f32,
99    ) -> HorizontalStackLayoutToken<'ui> {
100        let size = size.into();
101        assert_finite_vec2("Ui::begin_horizontal_stack_layout()", "size", size);
102        assert!(
103            align.is_finite(),
104            "Ui::begin_horizontal_stack_layout() align must be finite"
105        );
106        let size = sys::ImVec2::from(size);
107        unsafe {
108            match id.into() {
109                StackLayoutId::Str(value) => {
110                    sys::ImGuiStack_BeginHorizontal_Str(self.scratch_txt(value), size, align);
111                }
112                StackLayoutId::Ptr(value) => {
113                    sys::ImGuiStack_BeginHorizontal_Ptr(value, size, align);
114                }
115                StackLayoutId::Int(value) => {
116                    sys::ImGuiStack_BeginHorizontal_Int(value, size, align);
117                }
118                StackLayoutId::Raw(value) => {
119                    sys::ImGuiStack_BeginHorizontal_Id(value.raw(), size, align);
120                }
121            }
122        }
123        HorizontalStackLayoutToken::new(self)
124    }
125
126    /// Starts a stack-layout horizontal group.
127    ///
128    /// Alias for [`Self::begin_horizontal_stack_layout`] with the upstream
129    /// naming used by imgui-node-editor examples.
130    #[doc(alias = "BeginHorizontal")]
131    pub fn begin_horizontal<'ui, 'id>(
132        &'ui self,
133        id: impl Into<StackLayoutId<'id>>,
134        size: impl Into<[f32; 2]>,
135        align: f32,
136    ) -> HorizontalStackLayoutToken<'ui> {
137        self.begin_horizontal_stack_layout(id, size, align)
138    }
139
140    /// Runs a closure inside a stack-layout horizontal group.
141    #[doc(alias = "BeginHorizontal", alias = "EndHorizontal")]
142    pub fn horizontal_stack_layout<'id, R>(
143        &self,
144        id: impl Into<StackLayoutId<'id>>,
145        size: impl Into<[f32; 2]>,
146        align: f32,
147        f: impl FnOnce() -> R,
148    ) -> R {
149        let token = self.begin_horizontal_stack_layout(id, size, align);
150        let result = f();
151        token.end();
152        result
153    }
154
155    /// Runs a closure inside a stack-layout horizontal group.
156    ///
157    /// Alias for [`Self::horizontal_stack_layout`].
158    #[doc(alias = "BeginHorizontal", alias = "EndHorizontal")]
159    pub fn horizontal<'id, R>(
160        &self,
161        id: impl Into<StackLayoutId<'id>>,
162        size: impl Into<[f32; 2]>,
163        align: f32,
164        f: impl FnOnce() -> R,
165    ) -> R {
166        self.horizontal_stack_layout(id, size, align, f)
167    }
168
169    /// Starts a stack-layout vertical group.
170    ///
171    /// This is a compatibility helper for examples and utilities that follow
172    /// `imgui-node-editor`'s blueprint builder. It is backed by a local shim,
173    /// because Dear ImGui itself does not ship `BeginVertical`.
174    #[doc(alias = "BeginVertical")]
175    pub fn begin_vertical_stack_layout<'ui, 'id>(
176        &'ui self,
177        id: impl Into<StackLayoutId<'id>>,
178        size: impl Into<[f32; 2]>,
179        align: f32,
180    ) -> VerticalStackLayoutToken<'ui> {
181        let size = size.into();
182        assert_finite_vec2("Ui::begin_vertical_stack_layout()", "size", size);
183        assert!(
184            align.is_finite(),
185            "Ui::begin_vertical_stack_layout() align must be finite"
186        );
187        let size = sys::ImVec2::from(size);
188        unsafe {
189            match id.into() {
190                StackLayoutId::Str(value) => {
191                    sys::ImGuiStack_BeginVertical_Str(self.scratch_txt(value), size, align);
192                }
193                StackLayoutId::Ptr(value) => {
194                    sys::ImGuiStack_BeginVertical_Ptr(value, size, align);
195                }
196                StackLayoutId::Int(value) => {
197                    sys::ImGuiStack_BeginVertical_Int(value, size, align);
198                }
199                StackLayoutId::Raw(value) => {
200                    sys::ImGuiStack_BeginVertical_Id(value.raw(), size, align);
201                }
202            }
203        }
204        VerticalStackLayoutToken::new(self)
205    }
206
207    /// Starts a stack-layout vertical group.
208    ///
209    /// Alias for [`Self::begin_vertical_stack_layout`] with the upstream naming
210    /// used by imgui-node-editor examples.
211    #[doc(alias = "BeginVertical")]
212    pub fn begin_vertical<'ui, 'id>(
213        &'ui self,
214        id: impl Into<StackLayoutId<'id>>,
215        size: impl Into<[f32; 2]>,
216        align: f32,
217    ) -> VerticalStackLayoutToken<'ui> {
218        self.begin_vertical_stack_layout(id, size, align)
219    }
220
221    /// Runs a closure inside a stack-layout vertical group.
222    #[doc(alias = "BeginVertical", alias = "EndVertical")]
223    pub fn vertical_stack_layout<'id, R>(
224        &self,
225        id: impl Into<StackLayoutId<'id>>,
226        size: impl Into<[f32; 2]>,
227        align: f32,
228        f: impl FnOnce() -> R,
229    ) -> R {
230        let token = self.begin_vertical_stack_layout(id, size, align);
231        let result = f();
232        token.end();
233        result
234    }
235
236    /// Runs a closure inside a stack-layout vertical group.
237    ///
238    /// Alias for [`Self::vertical_stack_layout`].
239    #[doc(alias = "BeginVertical", alias = "EndVertical")]
240    pub fn vertical<'id, R>(
241        &self,
242        id: impl Into<StackLayoutId<'id>>,
243        size: impl Into<[f32; 2]>,
244        align: f32,
245        f: impl FnOnce() -> R,
246    ) -> R {
247        self.vertical_stack_layout(id, size, align, f)
248    }
249
250    /// Inserts a spring into the current stack layout.
251    ///
252    /// `weight <= 0.0` reserves only spacing. `spacing < 0.0` uses the current
253    /// style item spacing along the layout axis, matching the upstream stack
254    /// layout extension semantics.
255    #[doc(alias = "Spring")]
256    pub fn stack_layout_spring(&self, weight: f32, spacing: f32) {
257        assert!(
258            weight.is_finite(),
259            "Ui::stack_layout_spring() weight must be finite"
260        );
261        assert!(
262            spacing.is_finite(),
263            "Ui::stack_layout_spring() spacing must be finite"
264        );
265        unsafe { sys::ImGuiStack_Spring(weight, spacing) }
266    }
267
268    /// Inserts a spring into the current stack layout.
269    ///
270    /// Alias for [`Self::stack_layout_spring`] with the upstream naming used by
271    /// imgui-node-editor examples.
272    #[doc(alias = "Spring")]
273    pub fn spring(&self, weight: f32, spacing: f32) {
274        self.stack_layout_spring(weight, spacing)
275    }
276
277    /// Suspends the current stack layout until the returned token is dropped.
278    #[doc(alias = "SuspendLayout")]
279    pub fn suspend_stack_layout(&self) -> StackLayoutSuspensionToken<'_> {
280        unsafe { sys::ImGuiStack_SuspendLayout() };
281        StackLayoutSuspensionToken::new(self)
282    }
283}