arcdps_imgui/
stacks.rs

1use std::mem;
2use std::os::raw::{c_char, c_void};
3use std::ptr;
4use std::thread;
5
6use crate::context::Context;
7use crate::fonts::atlas::FontId;
8use crate::internal::RawCast;
9use crate::style::{StyleColor, StyleVar};
10use crate::sys;
11use crate::{Id, Ui};
12
13/// # Parameter stacks (shared)
14impl<'ui> Ui<'ui> {
15    /// Switches to the given font by pushing it to the font stack.
16    ///
17    /// Returns a `FontStackToken` that must be popped by calling `.pop()`
18    ///
19    /// # Panics
20    ///
21    /// Panics if the font atlas does not contain the given font
22    ///
23    /// # Examples
24    ///
25    /// ```no_run
26    /// # use arcdps_imgui::*;
27    /// # let mut ctx = Context::create();
28    /// # let font_data_sources = [];
29    /// // At initialization time
30    /// let my_custom_font = ctx.fonts().add_font(&font_data_sources);
31    /// # let ui = ctx.frame();
32    /// // During UI construction
33    /// let font = ui.push_font(my_custom_font);
34    /// ui.text("I use the custom font!");
35    /// font.pop();
36    /// ```
37    #[doc(alias = "PushFont")]
38    pub fn push_font(&self, id: FontId) -> FontStackToken<'_> {
39        let fonts = self.fonts();
40        let font = fonts
41            .get_font(id)
42            .expect("Font atlas did not contain the given font");
43        unsafe { sys::igPushFont(font.raw() as *const _ as *mut _) };
44        FontStackToken::new(self)
45    }
46    /// Changes a style color by pushing a change to the color stack.
47    ///
48    /// Returns a `ColorStackToken` that must be popped by calling `.pop()`
49    ///
50    /// # Examples
51    ///
52    /// ```no_run
53    /// # use arcdps_imgui::*;
54    /// # let mut ctx = Context::create();
55    /// # let ui = ctx.frame();
56    /// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
57    /// let color = ui.push_style_color(StyleColor::Text, RED);
58    /// ui.text("I'm red!");
59    /// color.pop();
60    /// ```
61    #[doc(alias = "PushStyleColorVec4")]
62    pub fn push_style_color(
63        &self,
64        style_color: StyleColor,
65        color: [f32; 4],
66    ) -> ColorStackToken<'_> {
67        unsafe { sys::igPushStyleColor_Vec4(style_color as i32, color.into()) };
68        ColorStackToken::new(self)
69    }
70
71    /// Changes style colors by pushing several changes to the color stack.
72    ///
73    /// Returns a `ColorStackToken` that must be popped by calling `.pop()`
74    ///
75    /// # Examples
76    ///
77    /// ```no_run
78    /// # use arcdps_imgui::*;
79    /// # let mut ctx = Context::create();
80    /// # let ui = ctx.frame();
81    /// const RED: [f32; 4] = [1.0, 0.0, 0.0, 1.0];
82    /// const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
83    /// let colors = ui.push_style_colors(&[
84    ///     (StyleColor::Text, RED),
85    ///     (StyleColor::TextDisabled, GREEN),
86    /// ]);
87    /// ui.text("I'm red!");
88    /// ui.text_disabled("I'm green!");
89    /// colors.pop(&ui);
90    /// ```
91    #[deprecated = "deprecated in 0.7.0. Use `push_style_color` multiple times for similar effect."]
92    pub fn push_style_colors<'a, I>(&self, style_colors: I) -> MultiColorStackToken
93    where
94        I: IntoIterator<Item = &'a (StyleColor, [f32; 4])>,
95    {
96        let mut count = 0;
97        for &(style_color, color) in style_colors {
98            unsafe { sys::igPushStyleColor_Vec4(style_color as i32, color.into()) };
99            count += 1;
100        }
101        MultiColorStackToken {
102            count,
103            ctx: self.ctx,
104        }
105    }
106    /// Changes a style variable by pushing a change to the style stack.
107    ///
108    /// Returns a `StyleStackToken` that can be popped by calling `.end()`
109    /// or by allowing to drop.
110    ///
111    /// # Examples
112    ///
113    /// ```no_run
114    /// # use arcdps_imgui::*;
115    /// # let mut ctx = Context::create();
116    /// # let ui = ctx.frame();
117    /// let style = ui.push_style_var(StyleVar::Alpha(0.2));
118    /// ui.text("I'm transparent!");
119    /// style.pop();
120    /// ```
121    #[doc(alias = "PushStyleVar")]
122    pub fn push_style_var(&self, style_var: StyleVar) -> StyleStackToken<'_> {
123        unsafe { push_style_var(style_var) };
124        StyleStackToken::new(self)
125    }
126    /// Changes style variables by pushing several changes to the style stack.
127    ///
128    /// Returns a `StyleStackToken` that must be popped by calling `.pop()`
129    ///
130    /// # Examples
131    ///
132    /// ```no_run
133    /// # use arcdps_imgui::*;
134    /// # let mut ctx = Context::create();
135    /// # let ui = ctx.frame();
136    /// let styles = ui.push_style_vars(&[
137    ///     StyleVar::Alpha(0.2),
138    ///     StyleVar::ItemSpacing([50.0, 50.0])
139    /// ]);
140    /// ui.text("We're transparent...");
141    /// ui.text("...with large spacing as well");
142    /// styles.pop(&ui);
143    /// ```
144    #[deprecated = "deprecated in 0.7.0. Use `push_style_var` multiple times for similar effect."]
145    pub fn push_style_vars<'a, I>(&self, style_vars: I) -> MultiStyleStackToken
146    where
147        I: IntoIterator<Item = &'a StyleVar>,
148    {
149        let mut count = 0;
150        for &style_var in style_vars {
151            unsafe { push_style_var(style_var) };
152            count += 1;
153        }
154        MultiStyleStackToken {
155            count,
156            ctx: self.ctx,
157        }
158    }
159}
160
161create_token!(
162    /// Tracks a font pushed to the font stack that can be popped by calling `.end()`
163    /// or by dropping.
164    pub struct FontStackToken<'ui>;
165
166    /// Pops a change from the font stack.
167    drop { sys::igPopFont() }
168);
169
170impl FontStackToken<'_> {
171    /// Pops a change from the font stack.
172    pub fn pop(self) {
173        self.end()
174    }
175}
176
177create_token!(
178    /// Tracks a color pushed to the color stack that can be popped by calling `.end()`
179    /// or by dropping.
180    pub struct ColorStackToken<'ui>;
181
182    /// Pops a change from the color stack.
183    drop { sys::igPopStyleColor(1) }
184);
185
186impl ColorStackToken<'_> {
187    /// Pops a change from the color stack.
188    pub fn pop(self) {
189        self.end()
190    }
191}
192
193/// Tracks one or more changes pushed to the color stack that must be popped by calling `.pop()`
194#[must_use]
195pub struct MultiColorStackToken {
196    count: usize,
197    ctx: *const Context,
198}
199
200impl MultiColorStackToken {
201    /// Pops changes from the color stack
202    #[doc(alias = "PopStyleColor")]
203    pub fn pop(mut self, _: &Ui<'_>) {
204        self.ctx = ptr::null();
205        unsafe { sys::igPopStyleColor(self.count as i32) };
206    }
207}
208
209impl Drop for MultiColorStackToken {
210    fn drop(&mut self) {
211        if !self.ctx.is_null() && !thread::panicking() {
212            panic!("A ColorStackToken was leaked. Did you call .pop()?");
213        }
214    }
215}
216
217create_token!(
218    /// Tracks a style pushed to the style stack that can be popped by calling `.end()`
219    /// or by dropping.
220    pub struct StyleStackToken<'ui>;
221
222    /// Pops a change from the style stack.
223    drop { sys::igPopStyleVar(1) }
224);
225
226impl StyleStackToken<'_> {
227    /// Pops a change from the style stack.
228    pub fn pop(self) {
229        self.end()
230    }
231}
232
233/// Tracks one or more changes pushed to the style stack that must be popped by calling `.pop()`
234#[must_use]
235pub struct MultiStyleStackToken {
236    count: usize,
237    ctx: *const Context,
238}
239
240impl MultiStyleStackToken {
241    /// Pops changes from the style stack
242    #[doc(alias = "PopStyleVar")]
243    pub fn pop(mut self, _: &Ui<'_>) {
244        self.ctx = ptr::null();
245        unsafe { sys::igPopStyleVar(self.count as i32) };
246    }
247}
248
249impl Drop for MultiStyleStackToken {
250    fn drop(&mut self) {
251        if !self.ctx.is_null() && !thread::panicking() {
252            panic!("A StyleStackToken was leaked. Did you call .pop()?");
253        }
254    }
255}
256
257#[inline]
258unsafe fn push_style_var(style_var: StyleVar) {
259    use crate::style::StyleVar::*;
260    use crate::sys::{igPushStyleVar_Float, igPushStyleVar_Vec2};
261    match style_var {
262        Alpha(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_Alpha as i32, v),
263        WindowPadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowPadding as i32, v.into()),
264        WindowRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_WindowRounding as i32, v),
265        WindowBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_WindowBorderSize as i32, v),
266        WindowMinSize(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowMinSize as i32, v.into()),
267        WindowTitleAlign(v) => {
268            igPushStyleVar_Vec2(sys::ImGuiStyleVar_WindowTitleAlign as i32, v.into())
269        }
270        ChildRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ChildRounding as i32, v),
271        ChildBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ChildBorderSize as i32, v),
272        PopupRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_PopupRounding as i32, v),
273        PopupBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_PopupBorderSize as i32, v),
274        FramePadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_FramePadding as i32, v.into()),
275        FrameRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_FrameRounding as i32, v),
276        FrameBorderSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_FrameBorderSize as i32, v),
277        ItemSpacing(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemSpacing as i32, v.into()),
278        ItemInnerSpacing(v) => {
279            igPushStyleVar_Vec2(sys::ImGuiStyleVar_ItemInnerSpacing as i32, v.into())
280        }
281        IndentSpacing(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_IndentSpacing as i32, v),
282        ScrollbarSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarSize as i32, v),
283        ScrollbarRounding(v) => {
284            igPushStyleVar_Float(sys::ImGuiStyleVar_ScrollbarRounding as i32, v)
285        }
286        GrabMinSize(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_GrabMinSize as i32, v),
287        GrabRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_GrabRounding as i32, v),
288        TabRounding(v) => igPushStyleVar_Float(sys::ImGuiStyleVar_TabRounding as i32, v),
289        ButtonTextAlign(v) => {
290            igPushStyleVar_Vec2(sys::ImGuiStyleVar_ButtonTextAlign as i32, v.into())
291        }
292        SelectableTextAlign(v) => {
293            igPushStyleVar_Vec2(sys::ImGuiStyleVar_SelectableTextAlign as i32, v.into())
294        }
295        CellPadding(v) => igPushStyleVar_Vec2(sys::ImGuiStyleVar_CellPadding as i32, v.into())
296    }
297}
298
299/// # Parameter stacks (current window)
300impl<'ui> Ui<'ui> {
301    /// Changes the item width by pushing a change to the item width stack.
302    ///
303    /// Returns an `ItemWidthStackToken` that may be popped by calling `.pop()`
304    ///
305    /// - `> 0.0`: width is `item_width` pixels
306    /// - `= 0.0`: default to ~2/3 of window width
307    /// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
308    /// the right side)
309    #[doc(alias = "PushItemWith")]
310    pub fn push_item_width(&self, item_width: f32) -> ItemWidthStackToken {
311        unsafe { sys::igPushItemWidth(item_width) };
312        ItemWidthStackToken { _ctx: self.ctx }
313    }
314    /// Sets the width of the next item.
315    ///
316    /// - `> 0.0`: width is `item_width` pixels
317    /// - `= 0.0`: default to ~2/3 of window width
318    /// - `< 0.0`: `item_width` pixels relative to the right of window (-1.0 always aligns width to
319    /// the right side)
320    #[doc(alias = "SetNextItemWidth")]
321    pub fn set_next_item_width(&self, item_width: f32) {
322        unsafe { sys::igSetNextItemWidth(item_width) };
323    }
324    /// Returns the width of the item given the pushed settings and the current cursor position.
325    ///
326    /// This is NOT necessarily the width of last item.
327    #[doc(alias = "CalcItemWidth")]
328    pub fn calc_item_width(&self) -> f32 {
329        unsafe { sys::igCalcItemWidth() }
330    }
331
332    /// Changes the text wrapping position to the end of window (or column), which
333    /// is generally the default.
334    ///
335    /// This is the same as calling [push_text_wrap_pos_with_pos](Self::push_text_wrap_pos_with_pos)
336    /// with `wrap_pos_x` set to 0.0.
337    ///
338    /// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()`
339    #[doc(alias = "PushTextWrapPos")]
340    pub fn push_text_wrap_pos(&self) -> TextWrapPosStackToken {
341        self.push_text_wrap_pos_with_pos(0.0)
342    }
343
344    /// Changes the text wrapping position by pushing a change to the text wrapping position stack.
345    ///
346    /// Returns a `TextWrapPosStackToken` that may be popped by calling `.pop()`
347    ///
348    /// - `> 0.0`: wrap at `wrap_pos_x` position in window local space
349    /// - `= 0.0`: wrap to end of window (or column)
350    /// - `< 0.0`: no wrapping
351    #[doc(alias = "PushTextWrapPos")]
352    pub fn push_text_wrap_pos_with_pos(&self, wrap_pos_x: f32) -> TextWrapPosStackToken {
353        unsafe { sys::igPushTextWrapPos(wrap_pos_x) };
354        TextWrapPosStackToken { _ctx: self.ctx }
355    }
356
357    /// Changes an item flag by pushing a change to the item flag stack.
358    ///
359    /// Returns a `ItemFlagsStackToken` that may be popped by calling `.pop()`
360    #[doc(alias = "PushItemFlag")]
361    pub fn push_item_flag(&self, item_flag: ItemFlag) -> ItemFlagsStackToken {
362        use self::ItemFlag::*;
363        match item_flag {
364            AllowKeyboardFocus(v) => unsafe { sys::igPushAllowKeyboardFocus(v) },
365            ButtonRepeat(v) => unsafe { sys::igPushButtonRepeat(v) },
366        }
367        ItemFlagsStackToken {
368            discriminant: mem::discriminant(&item_flag),
369            _ctx: self.ctx,
370        }
371    }
372}
373
374/// A temporary change in item flags
375#[derive(Copy, Clone, Debug, PartialEq)]
376pub enum ItemFlag {
377    AllowKeyboardFocus(bool),
378    ButtonRepeat(bool),
379}
380
381pub struct ItemWidthStackToken {
382    _ctx: *const Context,
383}
384
385impl ItemWidthStackToken {
386    /// Pops a change from the item width stack
387    #[doc(alias = "PopItemWidth")]
388    pub fn pop(mut self, _: &Ui<'_>) {
389        self._ctx = ptr::null();
390        unsafe { sys::igPopItemWidth() };
391    }
392}
393
394/// Tracks a change pushed to the text wrap position stack
395pub struct TextWrapPosStackToken {
396    _ctx: *const Context,
397}
398
399impl TextWrapPosStackToken {
400    /// Pops a change from the text wrap position stack
401    #[doc(alias = "PopTextWrapPos")]
402    pub fn pop(mut self, _: &Ui<'_>) {
403        self._ctx = ptr::null();
404        unsafe { sys::igPopTextWrapPos() };
405    }
406}
407
408/// Tracks a change pushed to the item flags stack
409pub struct ItemFlagsStackToken {
410    discriminant: mem::Discriminant<ItemFlag>,
411    _ctx: *const Context,
412}
413
414impl ItemFlagsStackToken {
415    /// Pops a change from the item flags stack
416
417    #[doc(alias = "PopAllowKeyboardFocus", alias = "PopButtonRepeat")]
418    pub fn pop(mut self, _: &Ui<'_>) {
419        self._ctx = ptr::null();
420        const ALLOW_KEYBOARD_FOCUS: ItemFlag = ItemFlag::AllowKeyboardFocus(true);
421        const BUTTON_REPEAT: ItemFlag = ItemFlag::ButtonRepeat(true);
422
423        if self.discriminant == mem::discriminant(&ALLOW_KEYBOARD_FOCUS) {
424            unsafe { sys::igPopAllowKeyboardFocus() };
425        } else if self.discriminant == mem::discriminant(&BUTTON_REPEAT) {
426            unsafe { sys::igPopButtonRepeat() };
427        } else {
428            unreachable!();
429        }
430    }
431}
432
433create_token!(
434    /// Tracks an ID pushed to the ID stack that can be popped by calling `.pop()`
435    /// or by dropping.
436    pub struct IdStackToken<'ui>;
437
438    /// Pops a change from the ID stack
439    drop { sys::igPopID() }
440);
441
442impl IdStackToken<'_> {
443    /// Pops a change from the ID stack
444    pub fn pop(self) {
445        self.end()
446    }
447}
448
449/// # ID stack
450impl<'ui> Ui<'ui> {
451    /// Pushes an identifier to the ID stack.
452    ///
453    /// Returns an `IdStackToken` that can be popped by calling `.end()`
454    /// or by dropping manually.
455    #[doc(alias = "PushId")]
456    pub fn push_id<'a, I: Into<Id<'a>>>(&self, id: I) -> IdStackToken<'ui> {
457        let id = id.into();
458
459        unsafe {
460            match id {
461                Id::Int(i) => sys::igPushID_Int(i),
462                Id::Str(s) => {
463                    let start = s.as_ptr() as *const c_char;
464                    let end = start.add(s.len());
465                    sys::igPushID_StrStr(start, end)
466                }
467                Id::Ptr(p) => sys::igPushID_Ptr(p as *const c_void),
468            }
469        }
470        IdStackToken::new(self)
471    }
472}