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);