1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
use std::ffi::CStr;
use std::marker::PhantomData;

use bitflags::bitflags;

use crate::sys;
use crate::{Id, ImColor32, Ui};

bitflags! {
    /// Flags passed to `begin_table` methods.
    ///
    /// Important! Sizing policies have complex and subtle side effects, more so than you would expect.
    /// Read comments/demos carefully + experiment with live demos to get acquainted with them.
    /// - The DEFAULT sizing policies are:
    ///    - Default to [SizingFixedFit]    if [ScrollX] is on, or if host window has (WindowFlags::AlwaysAutoResize)[crate::WindowFlags::AlwaysAutoResize].
    ///    - Default to [SizingStretchSame] if [ScrollX] is off.
    /// - When [ScrollX] is off:
    ///    - Table defaults to [SizingStretchSame] -> all Columns defaults to [TableColumnFlags::WidthStretch] with same weight.
    ///    - Columns sizing policy allowed: [Stretch] (default), [Fixed]/Auto.
    ///    - [Fixed] Columns will generally obtain their requested width (unless the table cannot fit them all).
    ///    - [Stretch] Columns will share the remaining width.
    ///    - Mixed [Fixed]/[Stretch] columns is possible but has various side-effects on resizing behaviors.
    ///      The typical use of mixing sizing policies is: any number of LEADING [Fixed] columns, followed by one or two TRAILING [Stretch] columns.
    ///      (this is because the visible order of columns have subtle but necessary effects on how they react to manual resizing).
    /// - When [ScrollX] is on:
    ///    - Table defaults to [SizingFixedFit] -> all Columns defaults to [TableColumnFlags::WidthFixed]
    ///    - Columns sizing policy allowed: [Fixed]/Auto mostly.
    ///    - [Fixed] Columns can be enlarged as needed. Table will show an horizontal scrollbar if needed.
    ///    - 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.
    ///    - Using [Stretch] columns OFTEN DOES NOT MAKE SENSE if [ScrollX] is on, UNLESS you have specified a value for `inner_width` in BeginTable().
    ///      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.
    /// - Read on documentation at the top of imgui_tables.cpp for more details.
    #[repr(transparent)]
    pub struct TableFlags: u32 {
        // Features

        /// Enable resizing columns.
        const RESIZABLE = sys::ImGuiTableFlags_Resizable;
        /// Enable reordering columns in header row, though you must set up a header row
        /// with `begin_table_header` or `table_setup_column`.
        const REORDERABLE =sys::ImGuiTableFlags_Reorderable;
        /// Enable hiding/disabling columns in context menu.
        const HIDEABLE = sys::ImGuiTableFlags_Hideable;
        /// Enable sorting. See `table_get_sort_specs` to object sort specs. Also see [SortMulti]
        /// and [SortTristate].
        const SORTABLE = sys::ImGuiTableFlags_Sortable;
        /// Disable persisting columns order, width, and sort settings in the .ini file.
        const NO_SAVED_SETTINGS = sys::ImGuiTableFlags_NoSavedSettings;
        /// Right-click on columns body/contents will display table context menu.
        /// By default you can only right click in a headers row.
        const CONTEXT_MENU_IN_BODY = sys::ImGuiTableFlags_ContextMenuInBody;

        // Decorations

        /// Set each RowBg color with [table_row_bg] or [table_row_bg_alt] (equivalent of calling
        /// `table_set_bg_color` with `ROW_BG0` on each row manually)
        const ROW_BG = sys::ImGuiTableFlags_RowBg;
        /// Draw horizontal borders between rows.
        const BORDERS_INNER_H = sys::ImGuiTableFlags_BordersInnerH;
        /// Draw horizontal borders at the top and bottom.
        const BORDERS_OUTER_H = sys::ImGuiTableFlags_BordersOuterH;
        /// Draw vertical borders between columns.
        const BORDERS_INNER_V = sys::ImGuiTableFlags_BordersInnerV;
        /// Draw vertical borders on the left and right sides.
        const BORDERS_OUTER_V = sys::ImGuiTableFlags_BordersOuterV;
        /// Draw all horizontal borders (this is just [BORDERS_INNER_H] | [BORDERS_OUTER_H]).
        const BORDERS_H = sys::ImGuiTableFlags_BordersH;
        /// Draw all vertical borders (this is just [BORDERS_INNER_V] | [BORDERS_OUTER_V]).
        const BORDERS_V = sys::ImGuiTableFlags_BordersV;
        /// Draw all inner borders (this is just [BORDERS_INNER_H] | [BORDERS_INNER_V]).
        const BORDERS_INNER = sys::ImGuiTableFlags_BordersInner;
        /// Draw all outer borders (this is just [BORDERS_OUTER_H] | [BORDERS_OUTER_V]).
        const BORDERS_OUTER = sys::ImGuiTableFlags_BordersOuter;
        /// Draw all borders (this is just [BORDERS_INNER] | [BORDERS_OUTER]).
        const BORDERS = sys::ImGuiTableFlags_Borders;
        /// **ALPHA** Disable vertical borders in columns Body (borders will always appears in Headers).
        /// May move to Style
        const NO_BORDERS_IN_BODY = sys::ImGuiTableFlags_NoBordersInBody;
        /// **ALPHA** Disable vertical borders in columns Body until hovered for resize (borders will always appears in Headers).
        /// May move to style
        const NO_BORDERS_IN_BODY_UNTIL_RESIZE = sys::ImGuiTableFlags_NoBordersInBodyUntilResize;

        // Sizing Policy (read above for defaults)

        /// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable),
        /// matching contents width.
        const SIZING_FIXED_FIT = sys::ImGuiTableFlags_SizingFixedFit;
        /// Columns default to [WidthFixed] or [WidthAuto] (if resizable or not resizable),
        /// matching the maximum contents width of all columns.
        /// Implicitly enable [NoKeepColumnsVisible].
        const SIZING_FIXED_SAME = sys::ImGuiTableFlags_SizingFixedSame;
        /// Columns default to [WidthStretch] with default weights proportional to each columns
        /// contents widths.
        const SIZING_STRETCH_PROP = sys::ImGuiTableFlags_SizingStretchProp;
        /// Columns default to [WidthStretch] with default weights all equal, unless overridden by
        /// a column's `TableHeader`.
        const SIZING_STRETCH_SAME = sys::ImGuiTableFlags_SizingStretchSame;

        // Sizing Extra Options

        /// Make outer width auto-fit to columns, overriding outer_size.x value. Only available when
        /// [ScrollX]/[ScrollY] are disabled and [Stretch] columns are not used.
        const NO_HOST_EXTEND_X = sys::ImGuiTableFlags_NoHostExtendX;
        /// Make outer height stop exactly at outer_size.y (prevent auto-extending table past the limit).
        /// Only available when [ScrollX]/[ScrollY] are disabled.
        /// Data below the limit will be clipped and not visible.
        const NO_HOST_EXTEND_Y = sys::ImGuiTableFlags_NoHostExtendY;
        /// Disable keeping column always minimally visible when [ScrollX] is off and table
        /// gets too small. Not recommended if columns are resizable.
        const NO_KEEP_COLUMNS_VISIBLE = sys::ImGuiTableFlags_NoKeepColumnsVisible;
        /// Disable distributing remainder width to stretched columns (width allocation on a 100-wide
        /// table with 3 columns: Without this flag: 33,33,34. With this flag: 33,33,33).
        /// With larger number of columns, resizing will appear to be less smooth.
        const PRECISE_WIDTHS = sys::ImGuiTableFlags_PreciseWidths;

        // Clipping

        /// Disable clipping rectangle for every individual columns (reduce draw command count, items will
        /// be able to overflow into other columns). Generally incompatible with [table_setup_scroll_freeze].
        const NO_CLIP = sys::ImGuiTableFlags_NoClip;

        // Padding

        /// Default if [BordersOuterV] is on. Enable outer-most padding. Generally desirable if you have headers.
        const PAD_OUTER_X = sys::ImGuiTableFlags_PadOuterX;
        /// Default if [BordersOuterV] is off. Disable outer-most padding.
        const NO_PAD_OUTER_X = sys::ImGuiTableFlags_NoPadOuterX;
        /// Disable inner padding between columns (double inner padding if [BordersOuterV] is on, single
        /// inner padding if BordersOuterV is off).
        const NO_PAD_INNER_X = sys::ImGuiTableFlags_NoPadInnerX;

        // Scrolling

        /// Enable horizontal scrolling. Require 'outer_size' parameter of [begin_table] to specify the
        /// container size. Changes default sizing policy. Because this create a child window,
        /// [ScrollY] is currently generally recommended when using [ScrollX].
        const SCROLL_X = sys::ImGuiTableFlags_ScrollX;
        /// Enable vertical scrolling. Require 'outer_size' parameter of [begin_table] to specify the
        /// container size.
        const SCROLL_Y = sys::ImGuiTableFlags_ScrollY;

        // Sorting

        /// Hold shift when clicking headers to sort on multiple column. [table_get_sort_specs] may return specs where `[spec_count] > 1`.
        const SORT_MULTI = sys::ImGuiTableFlags_SortMulti;
        /// Allow no sorting, disable default sorting. `table_get_sort_specs` may return specs where `[specs_count] == 0`.
        const SORT_TRISTATE = sys::ImGuiTableFlags_SortTristate;
    }
}

