8.19.10. With style
ImGui’s style stack lets you change a color or metric for a scoped region
of the UI — push it once, pop it when you’re done. Hand-balancing the
push/pop pairs is bug-prone (miss a pop and EndFrame asserts), and
heterogeneous tuples can’t go through daslang’s uniform-element-type
varargs, so the boost layer ships with_style((key, value), ...) { ... }
as a [call_macro]. Each (key, value) tuple is dispatched at
compile-time — ImGuiCol keys route to PushStyleColor, ImGuiStyleVar
keys route to PushStyleVar — and a single bulk pop_style_n undoes
them all at block exit.
Source: modules/dasImgui/examples/tutorial/with_style.das.
8.19.10.1. Walkthrough
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_style_builtin
19require imgui/imgui_visual_aids
20
21// =============================================================================
22// TUTORIAL: with_style — scoped color/metric overrides for a sub-tree.
23//
24// ImGui's style stack is one of its main customization knobs: PushStyleColor
25// changes a color until the matching PopStyleColor; PushStyleVar changes a
26// metric (rounding, padding, spacing, ...) until the matching PopStyleVar.
27// Hand-balancing the pushes and pops is bug-prone — miss a pop and the
28// next frame asserts on EndFrame. The `with_style((key, val), ...) { ... }`
29// boost macro brackets the push/pop pair around a block:
30//
31// with_style((ImGuiCol.Button, ImVec4(0.8f, 0.2f, 0.2f, 1.0f)),
32// (ImGuiStyleVar.FrameRounding, 8.0f)) {
33// button(STYLED_BTN, (text = "Red rounded"))
34// }
35//
36// Each `(key, value)` tuple is dispatched at compile time by overload
37// resolution — ImGuiCol keys go to PushStyleColor, ImGuiStyleVar keys to
38// PushStyleVar. After the block, the matching pops fire in one bulk call.
39//
40// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/with_style.das
41// LIVE: daslang-live modules/dasImgui/examples/tutorial/with_style.das
42//
43// DRIVE (when running live):
44// curl -X POST -d '{"name":"imgui_snapshot"}' localhost:9090/command
45// curl -X POST -d '{"name":"imgui_click","args":{"target":"STYLE_WIN/STYLED_BTN"}}' localhost:9090/command
46// curl -X POST -d '{"name":"imgui_click","args":{"target":"STYLE_WIN/NESTED_STYLED_BTN"}}' localhost:9090/command
47// =============================================================================
48
49[export]
50def init() {
51 live_create_window("dasImgui with_style tutorial", 720, 520)
52 live_imgui_init(live_window)
53 let io & = unsafe(GetIO())
54 GetStyle().FontScaleMain = 1.5
55}
56
57[export]
58def update() {
59 if (!live_begin_frame()) return
60 begin_frame()
61
62 ImGui_ImplGlfw_NewFrame()
63 apply_synth_io_override()
64 NewFrame()
65
66 SetNextWindowPos(ImVec2(30.0f, 30.0f), ImGuiCond.FirstUseEver)
67 SetNextWindowSize(ImVec2(640.0f, 440.0f), ImGuiCond.FirstUseEver)
68 window(STYLE_WIN, (text = "with_style", closable = false,
69 flags = ImGuiWindowFlags.None)) {
70 text("Mixed colors + var, single block:")
71 // A full button restyle sets all three interaction states - base,
72 // hovered, active - so the color holds while the pointer is over it.
73 with_style((ImGuiCol.Button, ImVec4(0.85f, 0.20f, 0.20f, 1.0f)),
74 (ImGuiCol.ButtonHovered, ImVec4(0.95f, 0.30f, 0.30f, 1.0f)),
75 (ImGuiCol.ButtonActive, ImVec4(0.70f, 0.12f, 0.12f, 1.0f)),
76 (ImGuiStyleVar.FrameRounding, 8.0f)) {
77 button(STYLED_BTN, (text = "Red rounded"))
78 }
79
80 separator(WS_SEP_1)
81 text("Nested with_style - inner stacks on outer:")
82 with_style((ImGuiCol.Text, ImVec4(1.0f, 0.95f, 0.30f, 1.0f))) {
83 text("Yellow text inherited from outer scope.")
84 with_style((ImGuiCol.Button, ImVec4(0.20f, 0.45f, 0.85f, 1.0f)),
85 (ImGuiCol.ButtonHovered, ImVec4(0.30f, 0.55f, 0.95f, 1.0f)),
86 (ImGuiCol.ButtonActive, ImVec4(0.12f, 0.35f, 0.70f, 1.0f)),
87 (ImGuiStyleVar.FrameRounding, 4.0f)) {
88 // Both the outer yellow text and the inner blue/rounded
89 // button are in scope.
90 button(NESTED_STYLED_BTN, (text = "Blue button + yellow text"))
91 }
92 }
93
94 separator(WS_SEP_2)
95 text("After with_style - baseline style restored.")
96 // If push/pop counts were off, this button would render with stale
97 // color/rounding, and ImGui asserts on a stack imbalance at EndFrame
98 // (crash). The boost macro guarantees the bulk pop_style_n always fires.
99 button(UNSTYLED_BTN, (text = "Plain button"))
100 }
101
102 end_of_frame()
103 Render()
104 var w, h : int
105 live_get_framebuffer_size(w, h)
106 glViewport(0, 0, w, h)
107 glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
108 glClear(GL_COLOR_BUFFER_BIT)
109 live_imgui_render()
110
111 live_end_frame()
112}
113
114[export]
115def shutdown() {
116 live_imgui_shutdown()
117 live_destroy_window()
118}
119
120[export]
121def main() {
122 init()
123 while (!exit_requested()) {
124 update()
125 maybe_collect_gc()
126 }
127 shutdown()
128}
8.19.10.1.1. Requires
One extra module on top of the baseline boost layer:
imgui/imgui_style_builtin— thewith_style[call_macro]plus thepush_style_one/pop_style_nprimitives it lowers to.
8.19.10.1.2. Single block, mixed types
Each tuple in the with_style(...) argument list is independent — colors
and metric overrides mix freely:
with_style((ImGuiCol.Button, ImVec4(0.85f, 0.20f, 0.20f, 1.0f)),
(ImGuiCol.ButtonHovered, ImVec4(0.95f, 0.30f, 0.30f, 1.0f)),
(ImGuiCol.ButtonActive, ImVec4(0.70f, 0.12f, 0.12f, 1.0f)),
(ImGuiStyleVar.FrameRounding, 8.0f)) {
button(STYLED_BTN, (text = "Red rounded"))
}
A full button restyle pushes all three interaction states — Button,
ButtonHovered, ButtonActive — so the color holds while the pointer
is over it (push only Button and the default hover/active theme colors
show through on interaction). Here they mix with a rounding metric in one
block.
The macro emits one push_style_one(key, val) per tuple in source order,
then invoke(blk), then a single pop_style_n(N) that pops the
matching count in two bulk ImGui calls (one PopStyleColor for the color
pushes, one PopStyleVar for the metric pushes).
8.19.10.1.3. Nesting
Nested with_style blocks stack: the inner block adds to the outer’s
overrides without disturbing them. When the inner block exits, the outer
scope is restored:
with_style((ImGuiCol.Text, ImVec4(1.0f, 0.95f, 0.30f, 1.0f))) {
text("Yellow text inherited from outer scope.")
with_style((ImGuiCol.Button, ImVec4(0.20f, 0.45f, 0.85f, 1.0f)),
(ImGuiStyleVar.FrameRounding, 4.0f)) {
// Yellow text + blue button + 4-radius rounding all active.
button(NESTED_STYLED_BTN, (text = "Blue button + yellow text"))
}
// Back to: yellow text, default button color/rounding.
}
8.19.10.1.4. Pop balance
After every with_style block exits, the baseline style is restored —
the underlying g_style_pop_stack tracks per-push kind tags so the
bulk pop fires the right number of PopStyleColor / PopStyleVar
calls. Miss a pop and ImGui asserts at EndFrame; the macro is the
only API surface that pushes, so user code can’t accidentally leak
pushes past the block boundary.
8.19.10.1.5. Standalone vs live
Same convention as previous tutorials.
8.19.10.1.6. Driving from outside
with_style is purely structural — there’s no per-block state to set.
Drive the buttons inside instead:
curl -X POST -d '{"name":"imgui_click","args":{"target":"STYLE_WIN/STYLED_BTN"}}' \
localhost:9090/command
curl -X POST -d '{"name":"imgui_click","args":{"target":"STYLE_WIN/NESTED_STYLED_BTN"}}' \
localhost:9090/command
8.19.10.1.7. Next steps
Widget identity comes next — with_id to disambiguate widgets that
share the same name within an ImGui ID scope, plus the id=/path=
sugar that lets the boost layer steer the registry path without
restructuring the call hierarchy.
See also
Full source: modules/dasImgui/examples/tutorial/with_style.das
Richer reference: modules/dasImgui/examples/features/style_override.das — the
features-side demo with the same surface plus a baseline-restore check.
Integration test: modules/dasImgui/tests/test_style_with_style.das.
Previous tutorial: Docking
Boost macros — the macro layer.