commit 908751cf810d74a22625b9ced47a2e5d212a3549
parent 59795eed0a64aca83ebe90771192da654f6e6ef2
Author: Jack Mordaunt <jackmordaunt.dev@gmail.com>
Date: Sun, 26 Oct 2025 22:41:36 -0300
wip: example: microui
Diffstat:
| A | example/microui.odin | | | 416 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 416 insertions(+), 0 deletions(-)
diff --git a/example/microui.odin b/example/microui.odin
@@ -0,0 +1,416 @@
+package main
+
+import "core:fmt"
+import "core:thread"
+
+import bl "../binding"
+import mu "vendor:microui"
+import sdl "vendor:sdl3"
+
+//todo need windowing lib
+// SDL3
+// tigr (neess bindings)
+// glfw
+//todo user input
+// font loading
+Context :: struct {
+ mu_ctx: ^mu.Context,
+ bl_ctx: ^bl.ContextCore,
+ log_buf: [1 << 16]byte,
+ log_buf_len: int,
+ log_buf_updated: bool,
+ bg: mu.Color,
+}
+
+context_init :: proc(ctx: ^Context) {
+ ctx.bl_ctx = new(bl.ContextCore)
+ bl.context_init(ctx.bl_ctx)
+ ctx.mu_ctx = new(mu.Context)
+ mu.init(ctx.mu_ctx)
+}
+
+main :: proc() {
+ ctx: Context
+
+ context_init(&ctx)
+
+ assert(sdl.Init({.VIDEO, .EVENTS}))
+
+ window := sdl.CreateWindow(
+ "microui - blend2d - sdl3",
+ 800,
+ 800,
+ {.HIGH_PIXEL_DENSITY, .RESIZABLE},
+ )
+
+ // NOTE: mus tbe requeried if window is resized.
+ surface := sdl.GetWindowSurface(window)
+
+ print_sdl_pixel_format(surface.format)
+
+ // img: bl.ImageCore
+
+ // bl.image_init(&img)
+ // bl.image_create(&img, 480, 480, .PRGB32)
+
+ // for {
+ // defer thread.yield()
+ // mu.begin(ctx.mu_ctx)
+ // all_windows(&ctx)
+ // mu.end(ctx.mu_ctx)
+ // render(&ctx)
+ // }
+}
+
+render :: proc(ctx: ^Context) {
+ bl.context_clear_all(ctx.bl_ctx)
+ defer bl.context_end(ctx.bl_ctx)
+
+ // cmd_backing is the iteration context, since mu.next_command is implemented
+ // via pointer math.
+ cmd_backing: ^mu.Command
+ for var in mu.next_command_iterator(ctx.mu_ctx, &cmd_backing) {
+ switch cmd in var {
+ case ^mu.Command_Text:
+ case ^mu.Command_Icon:
+ case ^mu.Command_Rect:
+ rect := cast(bl.RectI)(cmd.rect)
+ bl.context_fill_rect_i_rgba32(ctx.bl_ctx, &rect, transmute(u32)(cmd.color))
+ case ^mu.Command_Clip:
+ case ^mu.Command_Jump:
+ }
+ }
+
+}
+
+
+u8_slider :: proc(ctx: ^mu.Context, val: ^u8, lo, hi: u8) -> (res: mu.Result_Set) {
+ mu.push_id(ctx, uintptr(val))
+
+ @(static) tmp: mu.Real
+ tmp = mu.Real(val^)
+ res = mu.slider(ctx, &tmp, mu.Real(lo), mu.Real(hi), 0, "%.0f", {.ALIGN_CENTER})
+ val^ = u8(tmp)
+ mu.pop_id(ctx)
+ return
+}
+
+write_log :: proc(ctx: ^Context, str: string) {
+ ctx.log_buf_len += copy(ctx.log_buf[ctx.log_buf_len:], str)
+ ctx.log_buf_len += copy(ctx.log_buf[ctx.log_buf_len:], "\n")
+ ctx.log_buf_updated = true
+}
+
+read_log :: proc(ctx: ^Context) -> string {
+ return string(ctx.log_buf[:ctx.log_buf_len])
+}
+
+reset_log :: proc(ctx: ^Context) {
+ ctx.log_buf_updated = true
+ ctx.log_buf_len = 0
+}
+
+all_windows :: proc(ctx: ^Context) {
+ @(static) opts := mu.Options{.NO_CLOSE}
+
+ if mu.window(ctx.mu_ctx, "Demo Window", {40, 40, 300, 450}, opts) {
+ if .ACTIVE in mu.header(ctx.mu_ctx, "Window Info") {
+ win := mu.get_current_container(ctx.mu_ctx)
+ mu.layout_row(ctx.mu_ctx, {54, -1}, 0)
+ mu.label(ctx.mu_ctx, "Position:")
+ mu.label(ctx.mu_ctx, fmt.tprintf("%d, %d", win.rect.x, win.rect.y))
+ mu.label(ctx.mu_ctx, "Size:")
+ mu.label(ctx.mu_ctx, fmt.tprintf("%d, %d", win.rect.w, win.rect.h))
+ }
+
+ if .ACTIVE in mu.header(ctx.mu_ctx, "Window Options") {
+ mu.layout_row(ctx.mu_ctx, {120, 120, 120}, 0)
+ for opt in mu.Opt {
+ state := opt in opts
+ if .CHANGE in mu.checkbox(ctx.mu_ctx, fmt.tprintf("%v", opt), &state) {
+ if state {
+ opts += {opt}
+ } else {
+ opts -= {opt}
+ }
+ }
+ }
+ }
+
+ if .ACTIVE in mu.header(ctx.mu_ctx, "Test Buttons", {.EXPANDED}) {
+ mu.layout_row(ctx.mu_ctx, {86, -110, -1})
+ mu.label(ctx.mu_ctx, "Test buttons 1:")
+ if .SUBMIT in mu.button(ctx.mu_ctx, "Button 1") {write_log(ctx, "Pressed button 1")}
+ if .SUBMIT in mu.button(ctx.mu_ctx, "Button 2") {write_log(ctx, "Pressed button 2")}
+ mu.label(ctx.mu_ctx, "Test buttons 2:")
+ if .SUBMIT in mu.button(ctx.mu_ctx, "Button 3") {write_log(ctx, "Pressed button 3")}
+ if .SUBMIT in mu.button(ctx.mu_ctx, "Button 4") {write_log(ctx, "Pressed button 4")}
+ }
+
+ if .ACTIVE in mu.header(ctx.mu_ctx, "Tree and Text", {.EXPANDED}) {
+ mu.layout_row(ctx.mu_ctx, {140, -1})
+ mu.layout_begin_column(ctx.mu_ctx)
+ if .ACTIVE in mu.treenode(ctx.mu_ctx, "Test 1") {
+ if .ACTIVE in mu.treenode(ctx.mu_ctx, "Test 1a") {
+ mu.label(ctx.mu_ctx, "Hello")
+ mu.label(ctx.mu_ctx, "world")
+ }
+ if .ACTIVE in mu.treenode(ctx.mu_ctx, "Test 1b") {
+ if .SUBMIT in
+ mu.button(ctx.mu_ctx, "Button 1") {write_log(ctx, "Pressed button 1")}
+ if .SUBMIT in
+ mu.button(ctx.mu_ctx, "Button 2") {write_log(ctx, "Pressed button 2")}
+ }
+ }
+ if .ACTIVE in mu.treenode(ctx.mu_ctx, "Test 2") {
+ mu.layout_row(ctx.mu_ctx, {53, 53})
+ if .SUBMIT in
+ mu.button(ctx.mu_ctx, "Button 3") {write_log(ctx, "Pressed button 3")}
+ if .SUBMIT in
+ mu.button(ctx.mu_ctx, "Button 4") {write_log(ctx, "Pressed button 4")}
+ if .SUBMIT in
+ mu.button(ctx.mu_ctx, "Button 5") {write_log(ctx, "Pressed button 5")}
+ if .SUBMIT in
+ mu.button(ctx.mu_ctx, "Button 6") {write_log(ctx, "Pressed button 6")}
+ }
+ if .ACTIVE in mu.treenode(ctx.mu_ctx, "Test 3") {
+ @(static) checks := [3]bool{true, false, true}
+ mu.checkbox(ctx.mu_ctx, "Checkbox 1", &checks[0])
+ mu.checkbox(ctx.mu_ctx, "Checkbox 2", &checks[1])
+ mu.checkbox(ctx.mu_ctx, "Checkbox 3", &checks[2])
+
+ }
+ mu.layout_end_column(ctx.mu_ctx)
+
+ mu.layout_begin_column(ctx.mu_ctx)
+ mu.layout_row(ctx.mu_ctx, {-1})
+ mu.text(
+ ctx.mu_ctx,
+ "Lorem ipsum dolor sit amet, consectetur adipiscing " +
+ "elit. Maecenas lacinia, sem eu lacinia molestie, mi risus faucibus " +
+ "ipsum, eu varius magna felis a nulla.",
+ )
+ mu.layout_end_column(ctx.mu_ctx)
+ }
+
+ if .ACTIVE in mu.header(ctx.mu_ctx, "Background Colour", {.EXPANDED}) {
+ mu.layout_row(ctx.mu_ctx, {-78, -1}, 68)
+ mu.layout_begin_column(ctx.mu_ctx)
+ {
+ mu.layout_row(ctx.mu_ctx, {46, -1}, 0)
+ mu.label(ctx.mu_ctx, "Red:"); u8_slider(ctx.mu_ctx, &ctx.bg.r, 0, 255)
+ mu.label(ctx.mu_ctx, "Green:"); u8_slider(ctx.mu_ctx, &ctx.bg.g, 0, 255)
+ mu.label(ctx.mu_ctx, "Blue:"); u8_slider(ctx.mu_ctx, &ctx.bg.b, 0, 255)
+ }
+ mu.layout_end_column(ctx.mu_ctx)
+
+ r := mu.layout_next(ctx.mu_ctx)
+ mu.draw_rect(ctx.mu_ctx, r, ctx.bg)
+ mu.draw_box(ctx.mu_ctx, mu.expand_rect(r, 1), ctx.mu_ctx.style.colors[.BORDER])
+ mu.draw_control_text(
+ ctx.mu_ctx,
+ fmt.tprintf("#%02x%02x%02x", ctx.bg.r, ctx.bg.g, ctx.bg.b),
+ r,
+ .TEXT,
+ {.ALIGN_CENTER},
+ )
+ }
+ }
+
+ if mu.window(ctx.mu_ctx, "Log Window", {350, 40, 300, 200}, opts) {
+ mu.layout_row(ctx.mu_ctx, {-1}, -28)
+ mu.begin_panel(ctx.mu_ctx, "Log")
+ mu.layout_row(ctx.mu_ctx, {-1}, -1)
+ mu.text(ctx.mu_ctx, read_log(ctx))
+ if ctx.log_buf_updated {
+ panel := mu.get_current_container(ctx.mu_ctx)
+ panel.scroll.y = panel.content_size.y
+ ctx.log_buf_updated = false
+ }
+ mu.end_panel(ctx.mu_ctx)
+
+ @(static) buf: [128]byte
+ @(static) buf_len: int
+ submitted := false
+ mu.layout_row(ctx.mu_ctx, {-70, -1})
+ if .SUBMIT in mu.textbox(ctx.mu_ctx, buf[:], &buf_len) {
+ mu.set_focus(ctx.mu_ctx, ctx.mu_ctx.last_id)
+ submitted = true
+ }
+ if .SUBMIT in mu.button(ctx.mu_ctx, "Submit") {
+ submitted = true
+ }
+ if submitted {
+ write_log(ctx, string(buf[:buf_len]))
+ buf_len = 0
+ }
+ }
+
+ if mu.window(ctx.mu_ctx, "Style Window", {350, 250, 300, 240}) {
+ @(static) colors := [mu.Color_Type]string {
+ .TEXT = "text",
+ .BORDER = "border",
+ .WINDOW_BG = "window bg",
+ .TITLE_BG = "title bg",
+ .TITLE_TEXT = "title text",
+ .PANEL_BG = "panel bg",
+ .BUTTON = "button",
+ .BUTTON_HOVER = "button hover",
+ .BUTTON_FOCUS = "button focus",
+ .BASE = "base",
+ .BASE_HOVER = "base hover",
+ .BASE_FOCUS = "base focus",
+ .SCROLL_BASE = "scroll base",
+ .SCROLL_THUMB = "scroll thumb",
+ .SELECTION_BG = "selection bg",
+ }
+
+ sw := i32(f32(mu.get_current_container(ctx.mu_ctx).body.w) * 0.14)
+ mu.layout_row(ctx.mu_ctx, {80, sw, sw, sw, sw, -1})
+ for label, col in colors {
+ mu.label(ctx.mu_ctx, label)
+ u8_slider(ctx.mu_ctx, &ctx.mu_ctx.style.colors[col].r, 0, 255)
+ u8_slider(ctx.mu_ctx, &ctx.mu_ctx.style.colors[col].g, 0, 255)
+ u8_slider(ctx.mu_ctx, &ctx.mu_ctx.style.colors[col].b, 0, 255)
+ u8_slider(ctx.mu_ctx, &ctx.mu_ctx.style.colors[col].a, 0, 255)
+ mu.draw_rect(ctx.mu_ctx, mu.layout_next(ctx.mu_ctx), ctx.mu_ctx.style.colors[col])
+ }
+ }
+
+}
+
+print_sdl_pixel_format :: proc(pf: sdl.PixelFormat) {
+ switch pf {
+ case .UNKNOWN:
+ fmt.println("UNKNOWN")
+ case .INDEX1LSB:
+ fmt.println("INDEX1LSB")
+ case .INDEX1MSB:
+ fmt.println("INDEX1MSB")
+ case .INDEX2LSB:
+ fmt.println("INDEX2LSB")
+ case .INDEX2MSB:
+ fmt.println("INDEX2MSB")
+ case .INDEX4LSB:
+ fmt.println("INDEX4LSB")
+ case .INDEX4MSB:
+ fmt.println("INDEX4MSB")
+ case .INDEX8:
+ fmt.println("INDEX8")
+ case .RGB332:
+ fmt.println("RGB332")
+ case .XRGB4444:
+ fmt.println("XRGB4444")
+ case .XBGR4444:
+ fmt.println("XBGR4444")
+ case .XRGB1555:
+ fmt.println("XRGB1555")
+ case .XBGR1555:
+ fmt.println("XBGR1555")
+ case .ARGB4444:
+ fmt.println("ARGB4444")
+ case .RGBA4444:
+ fmt.println("RGBA4444")
+ case .ABGR4444:
+ fmt.println("ABGR4444")
+ case .BGRA4444:
+ fmt.println("BGRA4444")
+ case .ARGB1555:
+ fmt.println("ARGB1555")
+ case .RGBA5551:
+ fmt.println("RGBA5551")
+ case .ABGR1555:
+ fmt.println("ABGR1555")
+ case .BGRA5551:
+ fmt.println("BGRA5551")
+ case .RGB565:
+ fmt.println("RGB565")
+ case .BGR565:
+ fmt.println("BGR565")
+ case .RGB24:
+ fmt.println("RGB24")
+ case .BGR24:
+ fmt.println("BGR24")
+ case .XRGB8888:
+ fmt.println("XRGB8888")
+ case .RGBX8888:
+ fmt.println("RGBX8888")
+ case .XBGR8888:
+ fmt.println("XBGR8888")
+ case .BGRX8888:
+ fmt.println("BGRX8888")
+ case .ARGB8888:
+ fmt.println("ARGB8888")
+ case .RGBA8888:
+ fmt.println("RGBA8888")
+ case .ABGR8888:
+ fmt.println("ABGR8888")
+ case .BGRA8888:
+ fmt.println("BGRA8888")
+ case .XRGB2101010:
+ fmt.println("XRGB2101010")
+ case .XBGR2101010:
+ fmt.println("XBGR2101010")
+ case .ARGB2101010:
+ fmt.println("ARGB2101010")
+ case .ABGR2101010:
+ fmt.println("ABGR2101010")
+ case .RGB48:
+ fmt.println("RGB48")
+ case .BGR48:
+ fmt.println("BGR48")
+ case .RGBA64:
+ fmt.println("RGBA64")
+ case .ARGB64:
+ fmt.println("ARGB64")
+ case .BGRA64:
+ fmt.println("BGRA64")
+ case .ABGR64:
+ fmt.println("ABGR64")
+ case .RGB48_FLOAT:
+ fmt.println("RGB48_FLOAT")
+ case .BGR48_FLOAT:
+ fmt.println("BGR48_FLOAT")
+ case .RGBA64_FLOAT:
+ fmt.println("RGBA64_FLOAT")
+ case .ARGB64_FLOAT:
+ fmt.println("ARGB64_FLOAT")
+ case .BGRA64_FLOAT:
+ fmt.println("BGRA64_FLOAT")
+ case .ABGR64_FLOAT:
+ fmt.println("ABGR64_FLOAT")
+ case .RGB96_FLOAT:
+ fmt.println("RGB96_FLOAT")
+ case .BGR96_FLOAT:
+ fmt.println("BGR96_FLOAT")
+ case .RGBA128_FLOAT:
+ fmt.println("RGBA128_FLOAT")
+ case .ARGB128_FLOAT:
+ fmt.println("ARGB128_FLOAT")
+ case .BGRA128_FLOAT:
+ fmt.println("BGRA128_FLOAT")
+ case .ABGR128_FLOAT:
+ fmt.println("ABGR128_FLOAT")
+ case .YV12:
+ fmt.println("YV12")
+ case .IYUV:
+ fmt.println("IYUV")
+ case .YUY2:
+ fmt.println("YUY2")
+ case .UYVY:
+ fmt.println("UYVY")
+ case .YVYU:
+ fmt.println("YVYU")
+ case .NV12:
+ fmt.println("NV12")
+ case .NV21:
+ fmt.println("NV21")
+ case .P010:
+ fmt.println("P010")
+ case .EXTERNAL_OES:
+ fmt.println("EXTERNAL_OES")
+ case .MJPG:
+ fmt.println("MJPG")
+ }
+}
+