bitflags! {
    /// Flags for [table_next_row_with_flags].
    #[repr(transparent)]
    pub struct TableRowFlags: u32 {
        /// Identify header row (set default background color + width of its contents
        /// accounted different for auto column width)
        const HEADERS = sys::ImGuiTableRowFlags_Headers;
    }
}

bitflags! {
    /// Flags for [TableColumnSetup] and [table_setup_column_with].
    #[repr(transparent)]
    #[derive(Default)]
    pub struct TableColumnFlags: u32 {
        // Input configuration flags

        /// Default as a hidden/disabled column.
        const DEFAULT_HIDE = sys::ImGuiTableColumnFlags_DefaultHide;
        /// Default as a sorting column.
        const DEFAULT_SORT = sys::ImGuiTableColumnFlags_DefaultSort;
        /// Column will stretch. Preferable with horizontal scrolling disabled (default
        /// if table sizing policy is [ImGuiTableFlags::SizingStretchSame] or
        /// [ImGuiTableFlags::SizingStretchProp]).
        const WIDTH_STRETCH = sys::ImGuiTableColumnFlags_WidthStretch;
        /// Column will not stretch. Preferable with horizontal scrolling enabled (default
        /// if table sizing policy is [ImGuiTableFlags::SizingFixedFit] and table is resizable).
        const WIDTH_FIXED = sys::ImGuiTableColumnFlags_WidthFixed;
        /// Disable manual resizing.
        const NO_RESIZE = sys::ImGuiTableColumnFlags_NoResize;
        /// Disable manual reordering this column, this will also prevent other columns from
        /// crossing over this column.
        const NO_REORDER = sys::ImGuiTableColumnFlags_NoReorder;
        /// Disable ability to hide/disable this column.
        const NO_HIDE = sys::ImGuiTableColumnFlags_NoHide;
        /// Disable clipping for this column (all [NO_CLIP] columns will render in a same
        /// draw command).
        const NO_CLIP = sys::ImGuiTableColumnFlags_NoClip;
        /// Disable ability to sort on this field (even if [ImGuiTableFlags::Sortable] is
        /// set on the table).
        const NO_SORT = sys::ImGuiTableColumnFlags_NoSort;
        /// Disable ability to sort in the ascending direction.
        const NO_SORT_ASCENDING = sys::ImGuiTableColumnFlags_NoSortAscending;
        /// Disable ability to sort in the descending direction.
        const NO_SORT_DESCENDING = sys::ImGuiTableColumnFlags_NoSortDescending;
        /// Disable header text width contribution to automatic column width.
        const NO_HEADER_WIDTH = sys::ImGuiTableColumnFlags_NoHeaderWidth;
        /// Make the initial sort direction Ascending when first sorting on this column (default).
        const PREFER_SORT_ASCENDING = sys::ImGuiTableColumnFlags_PreferSortAscending;
        /// Make the initial sort direction Descending when first sorting on this column.
        const PREFER_SORT_DESCENDING = sys::ImGuiTableColumnFlags_PreferSortDescending;
        /// Use current Indent value when entering cell (default for column 0).
        const INDENT_ENABLE = sys::ImGuiTableColumnFlags_IndentEnable;
        /// Ignore current Indent value when entering cell (default for columns > 0).
        /// Indentation changes _within_ the cell will still be honored.
        const INDENT_DISABLE = sys::ImGuiTableColumnFlags_IndentDisable;

        // Output status flags, read-only via [table_get_column_flags]

        /// Status: is enabled == not hidden by user/api (referred to as "Hide" in
        /// [DefaultHide] and [NoHide]) flags.
        const IS_ENABLED = sys::ImGuiTableColumnFlags_IsEnabled;
        /// Status: is visible == is enabled AND not clipped by scrolling.
        const IS_VISIBLE = sys::ImGuiTableColumnFlags_IsVisible;
        /// Status: is currently part of the sort specs
        const IS_SORTED = sys::ImGuiTableColumnFlags_IsSorted;
        /// Status: is hovered by mouse
        const IS_HOVERED = sys::ImGuiTableColumnFlags_IsHovered;
    }
}

