spark

Cross Platform Minimal Installer GUI
Log | Files | Refs | Submodules | README

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