-
Notifications
You must be signed in to change notification settings - Fork 5
/
tables.internal.go
2877 lines (2581 loc) · 116 KB
/
tables.internal.go
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
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package imgui
import (
"fmt"
"unsafe"
)
const (
TABLE_DRAW_CHANNEL_BG0 int = 0
TABLE_DRAW_CHANNEL_BG2_FROZEN int = 1
TABLE_DRAW_CHANNEL_NOCLIP int = 2
TABLE_BORDER_SIZE float = 1.0
TABLE_RESIZE_SEPARATOR_HALF_THICKNESS float = 4.0
TABLE_RESIZE_SEPARATOR_FEEDBACK_TIMER float = 0.06
)
func TableSettingsCalcChunkSize(columns_count int) size_t {
return unsafe.Sizeof(ImGuiTableSettings{}) + (size_t)(columns_count)*unsafe.Sizeof(ImGuiTableColumnSettings{})
}
// Clear and initialize empty settings instance
func TableSettingsInit(settings *ImGuiTableSettings, id ImGuiID, columns_count, columns_count_max int) {
*settings = ImGuiTableSettings{}
settings.ID = id
settings.ColumnsCount = (ImGuiTableColumnIdx)(columns_count)
settings.ColumnsCountMax = (ImGuiTableColumnIdx)(columns_count_max)
settings.WantApply = true
}
// Helper
func TableFixFlags(flags ImGuiTableFlags, outer_window *ImGuiWindow) ImGuiTableFlags {
// Adjust flags: set default sizing policy
if (flags & ImGuiTableFlags_SizingMask_) == 0 {
if outer_window.Flags&ImGuiWindowFlags_AlwaysAutoResize != 0 {
flags |= (flags & ImGuiTableFlags_ScrollX) | ImGuiTableFlags_SizingFixedFit
} else {
flags |= (flags & ImGuiTableFlags_ScrollX) | ImGuiTableFlags_SizingStretchSame
}
}
// Adjust flags: enable NoKeepColumnsVisible when using ImGuiTableFlags_SizingFixedSame
if (flags & ImGuiTableFlags_SizingMask_) == ImGuiTableFlags_SizingFixedSame {
flags |= ImGuiTableFlags_NoKeepColumnsVisible
}
// Adjust flags: enforce borders when resizable
if flags&ImGuiTableFlags_Resizable != 0 {
flags |= ImGuiTableFlags_BordersInnerV
}
// Adjust flags: disable NoHostExtendX/NoHostExtendY if we have any scrolling going on
if flags&(ImGuiTableFlags_ScrollX|ImGuiTableFlags_ScrollY) != 0 {
flags &= ^(ImGuiTableFlags_NoHostExtendX | ImGuiTableFlags_NoHostExtendY)
}
// Adjust flags: NoBordersInBodyUntilResize takes priority over NoBordersInBody
if flags&ImGuiTableFlags_NoBordersInBodyUntilResize != 0 {
flags &= ^ImGuiTableFlags_NoBordersInBody
}
// Adjust flags: disable saved settings if there's nothing to save
if (flags & (ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Sortable)) == 0 {
flags |= ImGuiTableFlags_NoSavedSettings
}
// Inherit _NoSavedSettings from top-level window (child windows always have _NoSavedSettings set)
if outer_window.RootWindow.Flags&ImGuiWindowFlags_NoSavedSettings != 0 {
flags |= ImGuiTableFlags_NoSavedSettings
}
return flags
}
// Tables: Candidates for public API
func TableOpenContextMenu(column_n int /*= -1*/) {
var g = GImGui
var table = g.CurrentTable
if column_n == -1 && table.CurrentColumn != -1 { // When called within a column automatically use this one (for consistency)
column_n = table.CurrentColumn
}
if column_n == table.ColumnsCount { // To facilitate using with TableGetHoveredColumn()
column_n = -1
}
IM_ASSERT(column_n >= -1 && column_n < table.ColumnsCount)
if table.Flags&(ImGuiTableFlags_Resizable|ImGuiTableFlags_Reorderable|ImGuiTableFlags_Hideable) != 0 {
table.IsContextPopupOpen = true
table.ContextPopupColumn = (ImGuiTableColumnIdx)(column_n)
table.InstanceInteracted = table.InstanceCurrent
var context_menu_id = ImHashStr("##ContextMenu", 0, table.ID)
OpenPopupEx(context_menu_id, ImGuiPopupFlags_None)
}
}
// 'width' = inner column width, without padding
func TableSetColumnWidth(column_n int, width float) {
var g = GImGui
var table = g.CurrentTable
IM_ASSERT(table != nil && !table.IsLayoutLocked)
IM_ASSERT(column_n >= 0 && column_n < table.ColumnsCount)
var column_0 = &table.Columns[column_n]
var column_0_width = width
// Apply constraints early
// Compare both requested and actual given width to avoid overwriting requested width when column is stuck (minimum size, bounded)
IM_ASSERT(table.MinColumnWidth > 0.0)
var min_width = table.MinColumnWidth
var max_width = ImMax(min_width, TableGetMaxColumnWidth(table, column_n))
column_0_width = ImClamp(column_0_width, min_width, max_width)
if column_0.WidthGiven == column_0_width || column_0.WidthRequest == column_0_width {
return
}
//IMGUI_DEBUG_LOG("TableSetColumnWidth(%d, %.1f.%.1f)\n", column_0_idx, column_0.WidthGiven, column_0_width);
var column_1 *ImGuiTableColumn
if column_0.NextEnabledColumn != -1 {
column_1 = &table.Columns[column_0.NextEnabledColumn]
}
// In this surprisingly not simple because of how we support mixing Fixed and multiple Stretch columns.
// - All fixed: easy.
// - All stretch: easy.
// - One or more fixed + one stretch: easy.
// - One or more fixed + more than one stretch: tricky.
// Qt when manual resize is enabled only support a single _trailing_ stretch column.
// When forwarding resize from Wn| to Fn+1| we need to be considerate of the _NoResize flag on Fn+1.
// FIXME-TABLE: Find a way to rewrite all of this so interactions feel more consistent for the user.
// Scenarios:
// - F1 F2 F3 resize from F1| or F2| -. ok: alter .WidthRequested of Fixed column. Subsequent columns will be offset.
// - F1 F2 F3 resize from F3| -. ok: alter .WidthRequested of Fixed column. If active, ScrollX extent can be altered.
// - F1 F2 W3 resize from F1| or F2| -. ok: alter .WidthRequested of Fixed column. If active, ScrollX extent can be altered, but it doesn't make much sense as the Stretch column will always be minimal size.
// - F1 F2 W3 resize from W3| -. ok: no-op (disabled by Resize Rule 1)
// - W1 W2 W3 resize from W1| or W2| -. ok
// - W1 W2 W3 resize from W3| -. ok: no-op (disabled by Resize Rule 1)
// - W1 F2 F3 resize from F3| -. ok: no-op (disabled by Resize Rule 1)
// - W1 F2 resize from F2| -. ok: no-op (disabled by Resize Rule 1)
// - W1 W2 F3 resize from W1| or W2| -. ok
// - W1 F2 W3 resize from W1| or F2| -. ok
// - F1 W2 F3 resize from W2| -. ok
// - F1 W3 F2 resize from W3| -. ok
// - W1 F2 F3 resize from W1| -. ok: equivalent to resizing |F2. F3 will not move.
// - W1 F2 F3 resize from F2| -. ok
// All resizes from a Wx columns are locking other columns.
// Possible improvements:
// - W1 W2 W3 resize W1| -. to not be stuck, both W2 and W3 would stretch down. Seems possible to fix. Would be most beneficial to simplify resize of all-weighted columns.
// - W3 F1 F2 resize W3| -. to not be stuck past F1|, both F1 and F2 would need to stretch down, which would be lossy or ambiguous. Seems hard to fix.
// [Resize Rule 1] Can't resize from right of right-most visible column if there is any Stretch column. Implemented in TableUpdateLayout().
// If we have all Fixed columns OR resizing a Fixed column that doesn't come after a Stretch one, we can do an offsetting resize.
// This is the preferred resize path
if column_0.Flags&ImGuiTableColumnFlags_WidthFixed != 0 {
if column_1 == nil || table.LeftMostStretchedColumn == -1 || table.Columns[table.LeftMostStretchedColumn].DisplayOrder >= column_0.DisplayOrder {
column_0.WidthRequest = column_0_width
table.IsSettingsDirty = true
return
}
}
// We can also use previous column if there's no next one (this is used when doing an auto-fit on the right-most stretch column)
if column_1 == nil {
if column_0.PrevEnabledColumn != -1 {
column_1 = &table.Columns[column_0.PrevEnabledColumn]
}
}
if column_1 == nil {
return
}
// Resizing from right-side of a Stretch column before a Fixed column forward sizing to left-side of fixed column.
// (old_a + old_b == new_a + new_b) -. (new_a == old_a + old_b - new_b)
var column_1_width = ImMax(column_1.WidthRequest-(column_0_width-column_0.WidthRequest), min_width)
column_0_width = column_0.WidthRequest + column_1.WidthRequest - column_1_width
IM_ASSERT(column_0_width > 0.0 && column_1_width > 0.0)
column_0.WidthRequest = column_0_width
column_1.WidthRequest = column_1_width
if (column_0.Flags|column_1.Flags)&ImGuiTableColumnFlags_WidthStretch != 0 {
TableUpdateColumnsWeightFromWidth(table)
}
table.IsSettingsDirty = true
}
// Note that the NoSortAscending/NoSortDescending flags are processed in TableSortSpecsSanitize(), and they may change/revert
// the value of SortDirection. We could technically also do it here but it would be unnecessary and duplicate code.
func TableSetColumnSortDirection(column_n int, sort_direction ImGuiSortDirection, append_to_sort_specs bool) {
var g = GImGui
var table = g.CurrentTable
if (table.Flags & ImGuiTableFlags_SortMulti) == 0 {
append_to_sort_specs = false
}
if table.Flags&ImGuiTableFlags_SortTristate == 0 {
IM_ASSERT(sort_direction != ImGuiSortDirection_None)
}
var sort_order_max ImGuiTableColumnIdx = 0
if append_to_sort_specs {
for other_column_n := int(0); other_column_n < table.ColumnsCount; other_column_n++ {
sort_order_max = int8(ImMaxInt(int(sort_order_max), int(table.Columns[other_column_n].SortOrder)))
}
}
var column = &table.Columns[column_n]
column.SortDirection = (ImGuiSortDirection)(sort_direction)
if column.SortDirection == ImGuiSortDirection_None {
column.SortOrder = -1
} else if column.SortOrder == -1 || !append_to_sort_specs {
if append_to_sort_specs {
column.SortOrder = sort_order_max + 1
} else {
column.SortOrder = 0
}
}
for other_column_n := int(0); other_column_n < table.ColumnsCount; other_column_n++ {
var other_column = &table.Columns[other_column_n]
if other_column != column && !append_to_sort_specs {
other_column.SortOrder = -1
}
TableFixColumnSortDirection(table, other_column)
}
table.IsSettingsDirty = true
table.IsSortSpecsDirty = true
}
// May use (TableGetColumnFlags() & ImGuiTableColumnFlags_IsHovered) instead. Return hovered column. return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
// Return -1 when table is not hovered. return columns_count if the unused space at the right of visible columns is hovered.
func TableGetHoveredColumn() int {
var g = GImGui
var table = g.CurrentTable
if table == nil {
return -1
}
return (int)(table.HoveredColumnBody)
}
func TableGetHeaderRowHeight() float {
// Caring for a minor edge case:
// Calculate row height, for the unlikely case that some labels may be taller than others.
// If we didn't do that, uneven header height would highlight but smaller one before the tallest wouldn't catch input for all height.
// In your custom header row you may omit this all together and just call TableNextRow() without a height...
var row_height = GetTextLineHeight()
var columns_count = TableGetColumnCount()
for column_n := int(0); column_n < columns_count; column_n++ {
var flags = TableGetColumnFlags(column_n)
if (flags&ImGuiTableColumnFlags_IsEnabled) != 0 && (flags&ImGuiTableColumnFlags_NoHeaderLabel) == 0 {
row_height = ImMax(row_height, CalcTextSize(TableGetColumnName(column_n), true, -1).y)
}
}
row_height += GetStyle().CellPadding.y * 2.0
return row_height
}
// Bg2 is used by Selectable (and possibly other widgets) to render to the background.
// Unlike our Bg0/1 channel which we uses for RowBg/CellBg/Borders and where we guarantee all shapes to be CPU-clipped, the Bg2 channel being widgets-facing will rely on regular ClipRect.
func TablePushBackgroundChannel() {
var g = GImGui
var window = g.CurrentWindow
var table = g.CurrentTable
// Optimization: avoid SetCurrentChannel() + PushClipRect()
table.HostBackupInnerClipRect = window.ClipRect
SetWindowClipRectBeforeSetChannel(window, &table.Bg2ClipRectForDrawCmd)
table.DrawSplitter.SetCurrentChannel(window.DrawList, int(table.Bg2DrawChannelCurrent))
}
func TablePopBackgroundChannel() {
var g = GImGui
var window = g.CurrentWindow
var table = g.CurrentTable
var column = &table.Columns[table.CurrentColumn]
// Optimization: avoid PopClipRect() + SetCurrentChannel()
SetWindowClipRectBeforeSetChannel(window, &table.HostBackupInnerClipRect)
table.DrawSplitter.SetCurrentChannel(window.DrawList, int(column.DrawChannelCurrent))
}
// Tables: Internals
func GetCurrentTable() *ImGuiTable { var g = GImGui; return g.CurrentTable }
func TableFindByID(id ImGuiID) *ImGuiTable {
var g = GImGui
return g.Tables[id]
}
func BeginTableEx(name string, id ImGuiID, columns_count int, flags ImGuiTableFlags, outer_size *ImVec2, inner_width float) bool {
var g = GImGui
var outer_window = GetCurrentWindow()
if outer_window.SkipItems { // Consistent with other tables + beneficial side effect that assert on miscalling EndTable() will be more visible.
return false
}
// Sanity checks
IM_ASSERT_USER_ERROR(columns_count > 0 && columns_count <= IMGUI_TABLE_MAX_COLUMNS, "Only 1..64 columns allowed!")
if flags&ImGuiTableFlags_ScrollX != 0 {
IM_ASSERT(inner_width >= 0.0)
}
// If an outer size is specified ahead we will be able to early out when not visible. Exact clipping rules may evolve.
var use_child_window = (flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) != 0
var avail_size = GetContentRegionAvail()
var h float
if use_child_window {
h = ImMax(avail_size.y, 1.0)
}
var actual_outer_size = CalcItemSize(*outer_size, ImMax(avail_size.x, 1.0), h)
var outer_rect = ImRect{outer_window.DC.CursorPos, outer_window.DC.CursorPos.Add(actual_outer_size)}
if use_child_window && IsClippedEx(&outer_rect, 0, false) {
ItemSizeRect(&outer_rect, 0)
return false
}
// Acquire storage for the table
var table = g.Tables[id]
if table == nil {
table = &ImGuiTable{}
if g.Tables == nil {
g.Tables = make(map[uint32]*ImGuiTable)
}
g.Tables[id] = table
}
var instance_no int
if table.LastFrameActive == g.FrameCount {
instance_no = int(table.InstanceCurrent + 1)
}
var instance_id = id + uint(instance_no)
var table_last_flags = table.Flags
if instance_no > 0 {
IM_ASSERT_USER_ERROR(table.ColumnsCount == columns_count, "BeginTable(): Cannot change columns count mid-frame while preserving same ID")
}
// Acquire temporary buffers
var table_idx = int(id)
g.CurrentTableStackIdx++
if g.CurrentTableStackIdx+1 > int(len(g.TablesTempDataStack)) {
g.TablesTempDataStack = append(g.TablesTempDataStack, NewImGuiTableTempData())
}
var temp_data = &g.TablesTempDataStack[g.CurrentTableStackIdx]
table.TempData = &g.TablesTempDataStack[g.CurrentTableStackIdx]
temp_data.TableIndex = table_idx
table.DrawSplitter = &table.TempData.DrawSplitter
table.DrawSplitter.Clear()
// Fix flags
table.IsDefaultSizingPolicy = (flags & ImGuiTableFlags_SizingMask_) == 0
flags = TableFixFlags(flags, outer_window)
// Initialize
table.ID = id
table.Flags = flags
table.InstanceCurrent = (ImS16)(instance_no)
table.LastFrameActive = g.FrameCount
table.OuterWindow = outer_window
table.InnerWindow = outer_window
table.ColumnsCount = columns_count
table.IsLayoutLocked = false
table.InnerWidth = inner_width
temp_data.UserOuterSize = *outer_size
// When not using a child window, WorkRect.Max will grow as we append contents.
if use_child_window {
// Ensure no vertical scrollbar appears if we only want horizontal one, to make flag consistent
// (we have no other way to disable vertical scrollbar of a window while keeping the horizontal one showing)
var override_content_size = ImVec2{FLT_MAX, FLT_MAX}
if (flags&ImGuiTableFlags_ScrollX) != 0 && (flags&ImGuiTableFlags_ScrollY) == 0 {
override_content_size.y = FLT_MIN
}
// Ensure specified width (when not specified, Stretched columns will act as if the width == OuterWidth and
// never lead to any scrolling). We don't handle inner_width < 0.0f, we could potentially use it to right-align
// based on the right side of the child window work rect, which would require knowing ahead if we are going to
// have decoration taking horizontal spaces (typically a vertical scrollbar).
if (flags&ImGuiTableFlags_ScrollX) != 0 && inner_width > 0.0 {
override_content_size.x = inner_width
}
if override_content_size.x != FLT_MAX || override_content_size.y != FLT_MAX {
var x, y float
if override_content_size.x != FLT_MAX {
x = override_content_size.x
}
if override_content_size.y != FLT_MAX {
y = override_content_size.y
}
SetNextWindowContentSize(ImVec2{x, y})
}
// Reset scroll if we are reactivating it
if (table_last_flags & (ImGuiTableFlags_ScrollX | ImGuiTableFlags_ScrollY)) == 0 {
SetNextWindowScroll(&ImVec2{0.0, 0.0})
}
// Create scrolling region (without border and zero window padding)
var child_flags = ImGuiWindowFlags_None
if (flags & ImGuiTableFlags_ScrollX) != 0 {
child_flags = ImGuiWindowFlags_HorizontalScrollbar
}
size := outer_rect.GetSize()
BeginChildEx(name, instance_id, &size, false, child_flags)
table.InnerWindow = g.CurrentWindow
table.WorkRect = table.InnerWindow.WorkRect
table.OuterRect = table.InnerWindow.Rect()
table.InnerRect = table.InnerWindow.InnerRect
IM_ASSERT(table.InnerWindow.WindowPadding.x == 0.0 && table.InnerWindow.WindowPadding.y == 0.0 && table.InnerWindow.WindowBorderSize == 0.0)
} else {
// For non-scrolling tables, WorkRect == OuterRect == InnerRect.
// But at this point we do NOT have a correct value for .Max.y (unless a height has been explicitly passed in). It will only be updated in EndTable().
table.WorkRect = outer_rect
table.OuterRect = outer_rect
table.InnerRect = outer_rect
}
// Push a standardized ID for both child-using and not-child-using tables
PushOverrideID(instance_id)
// Backup a copy of host window members we will modify
var inner_window = table.InnerWindow
table.HostIndentX = inner_window.DC.Indent.x
table.HostClipRect = inner_window.ClipRect
table.HostSkipItems = inner_window.SkipItems
temp_data.HostBackupWorkRect = inner_window.WorkRect
temp_data.HostBackupParentWorkRect = inner_window.ParentWorkRect
temp_data.HostBackupColumnsOffset = outer_window.DC.ColumnsOffset
temp_data.HostBackupPrevLineSize = inner_window.DC.PrevLineSize
temp_data.HostBackupCurrLineSize = inner_window.DC.CurrLineSize
temp_data.HostBackupCursorMaxPos = inner_window.DC.CursorMaxPos
temp_data.HostBackupItemWidth = outer_window.DC.ItemWidth
temp_data.HostBackupItemWidthStackSize = int(len(outer_window.DC.ItemWidthStack))
inner_window.DC.PrevLineSize = ImVec2{0.0, 0.0}
inner_window.DC.CurrLineSize = ImVec2{0.0, 0.0}
// Padding and Spacing
// - None ........Content..... Pad .....Content........
// - PadOuter | Pad ..Content..... Pad .....Content.. Pad |
// - PadInner ........Content.. Pad | Pad ..Content........
// - PadOuter+PadInner | Pad ..Content.. Pad | Pad ..Content.. Pad |
var pad_outer_x = false
if (flags & ImGuiTableFlags_NoPadOuterX) == 0 {
if flags&ImGuiTableFlags_PadOuterX != 0 {
pad_outer_x = true
} else {
pad_outer_x = (flags & ImGuiTableFlags_BordersOuterV) != 0
}
}
var pad_inner_x = false
if (flags & ImGuiTableFlags_NoPadInnerX) == 0 {
pad_inner_x = true
}
var inner_spacing_for_border float = 0.0
if flags&ImGuiTableFlags_BordersInnerV != 0 {
inner_spacing_for_border = TABLE_BORDER_SIZE
}
var inner_spacing_explicit float
if pad_inner_x && (flags&ImGuiTableFlags_BordersInnerV) == 0 {
inner_spacing_explicit = g.Style.CellPadding.x
}
var inner_padding_explicit float
if pad_inner_x && (flags&ImGuiTableFlags_BordersInnerV) != 0 {
inner_padding_explicit = g.Style.CellPadding.x
}
table.CellSpacingX1 = inner_spacing_explicit + inner_spacing_for_border
table.CellSpacingX2 = inner_spacing_explicit
table.CellPaddingX = inner_padding_explicit
table.CellPaddingY = g.Style.CellPadding.y
var outer_padding_for_border float
if flags&ImGuiTableFlags_BordersOuterV != 0 {
outer_padding_for_border = TABLE_BORDER_SIZE
}
var outer_padding_explicit float
if pad_outer_x {
outer_padding_explicit = g.Style.CellPadding.x
}
table.OuterPaddingX = (outer_padding_for_border + outer_padding_explicit) - table.CellPaddingX
table.CurrentColumn = -1
table.CurrentRow = -1
table.RowBgColorCounter = 0
table.LastRowFlags = ImGuiTableRowFlags_None
table.InnerClipRect = inner_window.ClipRect
if inner_window == outer_window {
table.InnerClipRect = table.WorkRect
}
table.InnerClipRect.ClipWith(table.WorkRect) // We need this to honor inner_width
table.InnerClipRect.ClipWithFull(table.HostClipRect)
table.InnerClipRect.Max.y = inner_window.ClipRect.Max.y
if (flags & ImGuiTableFlags_NoHostExtendY) != 0 {
table.InnerClipRect.Max.y = ImMin(table.InnerClipRect.Max.y, inner_window.WorkRect.Max.y)
}
table.RowPosY1 = table.WorkRect.Min.y
table.RowPosY2 = table.WorkRect.Min.y // This is needed somehow
table.RowTextBaseline = 0.0 // This will be cleared again by TableBeginRow()
table.FreezeRowsRequest = 0
table.FreezeRowsCount = 0 // This will be setup by TableSetupScrollFreeze(), if any
table.FreezeColumnsRequest = 0
table.FreezeColumnsCount = 0
table.IsUnfrozenRows = true
table.DeclColumnsCount = 0
// Using opaque colors facilitate overlapping elements of the grid
table.BorderColorStrong = GetColorU32FromID(ImGuiCol_TableBorderStrong, 1)
table.BorderColorLight = GetColorU32FromID(ImGuiCol_TableBorderLight, 1)
// Make table current
g.CurrentTable = table
outer_window.DC.CurrentTableIdx = table_idx
if inner_window != outer_window { // So EndChild() within the inner window can restore the table properly.
inner_window.DC.CurrentTableIdx = table_idx
}
if (table_last_flags&ImGuiTableFlags_Reorderable) != 0 && (flags&ImGuiTableFlags_Reorderable) == 0 {
table.IsResetDisplayOrderRequest = true
}
// Mark as used
if g.TablesLastTimeActive == nil {
g.TablesLastTimeActive = make(map[int32]float32)
}
g.TablesLastTimeActive[table_idx] = (float)(g.Time)
temp_data.LastTimeActive = (float)(g.Time)
table.MemoryCompacted = false
// Setup memory buffer (clear data if columns count changed)
var old_columns_to_preserve []ImGuiTableColumn = nil
var old_columns_raw_data any = nil
var old_columns_count = int(len(table.Columns))
if old_columns_count != 0 && old_columns_count != columns_count {
// Attempt to preserve width on column count change (#4046)
old_columns_to_preserve = table.Columns
old_columns_raw_data = table.RawData
table.RawData = nil
}
if table.RawData == nil {
TableBeginInitMemory(table, columns_count)
table.IsInitializing = true
table.IsSettingsRequestLoad = true
}
if table.IsResetAllRequest {
TableResetSettings(table)
}
if table.IsInitializing {
// Initialize
table.SettingsOffset = -1
table.IsSortSpecsDirty = true
table.InstanceInteracted = -1
table.ContextPopupColumn = -1
table.ReorderColumn = -1
table.ResizedColumn = -1
table.LastResizedColumn = -1
table.AutoFitSingleColumn = -1
table.HoveredColumnBody = -1
table.HoveredColumnBorder = -1
for n := range table.Columns {
var column = &table.Columns[n]
if old_columns_to_preserve != nil && int(n) < old_columns_count {
// FIXME: We don't attempt to preserve column order in this path.
*column = old_columns_to_preserve[n]
} else {
var width_auto = column.WidthAuto
*column = NewImGuiTableColumn()
column.WidthAuto = width_auto
column.IsPreserveWidthAuto = true // Preserve WidthAuto when reinitializing a live table: not technically necessary but remove a visible flicker
column.IsEnabled = true
column.IsUserEnabled = true
column.IsUserEnabledNextFrame = true
}
column.DisplayOrder = (ImGuiTableColumnIdx)(n)
table.DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)(n)
}
}
if old_columns_raw_data != nil {
old_columns_raw_data = nil
}
// Load settings
if table.IsSettingsRequestLoad {
TableLoadSettings(table)
}
// Handle DPI/font resize
// This is designed to facilitate DPI changes with the assumption that e.g. style.CellPadding has been scaled as well.
// It will also react to changing fonts with mixed results. It doesn't need to be perfect but merely provide a decent transition.
// FIXME-DPI: Provide consistent standards for reference size. Perhaps using g.CurrentDpiScale would be more self explanatory.
// This is will lead us to non-rounded WidthRequest in columns, which should work but is a poorly tested path.
var new_ref_scale_unit = g.FontSize // g.Font.GetCharAdvance('A') ?
if table.RefScale != 0.0 && table.RefScale != new_ref_scale_unit {
var scale_factor = new_ref_scale_unit / table.RefScale
//IMGUI_DEBUG_LOG("[table] %08X RefScaleUnit %.3f . %.3f, scaling width by %.3f\n", table.ID, table.RefScaleUnit, new_ref_scale_unit, scale_factor);
for n := int(0); n < columns_count; n++ {
table.Columns[n].WidthRequest = table.Columns[n].WidthRequest * scale_factor
}
}
table.RefScale = new_ref_scale_unit
// Disable output until user calls TableNextRow() or TableNextColumn() leading to the TableUpdateLayout() call..
// This is not strictly necessary but will reduce cases were "out of table" output will be misleading to the user.
// Because we cannot safely assert in EndTable() when no rows have been created, this seems like our best option.
inner_window.SkipItems = true
// Clear names
// At this point the .NameOffset field of each column will be invalid until TableUpdateLayout() or the first call to TableSetupColumn()
if int(len(table.ColumnsNames)) > 0 {
table.ColumnsNames = table.ColumnsNames[:0]
}
// Apply queued resizing/reordering/hiding requests
TableBeginApplyRequests(table)
return true
}
// For reference, the average total _allocation count_ for a table is:
// + 0 (for ImGuiTable instance, we are pooling allocations in g.Tables)
// + 1 (for table.RawData allocated below)
// + 1 (for table.ColumnsNames, if names are used)
// + 1 (for table.Splitter._Channels)
// + 2 * active_channels_count (for ImDrawCmd and ImDrawIdx buffers inside channels)
// Where active_channels_count is variable but often == columns_count or columns_count + 1, see TableSetupDrawChannels() for details.
// Unused channels don't perform their +2 allocations.
func TableBeginInitMemory(e *ImGuiTable, columns_count int) {
// noop, will be handled by span helpers
}
// Apply queued resizing/reordering/hiding requests
func TableBeginApplyRequests(table *ImGuiTable) {
// Handle resizing request
// (We process this at the first TableBegin of the frame)
// FIXME-TABLE: Contains columns if our work area doesn't allow for scrolling?
if table.InstanceCurrent == 0 {
if table.ResizedColumn != -1 && table.ResizedColumnNextWidth != FLT_MAX {
TableSetColumnWidth(int(table.ResizedColumn), table.ResizedColumnNextWidth)
}
table.LastResizedColumn = table.ResizedColumn
table.ResizedColumnNextWidth = FLT_MAX
table.ResizedColumn = -1
// Process auto-fit for single column, which is a special case for stretch columns and fixed columns with FixedSame policy.
// FIXME-TABLE: Would be nice to redistribute available stretch space accordingly to other weights, instead of giving it all to siblings.
if table.AutoFitSingleColumn != -1 {
TableSetColumnWidth(int(table.AutoFitSingleColumn), table.Columns[table.AutoFitSingleColumn].WidthAuto)
table.AutoFitSingleColumn = -1
}
}
// Handle reordering request
// Note: we don't clear ReorderColumn after handling the request.
if table.InstanceCurrent == 0 {
if table.HeldHeaderColumn == -1 && table.ReorderColumn != -1 {
table.ReorderColumn = -1
}
table.HeldHeaderColumn = -1
if table.ReorderColumn != -1 && table.ReorderColumnDir != 0 {
// We need to handle reordering across hidden columns.
// In the configuration below, moving C to the right of E will lead to:
// ... C [D] E --. ... [D] E C (Column name/index)
// ... 2 3 4 ... 2 3 4 (Display order)
var reorder_dir = int(table.ReorderColumnDir)
IM_ASSERT(reorder_dir == -1 || reorder_dir == +1)
IM_ASSERT(table.Flags&ImGuiTableFlags_Reorderable != 0)
var src_column = &table.Columns[table.ReorderColumn]
var dst_column *ImGuiTableColumn
if reorder_dir == -1 {
dst_column = &table.Columns[src_column.PrevEnabledColumn]
} else {
dst_column = &table.Columns[src_column.NextEnabledColumn]
}
var src_order = int(src_column.DisplayOrder)
var dst_order = int(dst_column.DisplayOrder)
src_column.DisplayOrder = (ImGuiTableColumnIdx)(dst_order)
for order_n := src_order + reorder_dir; order_n != dst_order+reorder_dir; order_n += reorder_dir {
table.Columns[table.DisplayOrderToIndex[order_n]].DisplayOrder -= (ImGuiTableColumnIdx)(reorder_dir)
}
IM_ASSERT(int(dst_column.DisplayOrder) == dst_order-reorder_dir)
// Display order is stored in both columns.IndexDisplayOrder and table.DisplayOrder[],
// rebuild the later from the former.
for column_n := int(0); column_n < table.ColumnsCount; column_n++ {
table.DisplayOrderToIndex[table.Columns[column_n].DisplayOrder] = (ImGuiTableColumnIdx)(column_n)
}
table.ReorderColumnDir = 0
table.IsSettingsDirty = true
}
}
// Handle display order reset request
if table.IsResetDisplayOrderRequest {
for n := int(0); n < table.ColumnsCount; n++ {
table.DisplayOrderToIndex[n] = (ImGuiTableColumnIdx)(n)
table.Columns[n].DisplayOrder = (ImGuiTableColumnIdx)(n)
}
table.IsResetDisplayOrderRequest = false
table.IsSettingsDirty = true
}
}
// Adjust flags: default width mode + stretch columns are not allowed when auto extending
func TableSetupColumnFlags(table *ImGuiTable, column *ImGuiTableColumn, flags_in ImGuiTableColumnFlags) {
var flags = flags_in
// Sizing Policy
if (flags & ImGuiTableColumnFlags_WidthMask_) == 0 {
var table_sizing_policy = (table.Flags & ImGuiTableFlags_SizingMask_)
if table_sizing_policy == ImGuiTableFlags_SizingFixedFit || table_sizing_policy == ImGuiTableFlags_SizingFixedSame {
flags |= ImGuiTableColumnFlags_WidthFixed
} else {
flags |= ImGuiTableColumnFlags_WidthStretch
}
} else {
IM_ASSERT(ImIsPowerOfTwoInt(int(flags & ImGuiTableColumnFlags_WidthMask_))) // Check that only 1 of each set is used.
}
// Resize
if (table.Flags & ImGuiTableFlags_Resizable) == 0 {
flags |= ImGuiTableColumnFlags_NoResize
}
// Sorting
if (flags&ImGuiTableColumnFlags_NoSortAscending) != 0 && (flags&ImGuiTableColumnFlags_NoSortDescending) != 0 {
flags |= ImGuiTableColumnFlags_NoSort
}
// Indentation
if (flags & ImGuiTableColumnFlags_IndentMask_) == 0 {
if column == &table.Columns[0] {
flags |= ImGuiTableColumnFlags_IndentEnable
} else {
flags |= ImGuiTableColumnFlags_IndentDisable
}
}
// Alignment
//if ((flags & ImGuiTableColumnFlags_AlignMask_) == 0)
// flags |= ImGuiTableColumnFlags_AlignCenter;
//IM_ASSERT(ImIsPowerOfTwo(flags & ImGuiTableColumnFlags_AlignMask_)); // Check that only 1 of each set is used.
// Preserve status flags
column.Flags = flags | (column.Flags & ImGuiTableColumnFlags_StatusMask_)
// Build an ordered list of available sort directions
column.SortDirectionsAvailCount = 0
column.SortDirectionsAvailMask = 0
column.SortDirectionsAvailList = 0
if (table.Flags & ImGuiTableFlags_Sortable) != 0 {
var count, mask, list int
if (flags&ImGuiTableColumnFlags_PreferSortAscending) != 0 && (flags&ImGuiTableColumnFlags_NoSortAscending) == 0 {
mask |= 1 << ImGuiSortDirection_Ascending
list |= int(ImGuiSortDirection_Ascending) << (count << 1)
count++
}
if (flags&ImGuiTableColumnFlags_PreferSortDescending) != 0 && (flags&ImGuiTableColumnFlags_NoSortDescending) == 0 {
mask |= 1 << ImGuiSortDirection_Descending
list |= int(ImGuiSortDirection_Descending) << (count << 1)
count++
}
if (flags&ImGuiTableColumnFlags_PreferSortAscending) == 0 && (flags&ImGuiTableColumnFlags_NoSortAscending) == 0 {
mask |= 1 << ImGuiSortDirection_Ascending
list |= int(ImGuiSortDirection_Ascending) << (count << 1)
count++
}
if (flags&ImGuiTableColumnFlags_PreferSortDescending) == 0 && (flags&ImGuiTableColumnFlags_NoSortDescending) == 0 {
mask |= 1 << ImGuiSortDirection_Descending
list |= int(ImGuiSortDirection_Descending) << (count << 1)
count++
}
if (table.Flags&ImGuiTableFlags_SortTristate) != 0 || count == 0 {
mask |= 1 << ImGuiSortDirection_None
count++
}
column.SortDirectionsAvailList = (ImU8)(list)
column.SortDirectionsAvailMask = (ImU8)(mask)
column.SortDirectionsAvailCount = (ImU8)(count)
TableFixColumnSortDirection(table, column)
}
}
// Allocate draw channels. Called by TableUpdateLayout()
// - We allocate them following storage order instead of display order so reordering columns won't needlessly
// increase overall dormant memory cost.
// - We isolate headers draw commands in their own channels instead of just altering clip rects.
// This is in order to facilitate merging of draw commands.
// - After crossing FreezeRowsCount, all columns see their current draw channel changed to a second set of channels.
// - We only use the dummy draw channel so we can push a null clipping rectangle into it without affecting other
// channels, while simplifying per-row/per-cell overhead. It will be empty and discarded when merged.
// - We allocate 1 or 2 background draw channels. This is because we know TablePushBackgroundChannel() is only used for
// horizontal spanning. If we allowed vertical spanning we'd need one background draw channel per merge group (1-4).
//
// Draw channel allocation (before merging):
// - NoClip -. 2+D+1 channels: bg0/1 + bg2 + foreground (same clip rect == always 1 draw call)
// - Clip -. 2+D+N channels
// - FreezeRows -. 2+D+N*2 (unless scrolling value is zero)
// - FreezeRows || FreezeColunns -. 3+D+N*2 (unless scrolling value is zero)
// Where D is 1 if any column is clipped or hidden (dummy channel) otherwise 0.
func TableSetupDrawChannels(table *ImGuiTable) {
var freeze_row_multiplier int = 1
if table.FreezeRowsCount > 0 {
freeze_row_multiplier = 2
}
var channels_for_row = int(table.ColumnsEnabledCount)
if table.Flags&ImGuiTableFlags_NoClip != 0 {
channels_for_row = 1
}
var channels_for_bg = 1 + 1*freeze_row_multiplier
var channels_for_dummy int
if int(table.ColumnsEnabledCount) < table.ColumnsCount || table.VisibleMaskByIndex != table.EnabledMaskByIndex {
channels_for_dummy = 1
}
var channels_total = channels_for_bg + (channels_for_row * freeze_row_multiplier) + channels_for_dummy
table.DrawSplitter.Split(table.InnerWindow.DrawList, channels_total)
var ch int = -1
if channels_for_dummy > 0 {
ch = channels_total - 1
}
table.DummyDrawChannel = (ImGuiTableDrawChannelIdx)(ch)
table.Bg2DrawChannelCurrent = uint8(TABLE_DRAW_CHANNEL_BG2_FROZEN)
if table.FreezeRowsCount > 0 {
table.Bg2DrawChannelUnfrozen = uint8(2 + channels_for_row)
} else {
table.Bg2DrawChannelUnfrozen = uint8(TABLE_DRAW_CHANNEL_BG2_FROZEN)
}
var draw_channel_current int = 2
for column_n := int(0); column_n < table.ColumnsCount; column_n++ {
table.spanColumns(column_n)
var column = &table.Columns[column_n]
if column.IsVisibleX && column.IsVisibleY {
column.DrawChannelFrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current)
var ch int
if table.FreezeRowsCount > 0 {
ch = channels_for_row + 1
}
column.DrawChannelUnfrozen = (ImGuiTableDrawChannelIdx)(draw_channel_current + ch)
if (table.Flags & ImGuiTableFlags_NoClip) == 0 {
draw_channel_current++
}
} else {
column.DrawChannelFrozen = table.DummyDrawChannel
column.DrawChannelUnfrozen = table.DummyDrawChannel
}
column.DrawChannelCurrent = column.DrawChannelFrozen
}
// Initial draw cmd starts with a BgClipRect that matches the one of its host, to facilitate merge draw commands by default.
// All our cell highlight are manually clipped with BgClipRect. When unfreezing it will be made smaller to fit scrolling rect.
// (This technically isn't part of setting up draw channels, but is reasonably related to be done here)
table.BgClipRect = table.InnerClipRect
table.Bg0ClipRectForDrawCmd = table.OuterWindow.ClipRect
table.Bg2ClipRectForDrawCmd = table.HostClipRect
IM_ASSERT(table.BgClipRect.Min.y <= table.BgClipRect.Max.y)
}
// helper for the span allocator. called when setting an index
// of table.DisplayOrderToIndex
func (table *ImGuiTable) spanDisplayOrderToIndex(order_n int) {
displayOrderLen := int(len(table.DisplayOrderToIndex))
// check if that index already exists
if order_n <= displayOrderLen-1 {
return
}
// add missing items
for i := int(0); i < (order_n+1)-displayOrderLen; i++ {
table.DisplayOrderToIndex = append(table.DisplayOrderToIndex, 0)
}
}
// helper for the span allocator. called when setting an index
// of table.Columns
func (table *ImGuiTable) spanColumns(column_n int) {
columnsLen := int(len(table.Columns))
// check if that index already exists
if int(column_n) <= columnsLen-1 {
return
}
// add missing items
for i := int(0); i < (int(column_n)+1)-columnsLen; i++ {
table.Columns = append(table.Columns, NewImGuiTableColumn())
}
}
// Layout columns for the frame. This is in essence the followup to BeginTable().
// Runs on the first call to TableNextRow(), to give a chance for TableSetupColumn() to be called first.
// FIXME-TABLE: Our width (and therefore our WorkRect) will be minimal in the first frame for _WidthAuto columns.
// Increase feedback side-effect with widgets relying on WorkRect.Max.x... Maybe provide a default distribution for _WidthAuto columns?
func TableUpdateLayout(table *ImGuiTable) {
var g = GImGui
IM_ASSERT(!table.IsLayoutLocked)
var table_sizing_policy = (table.Flags & ImGuiTableFlags_SizingMask_)
table.IsDefaultDisplayOrder = true
table.ColumnsEnabledCount = 0
table.EnabledMaskByIndex = 0x00
table.EnabledMaskByDisplayOrder = 0x00
table.LeftMostEnabledColumn = -1
table.MinColumnWidth = ImMax(1.0, g.Style.FramePadding.x*1.0) // g.Style.ColumnsMinSpacing; // FIXME-TABLE
// [Part 1] Apply/lock Enabled and Order states. Calculate auto/ideal width for columns. Count fixed/stretch columns.
// Process columns in their visible orders as we are building the Prev/Next indices.
var count_fixed int = 0 // Number of columns that have fixed sizing policies
var count_stretch int = 0 // Number of columns that have stretch sizing policies
var prev_visible_column_idx int = -1
var has_auto_fit_request = false
var has_resizable = false
var stretch_sum_width_auto float = 0.0
var fixed_max_width_auto float = 0.0
for order_n := int(0); order_n < table.ColumnsCount; order_n++ {
table.spanDisplayOrderToIndex(order_n)
var column_n = table.DisplayOrderToIndex[order_n]
if int(column_n) != order_n {
table.IsDefaultDisplayOrder = false
}
table.spanColumns(int(column_n))
var column = &table.Columns[column_n]
// Clear column setup if not submitted by user. Currently we make it mandatory to call TableSetupColumn() every frame.
// It would easily work without but we're not ready to guarantee it since e.g. names need resubmission anyway.
// We take a slight shortcut but in theory we could be calling TableSetupColumn() here with dummy values, it should yield the same effect.
if table.DeclColumnsCount <= column_n {
TableSetupColumnFlags(table, column, ImGuiTableColumnFlags_None)
column.NameOffset = -1
column.UserID = 0
column.InitStretchWeightOrWidth = -1.0
}
// Update Enabled state, mark settings and sort specs dirty
if (table.Flags&ImGuiTableFlags_Hideable) == 0 || (column.Flags&ImGuiTableColumnFlags_NoHide) != 0 {
column.IsUserEnabledNextFrame = true
}
if column.IsUserEnabled != column.IsUserEnabledNextFrame {
column.IsUserEnabled = column.IsUserEnabledNextFrame
table.IsSettingsDirty = true
}
column.IsEnabled = column.IsUserEnabled && (column.Flags&ImGuiTableColumnFlags_Disabled) == 0
if column.SortOrder != -1 && !column.IsEnabled {
table.IsSortSpecsDirty = true
}
if column.SortOrder > 0 && (table.Flags&ImGuiTableFlags_SortMulti) == 0 {
table.IsSortSpecsDirty = true
}
// Auto-fit unsized columns
var start_auto_fit = (column.StretchWeight < 0.0)
if column.Flags&ImGuiTableColumnFlags_WidthFixed != 0 {
start_auto_fit = (column.WidthRequest < 0.0)
}
if start_auto_fit {
column.AutoFitQueue = (1 << 3) - 1
column.CannotSkipItemsQueue = (1 << 3) - 1 // Fit for three frames
}
if !column.IsEnabled {
column.IndexWithinEnabledSet = -1
continue
}
// Mark as enabled and link to previous/next enabled column
column.PrevEnabledColumn = (ImGuiTableColumnIdx)(prev_visible_column_idx)
column.NextEnabledColumn = -1
if prev_visible_column_idx != -1 {
table.spanColumns(prev_visible_column_idx)
table.Columns[prev_visible_column_idx].NextEnabledColumn = (ImGuiTableColumnIdx)(column_n)
} else {
table.LeftMostEnabledColumn = (ImGuiTableColumnIdx)(column_n)
}
column.IndexWithinEnabledSet = table.ColumnsEnabledCount
table.ColumnsEnabledCount++
table.EnabledMaskByIndex |= (ImU64)(1 << column_n)
displayOrderShift := column.DisplayOrder
if displayOrderShift < 0 {
displayOrderShift = 0
}
table.EnabledMaskByDisplayOrder |= (ImU64)(1 << displayOrderShift)
prev_visible_column_idx = int(column_n)
// FIXME (port): figure out this panic
// IM_ASSERT(column.IndexWithinEnabledSet <= column.DisplayOrder)
// Calculate ideal/auto column width (that's the width required for all contents to be visible without clipping)
// Combine width from regular rows + width from headers unless requested not to.
if !column.IsPreserveWidthAuto {
column.WidthAuto = TableGetColumnWidthAuto(table, column)
}