bitflags! {
    /// Enum for [table_set_bg_color].
    /// Background colors are rendering in 3 layers:
    ///  - Layer 0: draw with RowBg0 color if set, otherwise draw with ColumnBg0 if set.
    ///  - Layer 1: draw with RowBg1 color if set, otherwise draw with ColumnBg1 if set.
    ///  - Layer 2: draw with CellBg color if set.
    /// The purpose of the two row/columns layers is to let you decide if a background color
    /// changes should override or blend with the existing color.
    /// When using [TableFlags::RowBg] on the table, each row has the RowBg0 color automatically
    /// set for odd/even rows.
    /// If you set the color of RowBg0 target, your color will override the existing RowBg0 color.
    /// If you set the color of RowBg1 or ColumnBg1 target, your color will blend over the RowBg0 color.
    #[repr(transparent)]
    pub struct TableBgTarget: u32 {
        /// Set row background color 0 (generally used for background, automatically set when
        /// [TableFlags::RowBg] is used)
        const ROW_BG0 = sys::ImGuiTableBgTarget_RowBg0;
        /// Set row background color 1 (generally used for selection marking)
        const ROW_BG1 = sys::ImGuiTableBgTarget_RowBg1;
        /// Set cell background color (top-most color)
        const CELL_BG = sys::ImGuiTableBgTarget_CellBg;
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub enum TableSortDirection {
    Ascending,
    Descending,
}

impl<'ui> Ui<'ui> {
    /// Begins a table with no flags and with standard sizing contraints.
    ///
    /// This does no work on styling the headers (the top row) -- see either
    /// [begin_table_header](Self::begin_table_header) or the more complex
    /// [table_setup_column](Self::table_setup_column).
    ///
    /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
    /// If this makes a difference to you, you are probably trying to make too many columns.
    #[must_use = "if return is dropped immediately, table is ended immediately."]
    pub fn begin_table(
        &self,
        str_id: impl AsRef<str>,
        column_count: usize,
    ) -> Option<TableToken<'ui>> {
        self.begin_table_with_flags(str_id, column_count, TableFlags::empty())
    }

    /// Begins a table with flags and standard sizing contraints.
    ///
    /// This does no work on styling the headers (the top row) -- see either
    /// [begin_table_header](Self::begin_table_header) or the more complex
    /// [table_setup_column](Self::table_setup_column).
    ///
    /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
    /// If this makes a difference to you, you are probably trying to make too many columns.
    #[must_use = "if return is dropped immediately, table is ended immediately."]
    pub fn begin_table_with_flags(
        &self,
        str_id: impl AsRef<str>,
        column_count: usize,
        flags: TableFlags,
    ) -> Option<TableToken<'ui>> {
        self.begin_table_with_sizing(str_id, column_count, flags, [0.0, 0.0], 0.0)
    }

    /// Begins a table with all flags and sizing contraints. This is the base method,
    /// and gives users the most flexibility.
    ///
    /// This does no work on styling the headers (the top row) -- see either
    /// [begin_table_header](Self::begin_table_header) or the more complex
    /// [table_setup_column](Self::table_setup_column).
    ///
    /// Nb: we take `column` as a usize, but it will be converted with `as i32` to an i32.
    /// If this makes a difference to you, you are probably trying to make too many columns.
    #[must_use = "if return is dropped immediately, table is ended immediately."]
    pub fn begin_table_with_sizing(
        &self,
        str_id: impl AsRef<str>,
        column: usize,
        flags: TableFlags,
        outer_size: [f32; 2],
        inner_width: f32,
    ) -> Option<TableToken<'ui>> {
        let should_render = unsafe {
            sys::igBeginTable(
                self.scratch_txt(str_id),
                column as i32,
                flags.bits() as i32,
                outer_size.into(),
                inner_width,
            )
        };

        // todo: once msrv is 1.54, convert this to .then(||)
        if should_render {
            Some(TableToken::new(self))
        } else {
            None
        }
    }

    /// Begins a table with no flags and with standard sizing contraints.
    ///
    /// Takes an array of table header information, the length of which determines
    /// how many columns will be created.
    #[cfg(feature = "min-const-generics")]
    #[must_use = "if return is dropped immediately, table is ended immediately."]
    pub fn begin_table_header<'a, Name: AsRef<str>, const N: usize>(
        &self,
        str_id: impl AsRef<str>,
        column_data: [TableColumnSetup<'a, Name>; N],
    ) -> Option<TableToken<'ui>> {
        self.begin_table_header_with_flags(str_id, column_data, TableFlags::empty())
    }

    /// Begins a table with flags and standard sizing contraints.
    ///
    /// Takes an array of table header information, the length of which determines
    /// how many columns will be created.
    #[cfg(feature = "min-const-generics")]
    #[must_use = "if return is dropped immediately, table is ended immediately."]
    pub fn begin_table_header_with_flags<'a, Name: AsRef<str>, const N: usize>(
        &self,
        str_id: impl AsRef<str>,
        column_data: [TableColumnSetup<'a, Name>; N],
        flags: TableFlags,
    ) -> Option<TableToken<'ui>> {
        self.begin_table_header_with_sizing(str_id, column_data, flags, [0.0, 0.0], 0.0)
    }

    /// Begins a table with all flags and sizing contraints. This is the base method,
    /// and gives users the most flexibility.
    /// Takes an array of table header information, the length of which determines
    /// how many columns will be created.
    #[cfg(feature = "min-const-generics")]
    #[must_use = "if return is dropped immediately, table is ended immediately."]
    pub fn begin_table_header_with_sizing<'a, Name: AsRef<str>, const N: usize>(
        &self,
        str_id: impl AsRef<str>,
        column_data: [TableColumnSetup<'a, Name>; N],
        flags: TableFlags,
        outer_size: [f32; 2],
        inner_width: f32,
    ) -> Option<TableToken<'ui>> {
        self.begin_table_with_sizing(str_id, N, flags, outer_size, inner_width)
            .map(|data| {
                for value in column_data {
                    self.table_setup_column_with(value);
                }
                self.table_headers_row();

                data
            })
    }

    /// Moves a table to the next row (ie, down) with no flags,
    /// and with the next row having a standard computed height.
    ///
    /// If your table was made with [begin_table], this **must** be called
    /// before rendering any cells (along with [table_next_column]).
    /// If your table was made with [begin_table_header], this does not need to be called,
    /// though [table_next_column] still should be.
    ///
    /// [begin_table]: Self::begin_table
    /// [begin_table_header]: Self::begin_table_header
    /// [table_next_column]: Self::table_next_column
    #[inline]
    pub fn table_next_row(&self) {
        self.table_next_row_with_flags(TableRowFlags::empty());
    }

    /// Moves a  table to the next row (ie, down), with the given flags,
    /// and with the next row having a standard computed height.
    ///
    /// Setting a flag here will make the next row a "header" now, which may
    /// require setup of column data.
    ///
    /// See [table_next_row](Self::table_next_row) for information on how moving rows work. To set the row
    /// with a given height, see [table_next_row_with_height](Self::table_next_row_with_height).
    #[inline]
    pub fn table_next_row_with_flags(&self, flags: TableRowFlags) {
        self.table_next_row_with_height(flags, 0.0);
    }

    /// Moves a  table to the next row (ie, down), with the given flags,
    /// and with the given minimum height.
    ///
    /// See [table_next_row](Self::table_next_row) for information on how moving rows work.
    #[inline]
    pub fn table_next_row_with_height(&self, flags: TableRowFlags, min_row_height: f32) {
        unsafe {
            sys::igTableNextRow(flags.bits() as i32, min_row_height);
        }
    }

    /// Moves onto the next column. If at `column_count`, this will move to the next row.
    /// In this way, you can use this function as an iterator over each cell in the table.
    ///
    /// # Example
    /// ```no_run
    /// # let mut ctx = arcdps_imgui::Context::create();
    /// # { let ui = ctx.frame();
    /// if let Some(_t) = ui.begin_table("Basic-Table", 2) {
    ///     // we have to call next_row because we didn't make headers..
    ///     ui.table_next_row();
    ///
    ///     // you always have to call this to start...
    ///     // take advantage of this in loops!
    ///     ui.table_next_column();
    ///     ui.text("x: 0, y: 0");
    ///
    ///     ui.table_next_column();
    ///     ui.text("x: 1, y: 0");
    ///     
    ///     // notice that we go down a row here too.
    ///     ui.table_next_column();
    ///     ui.text("x: 0, y: 1");
    ///
    ///     ui.table_next_column();
    ///     ui.text("x: 1, y: 1");
    /// }
    /// # };
    /// ```
    ///
    /// This functions returns true if the given column is **visible.** It is not
    /// marked as must use, as you can still render commands into the not-visible column,
    /// though you can choose to not as an optimization.
    pub fn table_next_column(&self) -> bool {
        unsafe { sys::igTableNextColumn() }
    }

    /// Moves onto the given column.
    ///
    /// # Example
    /// ```no_run
    /// # let mut ctx = arcdps_imgui::Context::create();
    /// # { let ui = ctx.frame();
    /// if let Some(_t) = ui.begin_table("Basic-Table", 2) {
    ///     // we have to call next_row because we didn't make headers..
    ///     ui.table_next_row();
    ///
    ///     for i in 0..2 {
    ///         ui.table_set_column_index(i);
    ///         ui.text(format!("x: {}", i));
    ///     }
    ///     
    ///     // oops I just remembered, i need to add something on idx 0!
    ///     ui.table_set_column_index(0);
    ///     // if i uncomment this line, we'll write on top of our previous "x: 0"
    ///     // line:
    ///     // ui.text("hello from the future on top of the past");
    ///     // so we do a .newline();
    ///     ui.new_line();
    ///     ui.text("hello from the future");
    ///
    ///     // imgui will understand this and row spacing will be adjusted automatically.
    /// }
    /// # };
    /// ```
    ///
    /// This functions returns true if the given column is **visible.** It is not
    /// marked as must use, as you can still render commands into the not-visible column,
    /// though you can choose to not as an optimization.
    ///
    /// # Panics
    /// If `column_index >= ui.table_columm_count`, this function will panic. In `debug` releases,
    /// we will panic on the Rust side, for a nicer error message, though in release, we will
    /// panic in C++, which will result in an ugly stack overflow.
    pub fn table_set_column_index(&self, column_index: usize) -> bool {
        #[cfg(debug_assertions)]
        {
            let size = self.table_column_count() as usize;
            if column_index >= size {
                panic!(
                    "column_index >= self.table_get_column_count().\
                Requested {}, but only have {} columns.",
                    column_index, size
                );
            }
        }

        unsafe { sys::igTableSetColumnIndex(column_index as i32) }
    }

    /// Specify label per column, with no flags and default sizing. You can avoid calling
    /// this method entirely by using [begin_table_header](Self::begin_table_header).
    ///
    /// # Example
    /// ```no_run
    /// # let mut ctx = arcdps_imgui::Context::create();
    /// # { let ui = ctx.frame();
    /// if let Some(_t) = ui.begin_table("My Table", 2) {
    ///     ui.table_setup_column("One");
    ///     ui.table_setup_column("Two");
    ///     ui.table_setup_column("Three");
    ///     ui.table_headers_row();
    ///
    ///     // call next_column/set_column_index and proceed like normal.
    ///     // the above code is the equivalent of just using `begin_table_header`
    ///     // but does allow for some columns to have headers and others to not
    /// }
    /// # };
    /// ```
    ///
    /// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header
    /// row and automatically submit a table header for each column.
    /// Headers are required to perform: reordering, sorting, and opening the context menu (though,
    /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
    pub fn table_setup_column(&self, str_id: impl AsRef<str>) {
        self.table_setup_column_with(TableColumnSetup::new(str_id))
    }

    /// Specify label per column, with data given in [TableColumnSetup]. You can avoid calling
    /// this method entirely by using [begin_table_header](Self::begin_table_header).
    ///
    /// See [table_setup_column](Self::table_setup_column) for an example of how to setup columns
    /// yourself.
    ///
    /// Along with [table_headers_row](Self::table_headers_row), this method is used to create a header
    /// row and automatically submit a table header for each column.
    /// Headers are required to perform: reordering, sorting, and opening the context menu (though,
    /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
    pub fn table_setup_column_with<N: AsRef<str>>(&self, data: TableColumnSetup<'_, N>) {
        unsafe {
            sys::igTableSetupColumn(
                self.scratch_txt(data.name),
                data.flags.bits() as i32,
                data.init_width_or_weight,
                data.user_id.as_imgui_id(),
            )
        }
    }

    /// Locks columns/rows so they stay visible when scrolled. Generally, you will be calling this
    /// so that the header column is always visible (though go wild if you want). You can avoid
    /// calling this entirely by passing `true` to [begin_table_header](Self::begin_table_header).
    ///
    /// # Example
    /// ```no_run
    /// # let mut ctx = arcdps_imgui::Context::create();
    /// # { let ui = ctx.frame();
    /// const COLUMN_COUNT: usize = 3;
    /// if let Some(_t) = ui.begin_table("scroll-freeze-example", COLUMN_COUNT) {
    ///     // locks the header row. Notice how we need to call it BEFORE `table_headers_row`.
    ///     ui.table_setup_scroll_freeze(1, COLUMN_COUNT);
    ///     ui.table_setup_column("One");
    ///     ui.table_setup_column("Two");
    ///     ui.table_setup_column("Three");
    ///     ui.table_headers_row();
    /// }
    /// # };
    /// ```
    ///
    /// Nb: we take `locked_columns` and `locked_rows` as a `usize`, but it will be converted
    /// with `as i32` to an i32. If this makes a difference to you, you are probably
    /// trying to make too many columns.
    pub fn table_setup_scroll_freeze(&self, locked_columns: usize, locked_rows: usize) {
        unsafe {
            sys::igTableSetupScrollFreeze(locked_columns as i32, locked_rows as i32);
        }
    }

    /// Along with [table_setup_column](Self::table_setup_column), this method is used
    /// to create a header row and automatically submit a table header for each column.
    ///
    /// For an example of using this method, see [table_setup_column](Self::table_setup_column).
    ///
    /// Headers are required to perform: reordering, sorting, and opening the context menu (though,
    /// the context menu can also be made available in columns body using [TableFlags::CONTEXT_MENU_IN_BODY].
    ///
    /// You may manually submit headers using [table_next_column] + [table_header] calls, but this is
    ///  only useful in some advanced use cases (e.g. adding custom widgets in header row).
    /// See [table_header](Self::table_header) for more information.
    ///
    /// [table_next_column]: Self::table_next_column
    /// [table_header]: Self::table_header
    pub fn table_headers_row(&self) {
        unsafe {
            sys::igTableHeadersRow();
        }
    }

    /// Use this function to manually declare a column cell to be a header.
    ///
    /// You generally should avoid using this outside of specific cases,
    /// such as custom widgets. Instead, use [table_headers_row](Self::table_headers_row)
    /// and [table_setup_column](Self::table_setup_column).
    pub fn table_header(&self, label: impl AsRef<str>) {
        unsafe {
            sys::igTableHeader(self.scratch_txt(label));
        }
    }

    /// Gets the numbers of columns in the current table.
    pub fn table_column_count(&self) -> usize {
        unsafe { sys::igTableGetColumnCount() as usize }
    }

    /// Gets the current column index in the current table.
    pub fn table_column_index(&self) -> usize {
        unsafe { sys::igTableGetColumnIndex() as usize }
    }

    /// Gets the current row index in the current table.
    pub fn table_row_index(&self) -> usize {
        unsafe { sys::igTableGetRowIndex() as usize }
    }

    /// Gets the name of the current column. If there is no currently bound name
    /// for this column, we will return an empty string.
    ///
    /// Use [table_column_name_with_column](Self::table_column_name_with_column)
    /// for arbitrary indices.
    pub fn table_column_name(&mut self) -> &str {
        unsafe {
            // imgui uses utf8...though that is a continuous process there.
            CStr::from_ptr(sys::igTableGetColumnName(-1))
                .to_str()
                .unwrap()
        }
    }

    /// Gets the name of a given column. If there is no currently bound name
    /// for this column, we will return an empty string.
    ///
    /// Use [table_column_name](Self::table_column_name) for the current column.
    pub fn table_column_name_with_column(&mut self, column: usize) -> &str {
        unsafe {
            // imgui uses utf8...though that is a continuous process there.
            CStr::from_ptr(sys::igTableGetColumnName(column as i32))
                .to_str()
                .unwrap()
        }
    }

    /// Gets the flags on the current column in the current table.
    pub fn table_column_flags(&self) -> TableColumnFlags {
        unsafe {
            TableColumnFlags::from_bits(sys::igTableGetColumnFlags(-1) as u32)
                .expect("bad column flags")
        }
    }

    /// Gets the flags on the given column in the current table. To get the current column's
    /// flags without having to call [table_column_index](Self::table_column_index), use
    /// [table_column_flags](Self::table_column_flags).
    pub fn table_column_flags_with_column(&self, column_n: usize) -> TableColumnFlags {
        unsafe {
            TableColumnFlags::from_bits(sys::igTableGetColumnFlags(column_n as i32) as u32)
                .expect("bad column flags")
        }
    }

    /// Sets the given background color for this column. See [TableBgTarget]
    /// for more information on how colors work for tables.
    ///
    /// Use [table_set_bg_color_with_column](Self::table_set_bg_color_with_column) to set
    /// for arbitrary indices.
    pub fn table_set_bg_color(&self, target: TableBgTarget, color: impl Into<ImColor32>) {
        unsafe {
            sys::igTableSetBgColor(target.bits() as i32, color.into().into(), -1);
        }
    }

    /// Sets the given background color for any column. See [TableBgTarget]
    /// for more information on how colors work for tables.
    ///
    /// Use [table_set_bg_color](Self::table_set_bg_color) for the current column.
    pub fn table_set_bg_color_with_column(
        &self,
        target: TableBgTarget,
        color: impl Into<ImColor32>,
        column_index: usize,
    ) {
        unsafe {
            sys::igTableSetBgColor(
                target.bits() as i32,
                color.into().into(),
                column_index as i32,
            );
        }
    }

    /// Gets the sorting data for a table. This will be `None` when not sorting.
    ///
    /// See the examples folder for how to use the sorting API.
    pub fn table_sort_specs_mut(&self) -> Option<TableSortSpecsMut<'_>> {
        unsafe {
            let value = sys::igTableGetSortSpecs();
            if value.is_null() {
                None
            } else {
                Some(TableSortSpecsMut(value, PhantomData))
            }
        }
    }
}

