8.19.32. data_table
ImGui’s tables API — BeginTable / EndTable with body-internal
TableSetupColumn / TableHeadersRow / TableNextRow /
TableSetColumnIndex / TableNextColumn cursor primitives — lives
behind one boost container plus a family of snake_case pass-throughs in
imgui/imgui_table_builtin. The container is named data_table (not
table) because table is a daslang reserved keyword for the
table<K;V> type constructor.
The container takes the ImGui id, the column count, the flags, an outer
size and an inner width — same five arguments the cpp BeginTable takes —
and brackets the matching EndTable. Inside the body, the row/column
cursor calls are plain pass-throughs that resolve in the
imgui_table_builtin module namespace (so the project-wide lint, which
forbids raw imgui::* calls in user code, stays satisfied without an
allow-list extension).
Source: modules/dasImgui/examples/tutorial/data_table.das.
8.19.32.1. Walkthrough
The recording drives the sortable shape by clicking the real column headers:
clicking Name flips the active sort ascending to descending; clicking
Type replaces the key and re-groups the rows by type; and Shift+clicking
Value adds it as a secondary key (the 2 badge), so within each type the
rows break ties by value. Each click is verified against the re-rendered cell
text, so a header click that failed to re-sort would abort the recording.
1options gen2
2options _comment_hygiene = true
3options gc
4
5require imgui
6require imgui_app
7require opengl/opengl_boost
8require live/glfw_live
9require live/live_api
10require live/live_commands
11require live/live_vars
12require live_host
13require imgui/imgui_live
14require imgui/imgui_boost_runtime
15require imgui/imgui_boost_v2
16require imgui/imgui_widgets_builtin
17require imgui/imgui_containers_builtin
18require imgui/imgui_table_builtin
19require imgui/imgui_visual_aids
20
21// =============================================================================
22// TUTORIAL: data_table — boost container for ImGui's tables API.
23//
24// `BeginTable` / `EndTable` and the body-internal cursor calls
25// (TableSetupColumn, TableHeadersRow, TableNextRow, TableSetColumnIndex,
26// TableNextColumn) live behind one container + six snake_case pass-throughs
27// in `imgui/imgui_table_builtin`. The container is named `data_table`
28// (not `table`, which is a daslang reserved keyword for `table<K;V>`).
29//
30// data_table(MY_TABLE, (text = "##split", columns = 3, flags = ...,
31// outer_size = float2(0,0), inner_width = 0.0f)) {
32// table_setup_column("Name")
33// table_setup_column("Type")
34// table_setup_column("Value")
35// table_headers_row()
36// for (row in rows) {
37// table_next_row()
38// table_set_column_index(0); text(NAME[row], (text = ...))
39// table_set_column_index(1); text(TYPE[row], (text = ...))
40// table_set_column_index(2); text(VAL[row], (text = ...))
41// }
42// }
43//
44// `TableState` echoes per-call config (columns / flags / outer_size /
45// inner_width). `sort_specs()` (block helper) yields the per-frame sort
46// spec list — see the Sortable tables section below. Multi-select
47// hand-off and custom row-bg callbacks would extend the state
48// additively if added later.
49//
50// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/data_table.das
51// LIVE: daslang-live modules/dasImgui/examples/tutorial/data_table.das
52// =============================================================================
53
54struct private Row {
55 name : string
56 kind : string
57 value : string
58}
59
60var private ROWS <- [
61 Row(name = "alpha", kind = "int", value = "42"),
62 Row(name = "beta", kind = "float", value = "3.14"),
63 Row(name = "gamma", kind = "string", value = "hello"),
64 Row(name = "delta", kind = "bool", value = "true"),
65 Row(name = "epsilon", kind = "int", value = "-7"),
66 Row(name = "zeta", kind = "float", value = "0.001")
67]
68
69// Stable column identifiers passed to table_setup_column(.., user_id=...).
70// The sort comparator dispatches on user_id (not column_index) so the
71// sort stays correct when the user reorders columns via drag.
72let private COL_NAME = 0x0001u
73let private COL_KIND = 0x0002u
74let private COL_VAL = 0x0003u
75
76def private compare_rows(spec : TableSortSpec; a : Row; b : Row) : int {
77 // Returns -1 / 0 / 1 for a-vs-b along the column the spec names.
78 var ord = 0
79 if (spec.column_user_id == COL_NAME) {
80 if (a.name < b.name) {
81 ord = -1
82 } elif (a.name > b.name) {
83 ord = 1
84 }
85 } elif (spec.column_user_id == COL_KIND) {
86 if (a.kind < b.kind) {
87 ord = -1
88 } elif (a.kind > b.kind) {
89 ord = 1
90 }
91 } elif (spec.column_user_id == COL_VAL) {
92 if (a.value < b.value) {
93 ord = -1
94 } elif (a.value > b.value) {
95 ord = 1
96 }
97 }
98 if (spec.sort_direction == ImGuiSortDirection.Descending) {
99 ord = -ord
100 }
101 return ord
102}
103
104def private sort_rows(specs : array<TableSortSpec>) {
105 // Multi-key comparator: first spec that disambiguates a vs b wins; name is the total-order tiebreak.
106 ROWS |> sort() $(a, b) {
107 for (s in specs) {
108 let ord = compare_rows(s, a, b)
109 if (ord != 0) return ord < 0
110 }
111 return a.name < b.name
112 }
113}
114
115var DT_NAME : table<int; NarrativeState>
116var DT_KIND : table<int; NarrativeState>
117var DT_VAL : table<int; NarrativeState>
118
119[export]
120def init() {
121 live_create_window("dasImgui data_table tutorial", 860, 540)
122 live_imgui_init(live_window)
123 // Deterministic FirstUseEver layout for the recording: disable imgui.ini so a
124 // prior session's window pos/size can't drift the framing. Tutorial-scoped on
125 // purpose - not a blanket change to live_imgui_init.
126 DisableIniPersistence()
127 let io & = unsafe(GetIO())
128 GetStyle().FontScaleMain = 1.4
129}
130
131[export]
132def update() {
133 if (!live_begin_frame()) return
134 begin_frame()
135
136 ImGui_ImplGlfw_NewFrame()
137 apply_synth_io_override()
138 NewFrame()
139
140 SetNextWindowPos(ImVec2(30.0f, 30.0f), ImGuiCond.FirstUseEver)
141 SetNextWindowSize(ImVec2(640.0f, 380.0f), ImGuiCond.FirstUseEver)
142 window(DT_WIN, (text = "data_table", closable = false,
143 flags = ImGuiWindowFlags.None)) {
144 text(DT_HEADER, (text = "Three columns, six rows, frozen header. Click headers to sort; Shift+click for multi-column."))
145
146 let tflags = (ImGuiTableFlags.BordersOuter |
147 ImGuiTableFlags.RowBg |
148 ImGuiTableFlags.Resizable |
149 ImGuiTableFlags.Reorderable |
150 ImGuiTableFlags.Hideable |
151 ImGuiTableFlags.Sortable |
152 ImGuiTableFlags.SortMulti |
153 ImGuiTableFlags.ScrollY)
154 data_table(DT_TABLE, (text = "##rows", columns = 3,
155 flags = tflags,
156 outer_size = float2(0.0f, 0.0f),
157 inner_width = 0.0f)) {
158 table_setup_scroll_freeze(0, 1)
159 table_setup_column("Name", ImGuiTableColumnFlags.DefaultSort, 0.0f, COL_NAME)
160 table_setup_column("Type", ImGuiTableColumnFlags.None, 0.0f, COL_KIND)
161 table_setup_column("Value", ImGuiTableColumnFlags.None, 0.0f, COL_VAL)
162 table_headers_row()
163
164 sort_specs() $(specs) {
165 sort_rows(specs)
166 }
167
168 for (i in range(length(ROWS))) {
169 table_next_row()
170 table_set_column_index(0)
171 text(DT_NAME[i], (text = ROWS[i].name))
172 table_set_column_index(1)
173 text(DT_KIND[i], (text = ROWS[i].kind))
174 table_set_column_index(2)
175 text(DT_VAL[i], (text = ROWS[i].value))
176 }
177 }
178 }
179
180 end_of_frame()
181 Render()
182 var w, h : int
183 live_get_framebuffer_size(w, h)
184 glViewport(0, 0, w, h)
185 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
186 glClear(GL_COLOR_BUFFER_BIT)
187 live_imgui_render()
188
189 live_end_frame()
190}
191
192[export]
193def shutdown() {
194 live_imgui_shutdown()
195 live_destroy_window()
196}
197
198[export]
199def main() {
200 init()
201 while (!exit_requested()) {
202 update()
203 maybe_collect_gc()
204 }
205 shutdown()
206}
8.19.32.1.1. Requires
One extra module on top of the baseline boost layer:
imgui/imgui_table_builtin— thedata_tablecontainer plus thetable_*snake_case primitives the body calls into, theTableSortSpecstruct and thesort_specshelper.
8.19.32.1.2. Container shape
data_table follows the same named-tuple convention as the other
containers (window / child / tab_bar):
data_table(MY_TABLE, (text = "##rows", columns = 3,
flags = ImGuiTableFlags.Borders,
outer_size = float2(0.0f, 0.0f),
inner_width = 0.0f)) {
// body
}
text is the ImGui id (use the ##suffix convention to keep it
out of the visible label). columns is the column count;
outer_size = float2(0,0) lets ImGui auto-size; inner_width = 0.0f
means “no explicit inner width” (use the outer width).
8.19.32.1.3. Body primitives
The body cursor calls are plain def public wrappers — same
arguments as the underlying ImGui calls, snake_case names:
table_setup_column(text, flags?, init_width_or_weight?, user_id?)— declare a column before the header row.table_setup_scroll_freeze(cols, rows)— pin the first N columns / M rows during scrolling.table_headers_row()— submit the header row using thetable_setup_columnlabels.table_header(text)/table_angled_headers_row()— a single custom header cell, and the rotated-label header row.table_next_row(flags?, min_row_height?)— start the next row.table_set_column_index(col) -> bool— jump to a specific column; returnstruewhen the column is visible.table_next_column() -> bool— advance one column (or wrap to next row); also returns the visibility bool.table_set_bg_color(target, color, column_n?)— paint a row or cell background.table_get_column_count()/table_get_column_index()/table_get_row_index()/table_get_column_name(col?)/table_get_column_flags(col?)— cursor and layout queries.
TableState (the container’s state struct) echoes per-call config —
columns, flags, outer_size, inner_width — so snapshot consumers can read
the table’s shape without parsing the daslang call site. Multi-select
hand-off remains deferred — pinned ImGui 1.92.6 exposes
BeginMultiSelect / ImGuiMultiSelectIO, but the boost-wrapper
hand-off has not been designed yet. A custom row-bg callback API would
extend the state additively if added later.
8.19.32.1.4. Sortable tables
The tutorial table uses the full sortable shape — Sortable | SortMulti
| Reorderable | Hideable flags on the table, a stable user_id on
each table_setup_column, and a sort_specs() block-arg helper
inside the body that ImGui fires when the sort state goes dirty.
ImGuiTableFlags.Sortableenables single-column sort (click any header). AddingImGuiTableFlags.SortMultienables multi-column sort (Shift+click a second header to append a secondary sort key).table_setup_column("Name", flags, init_width_or_weight, user_id = COL_NAME)tags the column with a stable identifier (auint). The sort comparator dispatches oncolumn_user_idrather thancolumn_index, so the sort stays correct after the user reorders columns via drag.sort_specs() $(specs) { ... }is the wrapper that captures the ImGuiTableGetSortSpecs()data, converts eachImGuiTableColumnSortSpecsentry into a daslang-friendlyTableSortSpec(withcolumn_index,column_user_id,sort_order,sort_direction), invokes the body block with the array, and auto-clears theSpecsDirtyflag on return. The block only fires when ImGui reports dirty (header click), so the comparator cost is paid once per sort change rather than every frame.
The block-body comparator pattern walks the specs in priority order and
returns on the first spec that disambiguates a pair — sort_order = 0
is the primary key, sort_order = 1 is the first tiebreak, and so on.
A final tiebreak on a unique field (here: name) keeps the order
total.
For a complete standalone example (inventory table with id / name / qty
columns and a multi-key comparator), see modules/dasImgui/examples/features/sort_specs.das.
8.19.32.1.5. Why the name
table is a daslang reserved keyword — the type constructor for
table<K;V> (the hash-map type). Defining a function or container named
table is a parse error. data_table follows the standard UI-library
term (Material’s DataTable, Bootstrap’s table, etc.) and disambiguates
from the type namespace at every call site.
8.19.32.1.6. Standalone vs live
Same convention as previous tutorials. daslang.exe runs the table
once and exits at exit_requested(). daslang-live keeps the window
open and reloads on source edits.
See also
Full source: modules/dasImgui/examples/tutorial/data_table.das
Sortable inventory example: modules/dasImgui/examples/features/sort_specs.das
— the canonical sort_specs() reference with a multi-key comparator.
Integration tests: modules/dasImgui/tests/test_app_small_property_editor.das
(uses the same data_table container surface) and
modules/dasImgui/tests/test_sort_specs.das (smoke for the sortable rail).
Boost macros — the macro layer.