arcdps_imgui/
tables.rs

1use std::ffi::CStr;
2use std::marker::PhantomData;
3
4use bitflags::bitflags;
5
6use crate::sys;
7use crate::{Id, ImColor32, Ui};
8
9bitflags! {
10    /// Flags passed to `begin_table` methods.
11    ///
12    /// Important! Sizing policies have complex and subtle side effects, more so than you would expect.
13    /// Read comments/demos carefully + experiment with live demos to get acquainted with them.
14    /// - The DEFAULT sizing policies are:
15    ///    - Default to [SizingFixedFit]    if [ScrollX] is on, or if host window has (WindowFlags::AlwaysAutoResize)[crate::WindowFlags::AlwaysAutoResize].
16    ///    - Default to [SizingStretchSame] if [ScrollX] is off.
17    /// - When [ScrollX] is off:
18    ///    - Table defaults to [SizingStretchSame] -> all Columns defaults to [TableColumnFlags::WidthStretch] with same weight.
19    ///    - Columns sizing policy allowed: [Stretch] (default), [Fixed]/Auto.
20    ///    - [Fixed] Columns will generally obtain their requested width (unless the table cannot fit them all).
21    ///    - [Stretch] Columns will share the remaining width.
22    ///    - Mixed [Fixed]/[Stretch] columns is possible but has various side-effects on resizing behaviors.
23    ///      The typical use of mixing sizing policies is: any number of LEADING [Fixed] columns, followed by one or two TRAILING [Stretch] columns.
24    ///      (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
25    /// - When [ScrollX] is on:
26    ///    - Table defaults to [SizingFixedFit] -> all Columns defaults to [TableColumnFlags::WidthFixed]
27    ///    - Columns sizing policy allowed: [Fixed]/Auto mostly.
28    ///    - [Fixed] Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed.
29    ///    - When using auto-resizing (non-resizable) fixed columns, querying the content width to use item right-alignment e.g. SetNextItemWidth(-FLT_MIN) doesn't make sense, would create a feedback loop.
30    ///    - Using [Stretch] columns OFTEN DOES NOT MAKE SENSE if [ScrollX] is on, UNLESS you have specified a value for `inner_width` in BeginTable().
31    ///      If you specify a value for `inner_width` then effectively the scrolling space is known and [Stretch] or mixed [Fixed]/[Stretch] columns become meaningful again.
32    /// - Read on documentation at the top of imgui_tables.cpp for more details.
33    #[repr(transparent)]
34    pub struct TableFlags: u32 {
35        // Features
36
37        /// Enable resizing columns.
38        const RESIZABLE = sys::ImGuiTableFlags_Resizable;
39        /// Enable reordering columns in header row, though you must set up a header row
40        /// with `begin_table_header` or `table_setup_column`.
41        const REORDERABLE =sys::ImGuiTableFlags_Reorderable;
42        /// Enable hiding/disabling columns in context menu.
43        const HIDEABLE = sys::ImGuiTableFlags_Hideable;
44        /// Enable sorting. See `table_get_sort_specs` to object sort specs. Also see [SortMulti]
45        /// and [SortTristate].
46        const SORTABLE = sys::ImGuiTableFlags_Sortable;
47        /// Disable persisting columns order, width, and sort settings in the .ini file.
48        const NO_SAVED_SETTINGS = sys::ImGuiTableFlags_NoSavedSettings;
49        /// Right-click on columns body/contents will display table context menu.
50        /// By default you can only right click in a headers row.
51        const CONTEXT_MENU_IN_BODY = sys::ImGuiTableFlags_ContextMenuInBody;
52
53        // Decorations
54
55        /// Set each RowBg color with [table_row_bg] or [table_row_bg_alt] (equivalent of calling
56        /// `table_set_bg_color` with `ROW_BG0` on each row manually)
57        const ROW_BG = sys::ImGuiTableFlags_RowBg;
58        /// Draw horizontal borders between rows.
59        const BORDERS_INNER_H = sys::ImGuiTableFlags_BordersInnerH;
60        /// Draw horizontal borders at the top and bottom.
61        const BORDERS_OUTER_H = sys::ImGuiTableFlags_BordersOuterH;
62        /// Draw vertical borders between columns.
63        const BORDERS_INNER_V = sys::ImGuiTableFlags_BordersInnerV;
64        /// Draw vertical borders on the left and right sides.
65        const BORDERS_OUTER_V = sys::ImGuiTableFlags_BordersOuterV;
66        /// Draw all horizontal borders (this is just [BORDERS_INNER_H] | [BORDERS_OUTER_H]).
67        const BORDERS_H = sys::ImGuiTableFlags_BordersH;
68        /// Draw all vertical borders (this is just [BORDERS_INNER_V] | [BORDERS_OUTER_V]).
69        const BORDERS_V = sys::ImGuiTableFlags_BordersV;
70        /// Draw all inner borders (this is just [BORDERS_INNER_H] | [BORDERS_INNER_V]).
71        const BORDERS_INNER = sys::ImGuiTableFlags_BordersInner;
72        /// Draw all outer borders (this is just [BORDERS_OUTER_H] | [BORDERS_OUTER_V]).
73        const BORDERS_OUTER = sys::ImGuiTableFlags_BordersOuter;
74        /// Draw all borders (this is just [BORDERS_INNER] | [BORDERS_OUTER]).
75        const BORDERS = sys::ImGuiTableFlags_Borders;
76        /// **ALPHA** Disable vertical borders in columns Body (borders will always appears in Headers).
77        /// May move to Style
78        const NO_BORDERS_IN_BODY = sys::ImGuiTableFlags_NoBordersInBody;
79        /// **ALPHA** Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers).
80        /// May move to style
81        const NO_BORDERS_IN_BODY_UNTIL_RESIZE = sys::ImGuiTableFlags_NoBordersInBodyUntilResize;
82
83        // Sizing Policy (read above for defaults)
84
85        /// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable),
86        /// matching contents width.
87        const SIZING_FIXED_FIT = sys::ImGuiTableFlags_SizingFixedFit;
88        /// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable),
89        /// matching the maximum contents width of all columns.
90        /// Implicitly enable [NoKeepColumnsVisible].
91        const SIZING_FIXED_SAME = sys::ImGuiTableFlags_SizingFixedSame;
92        /// Columns default to [WidthStretch] with default weights proportional to each columns
93        /// contents widths.
94        const SIZING_STRETCH_PROP = sys::ImGuiTableFlags_SizingStretchProp;
95        /// Columns default to [WidthStretch] with default weights all equal, unless overridden by
96        /// a column's `TableHeader`.
97        const SIZING_STRETCH_SAME = sys::ImGuiTableFlags_SizingStretchSame;
98
99        // Sizing Extra Options
100
101        /// Make outer width auto-fit to columns, overriding outer_size.x value. Only available when
102        /// [ScrollX]/[ScrollY] are disabled and [Stretch] columns are not used.
103        const NO_HOST_EXTEND_X = sys::ImGuiTableFlags_NoHostExtendX;
104        /// Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).
105        /// Only available when [ScrollX]/[ScrollY] are disabled.
106        /// Data below the limit will be clipped and not visible.
107        const NO_HOST_EXTEND_Y = sys::ImGuiTableFlags_NoHostExtendY;
108        /// Disable keeping column always minimally visible when [ScrollX] is off and table
109        /// gets too small. Not recommended if columns are resizable.
110        const NO_KEEP_COLUMNS_VISIBLE = sys::ImGuiTableFlags_NoKeepColumnsVisible;
111        /// Disable distributing remainder width to stretched columns (width allocation on a 100-wide
112        /// table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33).
113        /// With larger number of columns, resizing will appear to be less smooth.
114        const PRECISE_WIDTHS = sys::ImGuiTableFlags_PreciseWidths;
115
116        // Clipping
117
118        /// Disable clipping rectangle for every individual columns (reduce draw command count, items will
119        /// be able to overflow into other columns). Generally incompatible with [table_setup_scroll_freeze].
120        const NO_CLIP = sys::ImGuiTableFlags_NoClip;
121
122        // Padding
123
124        /// Default if [BordersOuterV] is on. Enable outer-most padding. Generally desirable if you have headers.
125        const PAD_OUTER_X = sys::ImGuiTableFlags_PadOuterX;
126        /// Default if [BordersOuterV] is off. Disable outer-most padding.
127        const NO_PAD_OUTER_X = sys::ImGuiTableFlags_NoPadOuterX;
128        /// Disable inner padding between columns (double inner padding if [BordersOuterV] is on, single
129        /// inner padding if BordersOuterV is off).
130        const NO_PAD_INNER_X = sys::ImGuiTableFlags_NoPadInnerX;
131
132        // Scrolling
133
134        /// Enable horizontal scrolling. Require 'outer_size' parameter of [begin_table] to specify the
135        /// container size. Changes default sizing policy. Because this create a child window,
136        /// [ScrollY] is currently generally recommended when using [ScrollX].
137        const SCROLL_X = sys::ImGuiTableFlags_ScrollX;
138        /// Enable vertical scrolling. Require 'outer_size' parameter of [begin_table] to specify the
139        /// container size.
140        const SCROLL_Y = sys::ImGuiTableFlags_ScrollY;
141
142        // Sorting
143
144        /// Hold shift when clicking headers to sort on multiple column. [table_get_sort_specs] may return specs where `[spec_count] > 1`.
145        const SORT_MULTI = sys::ImGuiTableFlags_SortMulti;
146        /// Allow no sorting, disable default sorting. `table_get_sort_specs` may return specs where `[specs_count] == 0`.
147        const SORT_TRISTATE = sys::ImGuiTableFlags_SortTristate;
148    }
149}
150
151bitflags! {
152    /// Flags for [table_next_row_with_flags].
153    #[repr(transparent)]
154    pub struct TableRowFlags: u32 {
155        /// Identify header row (set default background color + width of its contents
156        /// accounted different for auto column width)
157        const HEADERS = sys::ImGuiTableRowFlags_Headers;
158    }
159}
160
161bitflags! {
162    /// Flags for [TableColumnSetup] and [table_setup_column_with].
163    #[repr(transparent)]
164    #[derive(Default)]
165    pub struct TableColumnFlags: u32 {
166        // Input configuration flags
167
168        /// Default as a hidden/disabled column.
169        const DEFAULT_HIDE = sys::ImGuiTableColumnFlags_DefaultHide;
170        /// Default as a sorting column.
171        const DEFAULT_SORT = sys::ImGuiTableColumnFlags_DefaultSort;
172        /// Column will stretch. Preferable with horizontal scrolling disabled (default
173        /// if table sizing policy is [ImGuiTableFlags::SizingStretchSame] or
174        /// [ImGuiTableFlags::SizingStretchProp]).
175        const WIDTH_STRETCH = sys::ImGuiTableColumnFlags_WidthStretch;
176        /// Column will not stretch. Preferable with horizontal scrolling enabled (default
177        /// if table sizing policy is [ImGuiTableFlags::SizingFixedFit] and table is resizable).
178        const WIDTH_FIXED = sys::ImGuiTableColumnFlags_WidthFixed;
179        /// Disable manual resizing.
180        const NO_RESIZE = sys::ImGuiTableColumnFlags_NoResize;
181        /// Disable manual reordering this column, this will also prevent other columns from
182        /// crossing over this column.
183        const NO_REORDER = sys::ImGuiTableColumnFlags_NoReorder;
184        /// Disable ability to hide/disable this column.
185        const NO_HIDE = sys::ImGuiTableColumnFlags_NoHide;
186        /// Disable clipping for this column (all [NO_CLIP] columns will render in a same
187        /// draw command).
188        const NO_CLIP = sys::ImGuiTableColumnFlags_NoClip;
189        /// Disable ability to sort on this field (even if [ImGuiTableFlags::Sortable] is
190        /// set on the table).
191        const NO_SORT = sys::ImGuiTableColumnFlags_NoSort;
192        /// Disable ability to sort in the ascending direction.
193        const NO_SORT_ASCENDING = sys::ImGuiTableColumnFlags_NoSortAscending;
194        /// Disable ability to sort in the descending direction.
195        const NO_SORT_DESCENDING = sys::ImGuiTableColumnFlags_NoSortDescending;
196        /// Disable header text width contribution to automatic column width.
197        const NO_HEADER_WIDTH = sys::ImGuiTableColumnFlags_NoHeaderWidth;
198        /// Make the initial sort direction Ascending when first sorting on this column (default).
199        const PREFER_SORT_ASCENDING = sys::ImGuiTableColumnFlags_PreferSortAscending;
200        /// Make the initial sort direction Descending when first sorting on this column.
201        const PREFER_SORT_DESCENDING = sys::ImGuiTableColumnFlags_PreferSortDescending;
202        /// Use current Indent value when entering cell (default for column 0).
203        const INDENT_ENABLE = sys::ImGuiTableColumnFlags_IndentEnable;
204        /// Ignore current Indent value when entering cell (default for columns > 0).
205        /// Indentation changes _within_ the cell will still be honored.
206        const INDENT_DISABLE = sys::ImGuiTableColumnFlags_IndentDisable;
207
208        // Output status flags, read-only via [table_get_column_flags]
209
210        /// Status: is enabled == not hidden by user/api (referred to as "Hide" in
211        /// [DefaultHide] and [NoHide]) flags.
212        const IS_ENABLED = sys::ImGuiTableColumnFlags_IsEnabled;
213        /// Status: is visible == is enabled AND not clipped by scrolling.
214        const IS_VISIBLE = sys::ImGuiTableColumnFlags_IsVisible;
215        /// Status: is currently part of the sort specs
216        const IS_SORTED = sys::ImGuiTableColumnFlags_IsSorted;
217        /// Status: is hovered by mouse
218        const IS_HOVERED = sys::ImGuiTableColumnFlags_IsHovered;
219    }
220}
221
222bitflags! {
223    /// Enum for [table_set_bg_color].
224    /// Background colors are rendering in 3 layers:
225    ///  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
226    ///  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
227    ///  - Layer 2: draw with CellBg color if set.
228    /// The purpose of the two row/columns layers is to let you decide if a background color
229    /// changes should override or blend with the existing color.
230    /// When using [TableFlags::RowBg] on the table, each row has the RowBg0 color automatically
231    /// set for odd/even rows.
232    /// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
233    /// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
234    #[repr(transparent)]
235    pub struct TableBgTarget: u32 {
236        /// Set row background color 0 (generally used for background, automatically set when
237        /// [TableFlags::RowBg] is used)
238        const ROW_BG0 = sys::ImGuiTableBgTarget_RowBg0;
239        /// Set row background color 1 (generally used for selection marking)
240        const ROW_BG1 = sys::ImGuiTableBgTarget_RowBg1;
241        /// Set cell background color (top-most color)
242        const CELL_BG = sys::ImGuiTableBgTarget_CellBg;
243    }
244}
245
246#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
247pub enum TableSortDirection {
248    Ascending,
249    Descending,
250}
251
252impl<'ui> Ui<'ui> {
253    /// Begins a table with no flags and with standard sizing contraints.
254    ///
255    /// This does no work on styling the headers (the top row) -- see either
256    /// [begin_table_header](Self::begin_table_header) or the more complex
257    /// [table_setup_column](Self::table_setup_column).
258    ///
259    /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
260    /// If this makes a difference to you, you are probably trying to make too many columns.
261    #[must_use = "if return is dropped immediately, table is ended immediately."]
262    pub fn begin_table(
263        &self,
264        str_id: impl AsRef<str>,
265        column_count: usize,
266    ) -> Option<TableToken<'ui>> {
267        self.begin_table_with_flags(str_id, column_count, TableFlags::empty())
268    }
269
270    /// Begins a table with flags and standard sizing contraints.
271    ///
272    /// This does no work on styling the headers (the top row) -- see either
273    /// [begin_table_header](Self::begin_table_header) or the more complex
274    /// [table_setup_column](Self::table_setup_column).
275    ///
276    /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
277    /// If this makes a difference to you, you are probably trying to make too many columns.
278    #[must_use = "if return is dropped immediately, table is ended immediately."]
279    pub fn begin_table_with_flags(
280        &self,
281        str_id: impl AsRef<str>,
282        column_count: usize,
283        flags: TableFlags,
284    ) -> Option<TableToken<'ui>> {
285        self.begin_table_with_sizing(str_id, column_count, flags, [0.0, 0.0], 0.0)
286    }
287
288    /// Begins a table with all flags and sizing contraints. This is the base method,
289    /// and gives users the most flexibility.
290    ///
291    /// This does no work on styling the headers (the top row) -- see either
292    /// [begin_table_header](Self::begin_table_header) or the more complex
293    /// [table_setup_column](Self::table_setup_column).
294    ///
295    /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
296    /// If this makes a difference to you, you are probably trying to make too many columns.
297    #[must_use = "if return is dropped immediately, table is ended immediately."]
298    pub fn begin_table_with_sizing(
299        &self,
300        str_id: impl AsRef<str>,
301        column: usize,
302        flags: TableFlags,
303        outer_size: [f32; 2],
304        inner_width: f32,
305    ) -> Option<TableToken<'ui>> {
306        let should_render = unsafe {
307            sys::igBeginTable(
308                self.scratch_txt(str_id),
309                column as i32,
310                flags.bits() as i32,
311                outer_size.into(),
312                inner_width,
313            )
314        };
315
316        // todo: once msrv is 1.54, convert this to .then(||)
317        if should_render {
318            Some(TableToken::new(self))
319        } else {
320            None
321        }
322    }
323
324    /// Begins a table with no flags and with standard sizing contraints.
325    ///
326    /// Takes an array of table header information, the length of which determines
327    /// how many columns will be created.
328    #[cfg(feature = "min-const-generics")]
329    #[must_use = "if return is dropped immediately, table is ended immediately."]
330    pub fn begin_table_header<'a, Name: AsRef<str>, const N: usize>(
331        &self,
332        str_id: impl AsRef<str>,
333        column_data: [TableColumnSetup<'a, Name>; N],
334    ) -> Option<TableToken<'ui>> {
335        self.begin_table_header_with_flags(str_id, column_data, TableFlags::empty())
336    }
337
338    /// Begins a table with flags and standard sizing contraints.
339    ///
340    /// Takes an array of table header information, the length of which determines
341    /// how many columns will be created.
342    #[cfg(feature = "min-const-generics")]
343    #[must_use = "if return is dropped immediately, table is ended immediately."]
344    pub fn begin_table_header_with_flags<'a, Name: AsRef<str>, const N: usize>(
345        &self,
346        str_id: impl AsRef<str>,
347        column_data: [TableColumnSetup<'a, Name>; N],
348        flags: TableFlags,
349    ) -> Option<TableToken<'ui>> {
350        self.begin_table_header_with_sizing(str_id, column_data, flags, [0.0, 0.0], 0.0)
351    }
352
353    /// Begins a table with all flags and sizing contraints. This is the base method,
354    /// and gives users the most flexibility.
355    /// Takes an array of table header information, the length of which determines
356    /// how many columns will be created.
357    #[cfg(feature = "min-const-generics")]
358    #[must_use = "if return is dropped immediately, table is ended immediately."]
359    pub fn begin_table_header_with_sizing<'a, Name: AsRef<str>, const N: usize>(
360        &self,
361        str_id: impl AsRef<str>,
362        column_data: [TableColumnSetup<'a, Name>; N],
363        flags: TableFlags,
364        outer_size: [f32; 2],
365        inner_width: f32,
366    ) -> Option<TableToken<'ui>> {
367        self.begin_table_with_sizing(str_id, N, flags, outer_size, inner_width)
368            .map(|data| {
369                for value in column_data {
370                    self.table_setup_column_with(value);
371                }
372                self.table_headers_row();
373
374                data
375            })
376    }
377
378    /// Moves a table to the next row (ie, down) with no flags,
379    /// and with the next row having a standard computed height.
380    ///
381    /// If your table was made with [begin_table], this **must** be called
382    /// before rendering any cells (along with [table_next_column]).
383    /// If your table was made with [begin_table_header], this does not need to be called,
384    /// though [table_next_column] still should be.
385    ///
386    /// [begin_table]: Self::begin_table
387    /// [begin_table_header]: Self::begin_table_header
388    /// [table_next_column]: Self::table_next_column
389    #[inline]
390    pub fn table_next_row(&self) {
391        self.table_next_row_with_flags(TableRowFlags::empty());
392    }
393
394    /// Moves a  table to the next row (ie, down), with the given flags,
395    /// and with the next row having a standard computed height.
396    ///
397    /// Setting a flag here will make the next row a "header" now, which may
398    /// require setup of column data.
399    ///
400    /// See [table_next_row](Self::table_next_row) for information on how moving rows work. To set the row
401    /// with a given height, see [table_next_row_with_height](Self::table_next_row_with_height).
402    #[inline]
403    pub fn table_next_row_with_flags(&self, flags: TableRowFlags) {
404        self.table_next_row_with_height(flags, 0.0);
405    }
406
407    /// Moves a  table to the next row (ie, down), with the given flags,
408    /// and with the given minimum height.
409    ///
410    /// See [table_next_row](Self::table_next_row) for information on how moving rows work.
411    #[inline]
412    pub fn table_next_row_with_height(&self, flags: TableRowFlags, min_row_height: f32) {
413        unsafe {
414            sys::igTableNextRow(flags.bits() as i32, min_row_height);
415        }
416    }
417
418    /// Moves onto the next column. If at `column_count`, this will move to the next row.
419    /// In this way, you can use this function as an iterator over each cell in the table.
420    ///
421    /// # Example
422    /// ```no_run
423    /// # let mut ctx = arcdps_imgui::Context::create();
424    /// # { let ui = ctx.frame();
425    /// if let Some(_t) = ui.begin_table("Basic-Table", 2) {
426    ///     // we have to call next_row because we didn't make headers..
427    ///     ui.table_next_row();
428    ///
429    ///     // you always have to call this to start...
430    ///     // take advantage of this in loops!
431    ///     ui.table_next_column();
432    ///     ui.text("x: 0, y: 0");
433    ///
434    ///     ui.table_next_column();
435    ///     ui.text("x: 1, y: 0");
436    ///     
437    ///     // notice that we go down a row here too.
438    ///     ui.table_next_column();
439    ///     ui.text("x: 0, y: 1");
440    ///
441    ///     ui.table_next_column();
442    ///     ui.text("x: 1, y: 1");
443    /// }
444    /// # };
445    /// ```
446    ///
447    /// This functions returns true if the given column is **visible.** It is not
448    /// marked as must use, as you can still render commands into the not-visible column,
449    /// though you can choose to not as an optimization.
450    pub fn table_next_column(&self) -> bool {
451        unsafe { sys::igTableNextColumn() }
452    }
453
454    /// Moves onto the given column.
455    ///
456    /// # Example
457    /// ```no_run
458    /// # let mut ctx = arcdps_imgui::Context::create();
459    /// # { let ui = ctx.frame();
460    /// if let Some(_t) = ui.begin_table("Basic-Table", 2) {
461    ///     // we have to call next_row because we didn't make headers..
462    ///     ui.table_next_row();
463    ///
464    ///     for i in 0..2 {
465    ///         ui.table_set_column_index(i);
466    ///         ui.text(format!("x: {}", i));
467    ///     }
468    ///     
469    ///     // oops I just remembered, i need to add something on idx 0!
470    ///     ui.table_set_column_index(0);
471    ///     // if i uncomment this line, we'll write on top of our previous "x: 0"
472    ///     // line:
473    ///     // ui.text("hello from the future on top of the past");
474    ///     // so we do a .newline();
475    ///     ui.new_line();
476    ///     ui.text("hello from the future");
477    ///
478    ///     // imgui will understand this and row spacing will be adjusted automatically.
479    /// }
480    /// # };
481    /// ```
482    ///
483    /// This functions returns true if the given column is **visible.** It is not
484    /// marked as must use, as you can still render commands into the not-visible column,
485    /// though you can choose to not as an optimization.
486    ///
487    /// # Panics
488    /// If `column_index >= ui.table_columm_count`, this function will panic. In `debug` releases,
489    /// we will panic on the Rust side, for a nicer error message, though in release, we will
490    /// panic in C++, which will result in an ugly stack overflow.
491    pub fn table_set_column_index(&self, column_index: usize) -> bool {
492        #[cfg(debug_assertions)]
493        {
494            let size = self.table_column_count() as usize;
495            if column_index >= size {
496                panic!(
497                    "column_index >= self.table_get_column_count().\
498                Requested {}, but only have {} columns.",
499                    column_index, size
500                );
501            }
502        }
503
504        unsafe { sys::igTableSetColumnIndex(column_index as i32) }
505    }
506
507    /// Specify label per column, with no flags and default sizing. You can avoid calling
508    /// this method entirely by using [begin_table_header](Self::begin_table_header).
509    ///
510    /// # Example
511    /// ```no_run
512    /// # let mut ctx = arcdps_imgui::Context::create();
513    /// # { let ui = ctx.frame();
514    /// if let Some(_t) = ui.begin_table("My Table", 2) {
515    ///     ui.table_setup_column("One");
516    ///     ui.table_setup_column("Two");
517    ///     ui.table_setup_column("Three");
518    ///     ui.table_headers_row();
519    ///
520    ///     // call next_column/set_column_index and proceed like normal.
521    ///     // the above code is the equivalent of just using `begin_table_header`
522    ///     // but does allow for some columns to have headers and others to not
523    /// }
524    /// # };
525    /// ```
526    ///
527    /// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header
528    /// row and automatically submit a table header for each column.
529    /// Headers are required to perform: reordering, sorting, and opening the context menu (though,
530    /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
531    pub fn table_setup_column(&self, str_id: impl AsRef<str>) {
532        self.table_setup_column_with(TableColumnSetup::new(str_id))
533    }
534
535    /// Specify label per column, with data given in [TableColumnSetup]. You can avoid calling
536    /// this method entirely by using [begin_table_header](Self::begin_table_header).
537    ///
538    /// See [table_setup_column](Self::table_setup_column) for an example of how to setup columns
539    /// yourself.
540    ///
541    /// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header
542    /// row and automatically submit a table header for each column.
543    /// Headers are required to perform: reordering, sorting, and opening the context menu (though,
544    /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
545    pub fn table_setup_column_with<N: AsRef<str>>(&self, data: TableColumnSetup<'_, N>) {
546        unsafe {
547            sys::igTableSetupColumn(
548                self.scratch_txt(data.name),
549                data.flags.bits() as i32,
550                data.init_width_or_weight,
551                data.user_id.as_imgui_id(),
552            )
553        }
554    }
555
556    /// Locks columns/rows so they stay visible when scrolled. Generally, you will be calling this
557    /// so that the header column is always visible (though go wild if you want). You can avoid
558    /// calling this entirely by passing `true` to [begin_table_header](Self::begin_table_header).
559    ///
560    /// # Example
561    /// ```no_run
562    /// # let mut ctx = arcdps_imgui::Context::create();
563    /// # { let ui = ctx.frame();
564    /// const COLUMN_COUNT: usize = 3;
565    /// if let Some(_t) = ui.begin_table("scroll-freeze-example", COLUMN_COUNT) {
566    ///     // locks the header row. Notice how we need to call it BEFORE `table_headers_row`.
567    ///     ui.table_setup_scroll_freeze(1, COLUMN_COUNT);
568    ///     ui.table_setup_column("One");
569    ///     ui.table_setup_column("Two");
570    ///     ui.table_setup_column("Three");
571    ///     ui.table_headers_row();
572    /// }
573    /// # };
574    /// ```
575    ///
576    /// Nb: we take `locked_columns` and `locked_rows` as a `usize`, but it will be converted
577    /// with `as i32` to an i32. If this makes a difference to you, you are probably
578    /// trying to make too many columns.
579    pub fn table_setup_scroll_freeze(&self, locked_columns: usize, locked_rows: usize) {
580        unsafe {
581            sys::igTableSetupScrollFreeze(locked_columns as i32, locked_rows as i32);
582        }
583    }
584
585    /// Along with [table_setup_column](Self::table_setup_column), this method is used
586    /// to create a header row and automatically submit a table header for each column.
587    ///
588    /// For an example of using this method, see [table_setup_column](Self::table_setup_column).
589    ///
590    /// Headers are required to perform: reordering, sorting, and opening the context menu (though,
591    /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
592    ///
593    /// You may manually submit headers using [table_next_column] + [table_header] calls, but this is
594    ///  only useful in some advanced use cases (e.g. adding custom widgets in header row).
595    /// See [table_header](Self::table_header) for more information.
596    ///
597    /// [table_next_column]: Self::table_next_column
598    /// [table_header]: Self::table_header
599    pub fn table_headers_row(&self) {
600        unsafe {
601            sys::igTableHeadersRow();
602        }
603    }
604
605    /// Use this function to manually declare a column cell to be a header.
606    ///
607    /// You generally should avoid using this outside of specific cases,
608    /// such as custom widgets. Instead, use [table_headers_row](Self::table_headers_row)
609    /// and [table_setup_column](Self::table_setup_column).
610    pub fn table_header(&self, label: impl AsRef<str>) {
611        unsafe {
612            sys::igTableHeader(self.scratch_txt(label));
613        }
614    }
615
616    /// Gets the numbers of columns in the current table.
617    pub fn table_column_count(&self) -> usize {
618        unsafe { sys::igTableGetColumnCount() as usize }
619    }
620
621    /// Gets the current column index in the current table.
622    pub fn table_column_index(&self) -> usize {
623        unsafe { sys::igTableGetColumnIndex() as usize }
624    }
625
626    /// Gets the current row index in the current table.
627    pub fn table_row_index(&self) -> usize {
628        unsafe { sys::igTableGetRowIndex() as usize }
629    }
630
631    /// Gets the name of the current column. If there is no currently bound name
632    /// for this column, we will return an empty string.
633    ///
634    /// Use [table_column_name_with_column](Self::table_column_name_with_column)
635    /// for arbitrary indices.
636    pub fn table_column_name(&mut self) -> &str {
637        unsafe {
638            // imgui uses utf8...though that is a continuous process there.
639            CStr::from_ptr(sys::igTableGetColumnName(-1))
640                .to_str()
641                .unwrap()
642        }
643    }
644
645    /// Gets the name of a given column. If there is no currently bound name
646    /// for this column, we will return an empty string.
647    ///
648    /// Use [table_column_name](Self::table_column_name) for the current column.
649    pub fn table_column_name_with_column(&mut self, column: usize) -> &str {
650        unsafe {
651            // imgui uses utf8...though that is a continuous process there.
652            CStr::from_ptr(sys::igTableGetColumnName(column as i32))
653                .to_str()
654                .unwrap()
655        }
656    }
657
658    /// Gets the flags on the current column in the current table.
659    pub fn table_column_flags(&self) -> TableColumnFlags {
660        unsafe {
661            TableColumnFlags::from_bits(sys::igTableGetColumnFlags(-1) as u32)
662                .expect("bad column flags")
663        }
664    }
665
666    /// Gets the flags on the given column in the current table. To get the current column's
667    /// flags without having to call [table_column_index](Self::table_column_index), use
668    /// [table_column_flags](Self::table_column_flags).
669    pub fn table_column_flags_with_column(&self, column_n: usize) -> TableColumnFlags {
670        unsafe {
671            TableColumnFlags::from_bits(sys::igTableGetColumnFlags(column_n as i32) as u32)
672                .expect("bad column flags")
673        }
674    }
675
676    /// Sets the given background color for this column. See [TableBgTarget]
677    /// for more information on how colors work for tables.
678    ///
679    /// Use [table_set_bg_color_with_column](Self::table_set_bg_color_with_column) to set
680    /// for arbitrary indices.
681    pub fn table_set_bg_color(&self, target: TableBgTarget, color: impl Into<ImColor32>) {
682        unsafe {
683            sys::igTableSetBgColor(target.bits() as i32, color.into().into(), -1);
684        }
685    }
686
687    /// Sets the given background color for any column. See [TableBgTarget]
688    /// for more information on how colors work for tables.
689    ///
690    /// Use [table_set_bg_color](Self::table_set_bg_color) for the current column.
691    pub fn table_set_bg_color_with_column(
692        &self,
693        target: TableBgTarget,
694        color: impl Into<ImColor32>,
695        column_index: usize,
696    ) {
697        unsafe {
698            sys::igTableSetBgColor(
699                target.bits() as i32,
700                color.into().into(),
701                column_index as i32,
702            );
703        }
704    }
705
706    /// Gets the sorting data for a table. This will be `None` when not sorting.
707    ///
708    /// See the examples folder for how to use the sorting API.
709    pub fn table_sort_specs_mut(&self) -> Option<TableSortSpecsMut<'_>> {
710        unsafe {
711            let value = sys::igTableGetSortSpecs();
712            if value.is_null() {
713                None
714            } else {
715                Some(TableSortSpecsMut(value, PhantomData))
716            }
717        }
718    }
719}
720
721/// A struct containing all the data needed to setup a table column header
722/// via [begin_table_header](Ui::begin_table_header) or [table_setup_column](Ui::table_setup_column).
723#[derive(Debug, Default)]
724pub struct TableColumnSetup<'a, Name> {
725    /// The name of column to be displayed to users.
726    pub name: Name,
727    /// The flags this column will have.
728    pub flags: TableColumnFlags,
729    /// The width or weight of the given column.
730    pub init_width_or_weight: f32,
731    /// A user_id, primarily used in sorting operations.
732    pub user_id: Id<'a>,
733}
734
735impl<'a, Name: AsRef<str>> TableColumnSetup<'a, Name> {
736    pub fn new(name: Name) -> Self {
737        Self {
738            name,
739            flags: TableColumnFlags::empty(),
740            init_width_or_weight: 0.0,
741            user_id: Id::Int(0),
742        }
743    }
744}
745
746/// A wrapper around table sort specs.
747///
748/// To use this simply, use [conditional_sort] and provide a closure --
749/// if you should sort your data, then the closure will be ran and imgui
750/// will be informed that your data is sorted.
751///
752/// For manual control (such as if sorting can fail), use [should_sort] to
753/// check if you should sort your data, sort your data using [specs] for information
754/// on how to sort it, and then [set_sorted] to indicate that the data is sorted.
755///
756/// [conditional_sort]: Self::conditional_sort
757/// [should_sort]: Self::should_sort
758/// [specs]: Self::specs
759/// [set_sorted]: Self::set_sorted
760pub struct TableSortSpecsMut<'ui>(*mut sys::ImGuiTableSortSpecs, PhantomData<Ui<'ui>>);
761
762impl TableSortSpecsMut<'_> {
763    /// Gets the specs for a given sort. In most scenarios, this will be a slice of 1 entry.
764    pub fn specs(&self) -> Specs<'_> {
765        let value =
766            unsafe { std::slice::from_raw_parts((*self.0).Specs, (*self.0).SpecsCount as usize) };
767
768        Specs(value)
769    }
770
771    /// Returns true if the data should be sorted.
772    pub fn should_sort(&self) -> bool {
773        unsafe { (*self.0).SpecsDirty }
774    }
775
776    /// Sets the internal flag that the data has been sorted.
777    pub fn set_sorted(&mut self) {
778        unsafe {
779            (*self.0).SpecsDirty = false;
780        }
781    }
782
783    /// Provide a closure, which will receive the Specs for a sort.
784    ///
785    /// If you should sort the data, the closure will run, and ImGui will be
786    /// told that the data has been sorted.
787    ///
788    /// If you need manual control over sorting, consider using [should_sort], [specs],
789    /// and [set_sorted] youself.
790    ///
791    /// [should_sort]: Self::should_sort
792    /// [specs]: Self::specs
793    /// [set_sorted]: Self::set_sorted
794    pub fn conditional_sort(mut self, mut f: impl FnMut(Specs<'_>)) {
795        let is_dirty = self.should_sort();
796
797        if is_dirty {
798            f(self.specs());
799        }
800
801        self.set_sorted();
802    }
803}
804
805/// A wrapper around a slice of [TableColumnSortSpecs].
806///
807/// This slice may be 0 if [TableFlags::SORT_TRISTATE] is true, may be > 1 is [TableFlags::SORT_MULTI] is true,
808/// but is generally == 1.
809///
810/// Consume this struct as an iterator.
811pub struct Specs<'a>(&'a [sys::ImGuiTableColumnSortSpecs]);
812
813impl<'a> Specs<'a> {
814    pub fn iter(self) -> impl Iterator<Item = TableColumnSortSpecs<'a>> {
815        self.0.iter().map(|v| TableColumnSortSpecs(v))
816    }
817}
818
819pub struct TableColumnSortSpecs<'a>(&'a sys::ImGuiTableColumnSortSpecs);
820impl<'a> TableColumnSortSpecs<'a> {
821    /// User id of the column (if specified by a TableSetupColumn() call)
822    pub fn column_user_id(&self) -> sys::ImGuiID {
823        self.0.ColumnUserID
824    }
825
826    /// Index of the column
827    pub fn column_idx(&self) -> usize {
828        self.0.ColumnIndex as usize
829    }
830
831    /// Index within parent [Specs] slice where this was found -- always stored in order starting
832    /// from 0, tables sorted on a single criteria will always have a 0 here.
833    ///
834    /// Generally, you don't need to access this, as it's the same as calling `specs.iter().enumerate()`.
835    pub fn sort_order(&self) -> usize {
836        self.0.SortOrder as usize
837    }
838
839    /// Gets the sort direction for the given column. This will nearly always be `Some` if you
840    /// can access it.
841    pub fn sort_direction(&self) -> Option<TableSortDirection> {
842        match self.0.SortDirection() {
843            0 => None,
844            1 => Some(TableSortDirection::Ascending),
845            2 => Some(TableSortDirection::Descending),
846            _ => unimplemented!(),
847        }
848    }
849}
850
851create_token!(
852    /// Tracks a table which can be rendered onto, ending with `.end()`
853    /// or by dropping.
854    pub struct TableToken<'ui>;
855
856    /// Ends the table.
857    drop { sys::igEndTable() }
858);