/// A struct containing all the data needed to setup a table column header
/// via [begin_table_header](Ui::begin_table_header) or [table_setup_column](Ui::table_setup_column).
#[derive(Debug, Default)]
pub struct TableColumnSetup<'a, Name> {
    /// The name of column to be displayed to users.
    pub name: Name,
    /// The flags this column will have.
    pub flags: TableColumnFlags,
    /// The width or weight of the given column.
    pub init_width_or_weight: f32,
    /// A user_id, primarily used in sorting operations.
    pub user_id: Id<'a>,
}

impl<'a, Name: AsRef<str>> TableColumnSetup<'a, Name> {
    pub fn new(name: Name) -> Self {
        Self {
            name,
            flags: TableColumnFlags::empty(),
            init_width_or_weight: 0.0,
            user_id: Id::Int(0),
        }
    }
}

/// A wrapper around table sort specs.
///
/// To use this simply, use [conditional_sort] and provide a closure --
/// if you should sort your data, then the closure will be ran and imgui
/// will be informed that your data is sorted.
///
/// For manual control (such as if sorting can fail), use [should_sort] to
/// check if you should sort your data, sort your data using [specs] for information
/// on how to sort it, and then [set_sorted] to indicate that the data is sorted.
///
/// [conditional_sort]: Self::conditional_sort
/// [should_sort]: Self::should_sort
/// [specs]: Self::specs
/// [set_sorted]: Self::set_sorted
pub struct TableSortSpecsMut<'ui>(*mut sys::ImGuiTableSortSpecs, PhantomData<Ui<'ui>>);

