Skip to main content

dear_imgui_rs/
columns.rs

1//! Legacy columns API
2//!
3//! Thin wrappers for the old Columns layout system. New code should prefer
4//! the `table` API (`widget::table`) which supersedes Columns with more
5//! features and better user experience.
6//!
7#![allow(
8    clippy::cast_possible_truncation,
9    clippy::cast_sign_loss,
10    clippy::as_conversions,
11    clippy::unnecessary_cast
12)]
13use crate::Ui;
14use crate::sys;
15use bitflags::bitflags;
16
17bitflags! {
18    /// Flags for old columns system
19    #[repr(transparent)]
20    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21    pub struct OldColumnFlags: i32 {
22        /// No flags
23        const NONE = sys::ImGuiOldColumnFlags_None as i32;
24        /// Disable column dividers
25        const NO_BORDER = sys::ImGuiOldColumnFlags_NoBorder as i32;
26        /// Disable resizing columns by dragging dividers
27        const NO_RESIZE = sys::ImGuiOldColumnFlags_NoResize as i32;
28        /// Disable column width preservation when the total width changes
29        const NO_PRESERVE_WIDTHS = sys::ImGuiOldColumnFlags_NoPreserveWidths as i32;
30        /// Disable forcing columns to fit within window
31        const NO_FORCE_WITHIN_WINDOW = sys::ImGuiOldColumnFlags_NoForceWithinWindow as i32;
32        /// Restore pre-1.51 behavior of extending the parent window contents size
33        const GROW_PARENT_CONTENTS_SIZE = sys::ImGuiOldColumnFlags_GrowParentContentsSize as i32;
34    }
35}
36
37impl Default for OldColumnFlags {
38    fn default() -> Self {
39        OldColumnFlags::NONE
40    }
41}
42
43/// # Columns
44impl Ui {
45    /// Creates columns layout.
46    ///
47    /// # Arguments
48    /// * `count` - Number of columns (must be >= 1)
49    /// * `id` - Optional ID for the columns (can be empty string)
50    /// * `border` - Whether to draw borders between columns
51    #[doc(alias = "Columns")]
52    pub fn columns(&self, count: i32, id: impl AsRef<str>, border: bool) {
53        unsafe { sys::igColumns(count, self.scratch_txt(id), border) }
54    }
55
56    /// Begin columns layout with advanced flags.
57    ///
58    /// # Arguments
59    /// * `id` - ID for the columns
60    /// * `count` - Number of columns (must be >= 1)
61    /// * `flags` - Column flags
62    #[doc(alias = "BeginColumns")]
63    pub fn begin_columns(&self, id: impl AsRef<str>, count: i32, flags: OldColumnFlags) {
64        unsafe { sys::igBeginColumns(self.scratch_txt(id), count, flags.bits()) }
65    }
66
67    /// Begin columns layout with advanced flags and return a token that ends columns on drop.
68    #[doc(alias = "BeginColumns")]
69    pub fn begin_columns_token(
70        &self,
71        id: impl AsRef<str>,
72        count: i32,
73        flags: OldColumnFlags,
74    ) -> ColumnsToken<'_> {
75        self.begin_columns(id, count, flags);
76        ColumnsToken { ui: self }
77    }
78
79    /// End columns layout.
80    #[doc(alias = "EndColumns")]
81    pub fn end_columns(&self) {
82        unsafe { sys::igEndColumns() }
83    }
84
85    /// Switches to the next column.
86    ///
87    /// If the current row is finished, switches to first column of the next row
88    #[doc(alias = "NextColumn")]
89    pub fn next_column(&self) {
90        unsafe { sys::igNextColumn() }
91    }
92
93    /// Returns the index of the current column
94    #[doc(alias = "GetColumnIndex")]
95    pub fn current_column_index(&self) -> i32 {
96        unsafe { sys::igGetColumnIndex() }
97    }
98
99    /// Returns the width of the current column (in pixels)
100    #[doc(alias = "GetColumnWidth")]
101    pub fn current_column_width(&self) -> f32 {
102        unsafe { sys::igGetColumnWidth(-1) }
103    }
104
105    /// Returns the width of the given column (in pixels)
106    #[doc(alias = "GetColumnWidth")]
107    pub fn column_width(&self, column_index: i32) -> f32 {
108        unsafe { sys::igGetColumnWidth(column_index) }
109    }
110
111    /// Sets the width of the current column (in pixels)
112    #[doc(alias = "SetColumnWidth")]
113    pub fn set_current_column_width(&self, width: f32) {
114        unsafe { sys::igSetColumnWidth(-1, width) };
115    }
116
117    /// Sets the width of the given column (in pixels)
118    #[doc(alias = "SetColumnWidth")]
119    pub fn set_column_width(&self, column_index: i32, width: f32) {
120        unsafe { sys::igSetColumnWidth(column_index, width) };
121    }
122
123    /// Returns the offset of the current column (in pixels from the left side of the content region)
124    #[doc(alias = "GetColumnOffset")]
125    pub fn current_column_offset(&self) -> f32 {
126        unsafe { sys::igGetColumnOffset(-1) }
127    }
128
129    /// Returns the offset of the given column (in pixels from the left side of the content region)
130    #[doc(alias = "GetColumnOffset")]
131    pub fn column_offset(&self, column_index: i32) -> f32 {
132        unsafe { sys::igGetColumnOffset(column_index) }
133    }
134
135    /// Sets the offset of the current column (in pixels from the left side of the content region)
136    #[doc(alias = "SetColumnOffset")]
137    pub fn set_current_column_offset(&self, offset_x: f32) {
138        unsafe { sys::igSetColumnOffset(-1, offset_x) };
139    }
140
141    /// Sets the offset of the given column (in pixels from the left side of the content region)
142    #[doc(alias = "SetColumnOffset")]
143    pub fn set_column_offset(&self, column_index: i32, offset_x: f32) {
144        unsafe { sys::igSetColumnOffset(column_index, offset_x) };
145    }
146
147    /// Returns the current amount of columns
148    #[doc(alias = "GetColumnsCount")]
149    pub fn column_count(&self) -> i32 {
150        unsafe { sys::igGetColumnsCount() }
151    }
152
153    // ============================================================================
154    // Advanced column utilities
155    // ============================================================================
156
157    /// Push column clip rect for the given column index.
158    /// This is useful for custom drawing within columns.
159    #[doc(alias = "PushColumnClipRect")]
160    pub fn push_column_clip_rect(&self, column_index: i32) {
161        unsafe { sys::igPushColumnClipRect(column_index) }
162    }
163
164    /// Push columns background for drawing.
165    #[doc(alias = "PushColumnsBackground")]
166    pub fn push_columns_background(&self) {
167        unsafe { sys::igPushColumnsBackground() }
168    }
169
170    /// Pop columns background.
171    #[doc(alias = "PopColumnsBackground")]
172    pub fn pop_columns_background(&self) {
173        unsafe { sys::igPopColumnsBackground() }
174    }
175
176    /// Get columns ID for the given string ID and count.
177    #[doc(alias = "GetColumnsID")]
178    pub fn get_columns_id(&self, str_id: impl AsRef<str>, count: i32) -> u32 {
179        unsafe { sys::igGetColumnsID(self.scratch_txt(str_id), count) }
180    }
181
182    // ============================================================================
183    // Column state utilities
184    // ============================================================================
185
186    /// Check if any column in the current legacy columns set is being resized.
187    ///
188    /// Returns `false` when the current window is not inside a legacy columns set.
189    pub fn is_any_column_resizing(&self) -> bool {
190        unsafe {
191            let window = sys::igGetCurrentWindowRead();
192            if window.is_null() {
193                return false;
194            }
195
196            let columns = (*window).DC.CurrentColumns;
197            if columns.is_null() {
198                return false;
199            }
200
201            (*columns).IsBeingResized
202        }
203    }
204
205    /// Get the total width of all columns.
206    pub fn get_columns_total_width(&self) -> f32 {
207        let count = self.column_count();
208        if count <= 0 {
209            return 0.0;
210        }
211
212        let mut total_width = 0.0;
213        for i in 0..count {
214            total_width += self.column_width(i);
215        }
216        total_width
217    }
218
219    /// Set all columns to equal width.
220    pub fn set_columns_equal_width(&self) {
221        let count = self.column_count();
222        if count <= 1 {
223            return;
224        }
225
226        let total_width = self.get_columns_total_width();
227        let equal_width = total_width / count as f32;
228
229        for i in 0..count {
230            self.set_column_width(i, equal_width);
231        }
232    }
233
234    /// Get column width as a percentage of total width.
235    pub fn get_column_width_percentage(&self, column_index: i32) -> f32 {
236        let total_width = self.get_columns_total_width();
237        if total_width <= 0.0 {
238            return 0.0;
239        }
240
241        let column_width = self.column_width(column_index);
242        (column_width / total_width) * 100.0
243    }
244
245    /// Set column width as a percentage of total width.
246    pub fn set_column_width_percentage(&self, column_index: i32, percentage: f32) {
247        let total_width = self.get_columns_total_width();
248        if total_width <= 0.0 {
249            return;
250        }
251
252        let new_width = (total_width * percentage) / 100.0;
253        self.set_column_width(column_index, new_width);
254    }
255}
256
257/// Token representing an active columns layout.
258#[must_use]
259pub struct ColumnsToken<'ui> {
260    ui: &'ui Ui,
261}
262
263impl Drop for ColumnsToken<'_> {
264    fn drop(&mut self) {
265        self.ui.end_columns();
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use super::OldColumnFlags;
272
273    #[test]
274    fn is_any_column_resizing_reads_current_columns_state() {
275        let mut ctx = crate::Context::create();
276        let _ = ctx.font_atlas_mut().build();
277        ctx.io_mut().set_display_size([128.0, 128.0]);
278        ctx.io_mut().set_delta_time(1.0 / 60.0);
279        let ui = ctx.frame();
280
281        ui.window("columns_resize_test").build(|| {
282            assert!(!ui.is_any_column_resizing());
283
284            let _columns = ui.begin_columns_token("legacy_columns", 2, OldColumnFlags::NONE);
285            let window = unsafe { crate::sys::igGetCurrentWindowRead() };
286            assert!(!window.is_null());
287
288            let columns = unsafe { (*window).DC.CurrentColumns };
289            assert!(!columns.is_null());
290            assert!(!ui.is_any_column_resizing());
291
292            unsafe {
293                (*columns).IsBeingResized = true;
294            }
295
296            assert!(ui.is_any_column_resizing());
297        });
298    }
299}