main.odin (7569B)
1 package main 2 3 import "base:runtime" 4 import "core:fmt" 5 6 import sapp "sokol/sokol/app" 7 import sg "sokol/sokol/gfx" 8 import sgl "sokol/sokol/gl" 9 import sglue "sokol/sokol/glue" 10 11 import mu "vendor:microui" 12 13 state := struct { 14 pip: sgl.Pipeline, 15 mu_ctx: mu.Context, 16 log_buf: [1 << 16]byte, 17 log_buf_len: int, 18 log_buf_updated: bool, 19 bg: mu.Color, 20 atlas_img: sg.Image, 21 atlas_smp: sg.Sampler, 22 } { 23 bg = {90, 95, 100, 255}, 24 } 25 26 r_init :: proc() { 27 pixels := make([][4]u8, mu.DEFAULT_ATLAS_WIDTH * mu.DEFAULT_ATLAS_HEIGHT) 28 for alpha, i in mu.default_atlas_alpha { 29 pixels[i].rgb = 0xff 30 pixels[i].a = alpha 31 } 32 33 state.atlas_img = sg.make_image( 34 sg.Image_Desc { 35 width = mu.DEFAULT_ATLAS_WIDTH, 36 height = mu.DEFAULT_ATLAS_HEIGHT, 37 data = { 38 subimage = { 39 0 = { 40 0 = { 41 ptr = raw_data(pixels), 42 size = mu.DEFAULT_ATLAS_WIDTH * mu.DEFAULT_ATLAS_HEIGHT * 4, 43 }, 44 }, 45 }, 46 }, 47 }, 48 ) 49 50 state.atlas_smp = sg.make_sampler( 51 sg.Sampler_Desc{min_filter = .NEAREST, mag_filter = .NEAREST}, 52 ) 53 54 55 state.pip = sgl.make_pipeline( 56 sg.Pipeline_Desc { 57 colors = { 58 0 = { 59 blend = { 60 enabled = true, 61 src_factor_rgb = .SRC_ALPHA, 62 dst_factor_rgb = .ONE_MINUS_SRC_ALPHA, 63 }, 64 }, 65 }, 66 }, 67 ) 68 69 free(raw_data(pixels)) 70 } 71 72 init :: proc "c" () { 73 context = runtime.default_context() 74 75 sg.setup(sg.Desc{environment = sglue.environment()}) 76 sgl.setup({}) 77 78 r_init() 79 80 ctx := &state.mu_ctx 81 mu.init(ctx) 82 83 ctx.text_width = mu.default_atlas_text_width 84 ctx.text_height = mu.default_atlas_text_height 85 } 86 87 r_begin :: proc(disp_width, disp_height: i32) { 88 sgl.defaults() 89 sgl.push_pipeline() 90 sgl.load_pipeline(state.pip) 91 sgl.enable_texture() 92 sgl.texture(state.atlas_img, state.atlas_smp) 93 sgl.matrix_mode_projection() 94 sgl.push_matrix() 95 sgl.ortho(0.0, f32(disp_width), f32(disp_height), 0.0, -1.0, +1.0) 96 sgl.begin_quads() 97 } 98 99 r_push_quad :: proc(dst: mu.Rect, src: mu.Rect, color: mu.Color) { 100 u0 := f32(src.x) / f32(mu.DEFAULT_ATLAS_WIDTH) 101 v0 := f32(src.y) / f32(mu.DEFAULT_ATLAS_HEIGHT) 102 u1 := f32(src.x + src.w) / f32(mu.DEFAULT_ATLAS_WIDTH) 103 v1 := f32(src.y + src.h) / f32(mu.DEFAULT_ATLAS_HEIGHT) 104 105 x0 := f32(dst.x) 106 y0 := f32(dst.y) 107 x1 := f32(dst.x + dst.w) 108 y1 := f32(dst.y + dst.h) 109 110 sgl.c4b(color.r, color.g, color.b, color.a) 111 sgl.v2f_t2f(x0, y0, u0, v0) 112 sgl.v2f_t2f(x1, y0, u1, v0) 113 sgl.v2f_t2f(x1, y1, u1, v1) 114 sgl.v2f_t2f(x0, y1, u0, v1) 115 } 116 117 r_draw_text :: proc(text: string, pos: mu.Vec2, color: mu.Color) { 118 dst := mu.Rect{pos.x, pos.y, 0, 0} 119 for ch in text { 120 src := mu.default_atlas[mu.DEFAULT_ATLAS_FONT + int(ch)] 121 dst.w = src.w 122 dst.h = src.h 123 r_push_quad(dst, src, color) 124 dst.x += dst.w 125 } 126 } 127 128 r_draw_rect :: proc(rect: mu.Rect, color: mu.Color) { 129 r_push_quad(rect, mu.default_atlas[mu.DEFAULT_ATLAS_WHITE], color) 130 } 131 132 r_draw_icon :: proc(id: mu.Icon, rect: mu.Rect, color: mu.Color) { 133 src := mu.default_atlas[id] 134 x := rect.x + (rect.w - src.w) / 2 135 y := rect.y + (rect.h - src.h) / 2 136 r_push_quad(mu.Rect{x, y, src.w, src.h}, src, color) 137 } 138 139 r_set_clip_rect :: proc(rect: mu.Rect) { 140 sgl.end() 141 sgl.scissor_rect(rect.x, rect.y, rect.w, rect.h, true) 142 sgl.begin_quads() 143 } 144 145 r_end :: proc() { 146 sgl.end() 147 sgl.pop_matrix() 148 sgl.pop_pipeline() 149 } 150 151 r_draw :: proc() { 152 sgl.draw() 153 } 154 155 frame :: proc "c" () { 156 context = runtime.default_context() 157 158 mu_ctx := &state.mu_ctx 159 mu.begin(mu_ctx) 160 all_windows(mu_ctx) 161 mu.end(mu_ctx) 162 163 r_begin(sapp.width(), sapp.height()) 164 command_backing: ^mu.Command 165 for variant in mu.next_command_iterator(mu_ctx, &command_backing) { 166 switch cmd in variant { 167 case ^mu.Command_Text: 168 r_draw_text(cmd.str, cmd.pos, cmd.color) 169 case ^mu.Command_Rect: 170 r_draw_rect(cmd.rect, cmd.color) 171 case ^mu.Command_Icon: 172 r_draw_icon(cmd.id, cmd.rect, cmd.color) 173 case ^mu.Command_Clip: 174 r_set_clip_rect(cmd.rect) 175 case ^mu.Command_Jump: 176 unreachable() 177 } 178 } 179 r_end() 180 181 sg.begin_pass( 182 sg.Pass { 183 action = { 184 colors = { 185 0 = { 186 load_action = .CLEAR, 187 clear_value = { 188 f32(state.bg.r) / 255.0, 189 f32(state.bg.g) / 255.0, 190 f32(state.bg.b) / 255.0, 191 1.0, 192 }, 193 }, 194 }, 195 }, 196 swapchain = sglue.swapchain(), 197 }, 198 ) 199 200 r_draw() 201 sg.end_pass() 202 sg.commit() 203 } 204 205 key_map := map[sapp.Keycode]mu.Key { 206 .LEFT_SHIFT = .SHIFT, 207 .RIGHT_SHIFT = .SHIFT, 208 .LEFT_CONTROL = .CTRL, 209 .RIGHT_CONTROL = .CTRL, 210 .LEFT_ALT = .ALT, 211 .RIGHT_ALT = .ALT, 212 .ENTER = .RETURN, 213 .KP_ENTER = .RETURN, 214 .BACKSPACE = .BACKSPACE, 215 } 216 event :: proc "c" (ev: ^sapp.Event) { 217 218 context = runtime.default_context() 219 mu_ctx := &state.mu_ctx 220 #partial switch ev.type { 221 case .MOUSE_DOWN: 222 mu.input_mouse_down(mu_ctx, i32(ev.mouse_x), i32(ev.mouse_y), mu.Mouse(ev.mouse_button)) 223 case .MOUSE_UP: 224 mu.input_mouse_up(mu_ctx, i32(ev.mouse_x), i32(ev.mouse_y), mu.Mouse(ev.mouse_button)) 225 case .MOUSE_MOVE: 226 mu.input_mouse_move(mu_ctx, i32(ev.mouse_x), i32(ev.mouse_y)) 227 case .MOUSE_SCROLL: 228 mu.input_scroll(mu_ctx, 0, i32(ev.scroll_y)) 229 case .KEY_DOWN: 230 if ev.key_code in key_map { 231 mu.input_key_down(mu_ctx, key_map[ev.key_code]) 232 } 233 case .KEY_UP: 234 if ev.key_code in key_map { 235 mu.input_key_up(mu_ctx, key_map[ev.key_code]) 236 } 237 case .CHAR: 238 mu.input_text(mu_ctx, fmt.tprint(rune(ev.char_code))) 239 } 240 } 241 242 cleanup :: proc "c" () { 243 sgl.shutdown() 244 sg.shutdown() 245 } 246 247 main :: proc() { 248 sapp.run( 249 { 250 init_cb = init, 251 frame_cb = frame, 252 cleanup_cb = cleanup, 253 event_cb = event, 254 width = 300, 255 height = 300, 256 sample_count = 4, 257 window_title = "sokol microui", 258 icon = {sokol_default = true}, 259 }, 260 ) 261 } 262 263 u8_slider :: proc(ctx: ^mu.Context, val: ^u8, lo, hi: u8) -> (res: mu.Result_Set) { 264 mu.push_id(ctx, uintptr(val)) 265 266 @(static) tmp: mu.Real 267 tmp = mu.Real(val^) 268 res = mu.slider(ctx, &tmp, mu.Real(lo), mu.Real(hi), 0, "%.0f", {.ALIGN_CENTER}) 269 val^ = u8(tmp) 270 mu.pop_id(ctx) 271 return 272 } 273 274 write_log :: proc(str: string) { 275 state.log_buf_len += copy(state.log_buf[state.log_buf_len:], str) 276 state.log_buf_len += copy(state.log_buf[state.log_buf_len:], "\n") 277 state.log_buf_updated = true 278 } 279 280 read_log :: proc() -> string { 281 return string(state.log_buf[:state.log_buf_len]) 282 } 283 reset_log :: proc() { 284 state.log_buf_updated = true 285 state.log_buf_len = 0 286 } 287 288 289 all_windows :: proc(ctx: ^mu.Context) { 290 window_width := sapp.width() 291 window_height := sapp.height() 292 293 if mu.window( 294 ctx, 295 "Spark", 296 {x = 0, y = 0, w = window_width, h = window_height}, 297 opt = {.NO_TITLE, .NO_INTERACT, .NO_FRAME, .NO_CLOSE, .NO_RESIZE, .NO_SCROLL}, 298 ) { 299 // To achieve a single window layout we need to syncrhonize the microui window rectangle 300 // with the dimensions of the sokol window. 301 cnt := mu.get_current_container(ctx) 302 cnt.rect.w = window_width 303 cnt.rect.h = window_height 304 305 mu.layout_row(ctx, {-1}) 306 307 @(static) prog: mu.Real = 0.0 308 prog = min(1.0, prog + 0.001) 309 progress(ctx, &prog) 310 } 311 } 312 313 314 progress :: proc( 315 ctx: ^mu.Context, 316 value: ^mu.Real, 317 opt: mu.Options = {.ALIGN_CENTER}, 318 ) -> ( 319 res: mu.Result_Set, 320 ) { 321 id := mu.get_id(ctx, uintptr(value)) 322 base := mu.layout_next(ctx) 323 mu.draw_control_frame(ctx, id, base, .BASE, opt) 324 325 prog := mu.Rect{base.x, base.y, base.w - i32((1.0 - value^) * f32(base.w)), base.h} 326 mu.draw_rect(ctx, prog, {50, 50, 100, 255}) 327 328 return 329 } 330 331 draw_rrect :: proc(ctx: ^mu.Context, rect: mu.Rect, radius: i32, color: mu.Color) { 332 rect := rect 333 rect = mu.intersect_rects(rect, mu.get_clip_rect(ctx)) 334 if rect.w > 0 && rect.h > 0 { 335 cmd := mu.push_command(ctx, mu.Command_Rect) 336 cmd.rect = rect 337 cmd.color = color 338 } 339 } 340