impl TableSortSpecsMut<'_> {
    /// Gets the specs for a given sort. In most scenarios, this will be a slice of 1 entry.
    pub fn specs(&self) -> Specs<'_> {
        let value =
            unsafe { std::slice::from_raw_parts((*self.0).Specs, (*self.0).SpecsCount as usize) };

        Specs(value)
    }

    /// Returns true if the data should be sorted.
    pub fn should_sort(&self) -> bool {
        unsafe { (*self.0).SpecsDirty }
    }

    /// Sets the internal flag that the data has been sorted.
    pub fn set_sorted(&mut self) {
        unsafe {
            (*self.0).SpecsDirty = false;
        }
    }

    /// Provide a closure, which will receive the Specs for a sort.
    ///
    /// If you should sort the data, the closure will run, and ImGui will be
    /// told that the data has been sorted.
    ///
    /// If you need manual control over sorting, consider using [should_sort], [specs],
    /// and [set_sorted] youself.
    ///
    /// [should_sort]: Self::should_sort
    /// [specs]: Self::specs
    /// [set_sorted]: Self::set_sorted
    pub fn conditional_sort(mut self, mut f: impl FnMut(Specs<'_>)) {
        let is_dirty = self.should_sort();

        if is_dirty {
            f(self.specs());
        }

        self.set_sorted();
    }
}

/// A wrapper around a slice of [TableColumnSortSpecs].
///
/// This slice may be 0 if [TableFlags::SORT_TRISTATE] is true, may be > 1 is [TableFlags::SORT_MULTI] is true,
/// but is generally == 1.
///
/// Consume this struct as an iterator.
pub struct Specs<'a>(&'a [sys::ImGuiTableColumnSortSpecs]);

impl<'a> Specs<'a> {
    pub fn iter(self) -> impl Iterator<Item = TableColumnSortSpecs<'a>> {
        self.0.iter().map(|v| TableColumnSortSpecs(v))
    }
}

pub struct TableColumnSortSpecs<'a>(&'a sys::ImGuiTableColumnSortSpecs);
impl<'a> TableColumnSortSpecs<'a> {
    /// User id of the column (if specified by a TableSetupColumn() call)
    pub fn column_user_id(&self) -> sys::ImGuiID {
        self.0.ColumnUserID
    }

    /// Index of the column
    pub fn column_idx(&self) -> usize {
        self.0.ColumnIndex as usize
    }

    /// Index within parent [Specs] slice where this was found -- always stored in order starting
    /// from 0, tables sorted on a single criteria will always have a 0 here.
    ///
    /// Generally, you don't need to access this, as it's the same as calling `specs.iter().enumerate()`.
    pub fn sort_order(&self) -> usize {
        self.0.SortOrder as usize
    }

    /// Gets the sort direction for the given column. This will nearly always be `Some` if you
    /// can access it.
    pub fn sort_direction(&self) -> Option<TableSortDirection> {
        match self.0.SortDirection() {
            0 => None,
            1 => Some(TableSortDirection::Ascending),
            2 => Some(TableSortDirection::Descending),
            _ => unimplemented!(),
        }
    }
}

create_token!(
    /// Tracks a table which can be rendered onto, ending with `.end()`
    /// or by dropping.
    pub struct TableToken<'ui>;

    /// Ends the table.
    drop { sys::igEndTable() }
);