From 3ece69dfc66412f1a4fa6d6842b74c0ad2c83d78 Mon Sep 17 00:00:00 2001 From: IllusionMan1212 Date: Sat, 20 Jan 2024 06:39:49 +0200 Subject: [PATCH] zephr: consume hover event on top-most ui elements --- game.odin | 892 +++++++++++++++++++++--------------------- zephr/shader.odin | 30 +- zephr/text.odin | 310 +++++++-------- zephr/texture.odin | 38 +- zephr/ui.odin | 751 ++++++++++++++++++----------------- zephr/zephr.odin | 352 +++++++++-------- zephr/zephr_math.odin | 38 +- 7 files changed, 1230 insertions(+), 1181 deletions(-) diff --git a/game.odin b/game.odin index 4d1b6f3..f53b487 100644 --- a/game.odin +++ b/game.odin @@ -81,29 +81,29 @@ game : Game get_tile_color :: proc(value: u16) -> zephr.Color { switch (value) { case 2: - return game.palette.tile_colors[0]; + return game.palette.tile_colors[0] case 4: - return game.palette.tile_colors[1]; + return game.palette.tile_colors[1] case 8: - return game.palette.tile_colors[2]; + return game.palette.tile_colors[2] case 16: - return game.palette.tile_colors[3]; + return game.palette.tile_colors[3] case 32: - return game.palette.tile_colors[4]; + return game.palette.tile_colors[4] case 64: - return game.palette.tile_colors[5]; + return game.palette.tile_colors[5] case 128: - return game.palette.tile_colors[6]; + return game.palette.tile_colors[6] case 256: - return game.palette.tile_colors[7]; + return game.palette.tile_colors[7] case 512: - return game.palette.tile_colors[8]; + return game.palette.tile_colors[8] case 1024: - return game.palette.tile_colors[9]; + return game.palette.tile_colors[9] case 2048: - return game.palette.tile_colors[10]; + return game.palette.tile_colors[10] case: - return game.palette.tile_colors[10]; + return game.palette.tile_colors[10] } } @@ -112,20 +112,20 @@ get_tile_font_size :: proc(value: u16) -> u32 { case 2: fallthrough case 4: fallthrough case 8: - return 60; + return 60 case 16: fallthrough case 32: fallthrough case 64: - return 55; + return 55 case 128: fallthrough case 256: fallthrough case 512: - return 50; + return 50 case 1024: fallthrough case 2048: - return 45; + return 45 case: - return 40; + return 40 } } @@ -139,17 +139,17 @@ get_tile_font_size :: proc(value: u16) -> u32 { move_right :: proc() { - game.last_move_dir = .RIGHT; + game.last_move_dir = .RIGHT for i in 0..<4 { for j := 2; j >= 0; j -= 1 { if (game.board[i][j].value == 0) { - continue; + continue } - tiles_to_move := 0; - x := j; + tiles_to_move := 0 + x := j - src := &game.board[i][j]; + src := &game.board[i][j] loop: for { if (x >= 3) do break loop @@ -157,35 +157,35 @@ move_right :: proc() { // next tile is empty so we can move there if (game.board[i][x].new_value == 0) { - tiles_to_move += 1; - continue; + tiles_to_move += 1 + continue } // next tile has the same value so we can merge and move if (game.board[i][x].new_value == src.new_value && !game.board[i][x].merged) { - tiles_to_move += 1; - game.board[i][x].merged = true; - break; + tiles_to_move += 1 + game.board[i][x].merged = true + break } else { // next tile has a different value so we can't move there - x -= 1; - break; + x -= 1 + break } } - src.tiles_to_move = auto_cast tiles_to_move; + src.tiles_to_move = auto_cast tiles_to_move if (tiles_to_move > 0) { if (game.board[i][x].merged) { - game.board[i][x].new_value *= 2; - game.score += auto_cast game.board[i][x].new_value; + game.board[i][x].new_value *= 2 + game.score += auto_cast game.board[i][x].new_value } else { - game.board[i][x].new_value = src.new_value; + game.board[i][x].new_value = src.new_value } - src.new_value = 0; - game.animating = true; - game.spawning_tile_coords = get_random_available_tile_coords(); - game.spawning_tile_value = get_spawned_tile_value(); + src.new_value = 0 + game.animating = true + game.spawning_tile_coords = get_random_available_tile_coords() + game.spawning_tile_value = get_spawned_tile_value() } } } @@ -196,173 +196,153 @@ move_right :: proc() { // and a tile is an enitity that has a position (in the board or on the screen??) that we can animate using lerp // we draw the tile entities and not the board state. the tile entities update their positions according to the board state at the end of the frame?? move_left :: proc() { - game.last_move_dir = .LEFT; + game.last_move_dir = .LEFT for i in 0..<4 { for j in 1..<4 { if (game.board[i][j].value == 0) { - continue; + continue } - tiles_to_move := 0; - x := j; + tiles_to_move := 0 + x := j - src := &game.board[i][j]; + src := &game.board[i][j] loop: for { if (x <= 0) do break loop - x -= 1; + x -= 1 // next tile is empty so we can move there if (game.board[i][x].new_value == 0) { - tiles_to_move += 1; - continue; + tiles_to_move += 1 + continue } // next tile has the same value so we can merge and move if (game.board[i][x].new_value == src.new_value && !game.board[i][x].merged) { - tiles_to_move += 1; - game.board[i][x].merged = true; - break; + tiles_to_move += 1 + game.board[i][x].merged = true + break } else { // next tile has a different value so we can't move there x += 1 - break; + break } } - src.tiles_to_move = auto_cast tiles_to_move; + src.tiles_to_move = auto_cast tiles_to_move if (tiles_to_move > 0) { if (game.board[i][x].merged) { - game.board[i][x].new_value *= 2; - game.score += auto_cast game.board[i][x].new_value; + game.board[i][x].new_value *= 2 + game.score += auto_cast game.board[i][x].new_value } else { - game.board[i][x].new_value = src.new_value; + game.board[i][x].new_value = src.new_value } - src.new_value = 0; - game.animating = true; - game.spawning_tile_coords = get_random_available_tile_coords(); - game.spawning_tile_value = get_spawned_tile_value(); + src.new_value = 0 + game.animating = true + game.spawning_tile_coords = get_random_available_tile_coords() + game.spawning_tile_value = get_spawned_tile_value() } } } } -print_board :: proc() { - for i in 0..<4 { - for j in 0..<4 { - fmt.print(game.board[i][j].value, " "); - } - fmt.println(); - } - fmt.println(); -} - -print_new_board :: proc() { - for i in 0..<4 { - for j in 0..<4 { - fmt.print(game.board[i][j].new_value, " "); - } - fmt.println(); - } - fmt.println(); -} - move_down :: proc() { - game.last_move_dir = .DOWN; + game.last_move_dir = .DOWN for i := 2; i >= 0; i -= 1 { for j in 0..<4 { if (game.board[i][j].value == 0) { - continue; + continue } - tiles_to_move := 0; - y := i; + tiles_to_move := 0 + y := i - src := &game.board[i][j]; + src := &game.board[i][j] loop: for { if (y >= 3) do break loop - y += 1; + y += 1 if (game.board[y][j].new_value == 0) { - tiles_to_move += 1; - continue; + tiles_to_move += 1 + continue } if (game.board[y][j].new_value == src.new_value && !game.board[y][j].merged) { - tiles_to_move += 1; - game.board[y][j].merged = true; - break; + tiles_to_move += 1 + game.board[y][j].merged = true + break } else { - y -= 1; - break; + y -= 1 + break } } - src.tiles_to_move = auto_cast tiles_to_move; + src.tiles_to_move = auto_cast tiles_to_move if (tiles_to_move > 0) { if (game.board[y][j].merged) { - game.board[y][j].new_value *= 2; - game.score += auto_cast game.board[y][j].new_value; + game.board[y][j].new_value *= 2 + game.score += auto_cast game.board[y][j].new_value } else { - game.board[y][j].new_value = src.new_value; + game.board[y][j].new_value = src.new_value } - src.new_value = 0; - game.animating = true; - game.spawning_tile_coords = get_random_available_tile_coords(); - game.spawning_tile_value = get_spawned_tile_value(); + src.new_value = 0 + game.animating = true + game.spawning_tile_coords = get_random_available_tile_coords() + game.spawning_tile_value = get_spawned_tile_value() } } } } move_up :: proc() { - game.last_move_dir = .UP; + game.last_move_dir = .UP for i in 1..<4 { for j in 0..<4 { if (game.board[i][j].new_value == 0) { - continue; + continue } - tiles_to_move := 0; - y := i; + tiles_to_move := 0 + y := i - src := &game.board[i][j]; + src := &game.board[i][j] loop: for { if (y <= 0) do break loop - y -= 1; + y -= 1 if (game.board[y][j].new_value == 0) { - tiles_to_move += 1; - continue; + tiles_to_move += 1 + continue } if (game.board[y][j].new_value == src.new_value && !game.board[y][j].merged) { - tiles_to_move += 1; - game.board[y][j].merged = true; - break; + tiles_to_move += 1 + game.board[y][j].merged = true + break } else { - y += 1; - break; + y += 1 + break } } - src.tiles_to_move = auto_cast tiles_to_move; + src.tiles_to_move = auto_cast tiles_to_move if (tiles_to_move > 0) { if (game.board[y][j].merged) { - game.board[y][j].new_value *= 2; - game.score += auto_cast game.board[y][j].new_value; + game.board[y][j].new_value *= 2 + game.score += auto_cast game.board[y][j].new_value } else { - game.board[y][j].new_value = src.new_value; + game.board[y][j].new_value = src.new_value } - src.new_value = 0; - game.animating = true; - game.spawning_tile_coords = get_random_available_tile_coords(); - game.spawning_tile_value = get_spawned_tile_value(); + src.new_value = 0 + game.animating = true + game.spawning_tile_coords = get_random_available_tile_coords() + game.spawning_tile_value = get_spawned_tile_value() } } } @@ -392,36 +372,36 @@ reset_palette :: proc() { reset_game :: proc() { timeElapsed = 0 - game.score = 0; - game.animating = false; - game.spawning_new_tile = false; - game.quit_dialog = false; - game.help_dialog = false; - game.settings_dialog = false; - game.has_lost = false; - game.game_over_bg_opacity = 0; - game.game_over_opacity = 0; + game.score = 0 + game.animating = false + game.spawning_new_tile = false + game.quit_dialog = false + game.help_dialog = false + game.settings_dialog = false + game.has_lost = false + game.game_over_bg_opacity = 0 + game.game_over_opacity = 0 for y in 0..<4 { for x in 0..<4 { - game.board[y][x].value = 0; - game.board[y][x].new_value = 0; - game.board[y][x].merged = false; - game.board[y][x].tiles_to_move = 0; - game.board[y][x].anim_x_offset_relative = 0; - game.board[y][x].anim_y_offset_relative = 0; + game.board[y][x].value = 0 + game.board[y][x].new_value = 0 + game.board[y][x].merged = false + game.board[y][x].tiles_to_move = 0 + game.board[y][x].anim_x_offset_relative = 0 + game.board[y][x].anim_y_offset_relative = 0 } } for i in 0..<2 { - spawn_random_tile(); + spawn_random_tile() } } game_attempt_quit :: proc() { - game.help_dialog = false; - game.settings_dialog = false; - game.quit_dialog = true; + game.help_dialog = false + game.settings_dialog = false + game.quit_dialog = true } game_init :: proc() { @@ -439,48 +419,48 @@ game_init :: proc() { get_spawned_tile_value :: proc() -> u16 { // 90% chance of spawning a 2, 10% chance of spawning a 4 if (rand.int_max(11) < 9) { - return 2; + return 2 } else { - return 4; + return 4 } } spawn_new_tile_with_value :: proc(x, y: u8, value: u16) { - game.board[x][y].value = value; - game.board[x][y].new_value = value; + game.board[x][y].value = value + game.board[x][y].new_value = value } spawn_new_tile :: proc(x, y: u8) { - val := get_spawned_tile_value(); + val := get_spawned_tile_value() - game.board[x][y].value = val; - game.board[x][y].new_value = val; + game.board[x][y].value = val + game.board[x][y].new_value = val } get_random_available_tile_coords :: proc() -> zephr.Vec2 { - available_tiles_count := 0; + available_tiles_count := 0 available_tiles: [16]int for i in 0..<4 { for j in 0..<4 { if (game.board[i][j].new_value == 0 && !game.board[i][j].merged) { - available_tiles[available_tiles_count] = i * 4 + j; - available_tiles_count += 1; + available_tiles[available_tiles_count] = i * 4 + j + available_tiles_count += 1 } } } rand_idx := rand.int_max(available_tiles_count) - x := available_tiles[rand_idx] / 4; - y := available_tiles[rand_idx] % 4; + x := available_tiles[rand_idx] / 4 + y := available_tiles[rand_idx] % 4 - return zephr.Vec2{cast(f32)x, cast(f32)y}; + return zephr.Vec2{cast(f32)x, cast(f32)y} } spawn_random_tile :: proc() { - coords := get_random_available_tile_coords(); - spawn_new_tile(cast(u8)coords.x, cast(u8)coords.y); + coords := get_random_available_tile_coords() + spawn_new_tile(cast(u8)coords.x, cast(u8)coords.y) } gameover :: proc() -> bool { @@ -489,35 +469,35 @@ gameover :: proc() -> bool { for y in 0..<4 { for x in 0..<4 { if (game.board[y][x].value == 0) { - return false; + return false } for i in 0..<4 { - new_y := cast(int)clamp(cast(f32)y + neighbors[i].y, 0, 3); - new_x := cast(int)clamp(cast(f32)x + neighbors[i].x, 0, 3); + new_y := cast(int)clamp(cast(f32)y + neighbors[i].y, 0, 3) + new_x := cast(int)clamp(cast(f32)x + neighbors[i].x, 0, 3) if ((new_y != y || new_x != x) && game.board[y][x].value == game.board[new_y][new_x].value) { - return false; + return false } } } } - return true; + return true } handle_keyboard_input :: proc(e: zephr.Event) { - can_move := !game.quit_dialog && !game.help_dialog && !game.settings_dialog && !game.animating; + can_move := !game.quit_dialog && !game.help_dialog && !game.settings_dialog && !game.animating if (.LEFT_CTRL in e.key.mods && e.key.scancode == .Q) { - game_attempt_quit(); + game_attempt_quit() } else if (e.key.scancode == .ESCAPE) { - game.quit_dialog = false; - game.help_dialog = false; - game.settings_dialog = false; + game.quit_dialog = false + game.help_dialog = false + game.settings_dialog = false } else if (e.key.scancode == .F11) { - zephr.toggle_fullscreen(); + zephr.toggle_fullscreen() } else if (e.key.scancode == .UP) { if (can_move) do move_up() } else if (e.key.scancode == .DOWN) { @@ -541,24 +521,24 @@ game_loop :: proc() { #partial switch (e.type) { case .WINDOW_CLOSED: if (game.quit_dialog) { - zephr.quit(); + zephr.quit() } else { - game_attempt_quit(); + game_attempt_quit() } case .KEY_PRESSED: - handle_keyboard_input(e); + handle_keyboard_input(e) } } now := zephr.get_time() delta_t := now - last_frame - last_frame = now; + last_frame = now update_positions(delta_t) - draw_bg(); - draw_board(); - draw_ui(); + draw_bg() + draw_board() + draw_ui() zephr.swap_buffers() } @@ -574,25 +554,25 @@ update_positions :: proc(delta_t: time.Duration) { case .LEFT: for i in 0..<4 { for j in 1..<4 { - tiles_to_move := game.board[i][j].tiles_to_move; - speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move; - src := &game.board[i][j]; - dest := &game.board[i][j - cast(int)tiles_to_move]; + tiles_to_move := game.board[i][j].tiles_to_move + speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move + src := &game.board[i][j] + dest := &game.board[i][j - cast(int)tiles_to_move] if (src.tiles_to_move != 0) { - src.anim_x_offset_relative -= speed * delta_t_f; + src.anim_x_offset_relative -= speed * delta_t_f if (src.anim_x_offset_relative <= -0.240 * (cast(f64)tiles_to_move + (cast(f64)tiles_to_move * 0.02))) { if (dest.new_value == src.value * 2) { - dest.merged = false; + dest.merged = false } - src.value = src.new_value; - dest.value = dest.new_value; + src.value = src.new_value + dest.value = dest.new_value - src.tiles_to_move = 0; - src.anim_x_offset_relative = 0; - game.spawning_new_tile = true; + src.tiles_to_move = 0 + src.anim_x_offset_relative = 0 + game.spawning_new_tile = true } } } @@ -600,25 +580,25 @@ update_positions :: proc(delta_t: time.Duration) { case .RIGHT: for i in 0..<4 { for j := 2; j >= 0; j -= 1 { - tiles_to_move := game.board[i][j].tiles_to_move; - speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move; - src := &game.board[i][j]; - dest := &game.board[i][j + cast(int)tiles_to_move]; + tiles_to_move := game.board[i][j].tiles_to_move + speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move + src := &game.board[i][j] + dest := &game.board[i][j + cast(int)tiles_to_move] if (src.tiles_to_move != 0) { - src.anim_x_offset_relative += speed * delta_t_f; + src.anim_x_offset_relative += speed * delta_t_f if (src.anim_x_offset_relative >= 0.240 * (cast(f64)tiles_to_move + (cast(f64)tiles_to_move * 0.02))) { if (dest.new_value == src.value * 2) { - dest.merged = false; + dest.merged = false } - src.value = src.new_value; - dest.value = dest.new_value; + src.value = src.new_value + dest.value = dest.new_value - src.tiles_to_move = 0; - src.anim_x_offset_relative = 0; - game.spawning_new_tile = true; + src.tiles_to_move = 0 + src.anim_x_offset_relative = 0 + game.spawning_new_tile = true } } } @@ -626,25 +606,25 @@ update_positions :: proc(delta_t: time.Duration) { case .UP: for i in 1..<4 { for j in 0..<4 { - tiles_to_move := game.board[i][j].tiles_to_move; - speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move; - src := &game.board[i][j]; - dest := &game.board[i - cast(int)tiles_to_move][j]; + tiles_to_move := game.board[i][j].tiles_to_move + speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move + src := &game.board[i][j] + dest := &game.board[i - cast(int)tiles_to_move][j] if (src.tiles_to_move != 0) { - src.anim_y_offset_relative -= speed * delta_t_f; + src.anim_y_offset_relative -= speed * delta_t_f if (src.anim_y_offset_relative <= -0.240 * (cast(f64)tiles_to_move + (cast(f64)tiles_to_move * 0.02))) { if (dest.new_value == src.value * 2) { - dest.merged = false; + dest.merged = false } - src.value = src.new_value; - dest.value = dest.new_value; + src.value = src.new_value + dest.value = dest.new_value - src.tiles_to_move = 0; - src.anim_y_offset_relative = 0; - game.spawning_new_tile = true; + src.tiles_to_move = 0 + src.anim_y_offset_relative = 0 + game.spawning_new_tile = true } } } @@ -652,25 +632,25 @@ update_positions :: proc(delta_t: time.Duration) { case .DOWN: for i := 2; i >= 0; i -= 1 { for j in 0..<4 { - tiles_to_move := game.board[i][j].tiles_to_move; - speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move; - src := &game.board[i][j]; - dest := &game.board[i + cast(int)tiles_to_move][j]; + tiles_to_move := game.board[i][j].tiles_to_move + speed := TILE_ANIM_SPEED * cast(f64)tiles_to_move + src := &game.board[i][j] + dest := &game.board[i + cast(int)tiles_to_move][j] if (src.tiles_to_move != 0) { - src.anim_y_offset_relative += speed * delta_t_f; + src.anim_y_offset_relative += speed * delta_t_f if (src.anim_y_offset_relative >= 0.240 * (cast(f64)tiles_to_move + (cast(f64)tiles_to_move * 0.02))) { if (dest.new_value == src.value * 2) { - dest.merged = false; + dest.merged = false } - src.value = src.new_value; - dest.value = dest.new_value; + src.value = src.new_value + dest.value = dest.new_value - src.tiles_to_move = 0; - src.anim_y_offset_relative = 0; - game.spawning_new_tile = true; + src.tiles_to_move = 0 + src.anim_y_offset_relative = 0 + game.spawning_new_tile = true } } } @@ -679,19 +659,19 @@ update_positions :: proc(delta_t: time.Duration) { } if (gameover()) { - game.has_lost = true; - game.animating = true; + game.has_lost = true + game.animating = true } if (game.spawning_new_tile) { - game.spawning_tile_scale += (TILE_SPAWN_SPEED * cast(f32)delta_t_f); + game.spawning_tile_scale += (TILE_SPAWN_SPEED * cast(f32)delta_t_f) if (game.spawning_tile_scale >= 1.0) { - spawn_new_tile_with_value(cast(u8)game.spawning_tile_coords.x, cast(u8)game.spawning_tile_coords.y, game.spawning_tile_value); - game.spawning_tile_coords = zephr.Vec2{0, 0}; - game.spawning_tile_value = 0; - game.spawning_new_tile = false; - game.animating = false; + spawn_new_tile_with_value(cast(u8)game.spawning_tile_coords.x, cast(u8)game.spawning_tile_coords.y, game.spawning_tile_value) + game.spawning_tile_coords = zephr.Vec2{0, 0} + game.spawning_tile_value = 0 + game.spawning_new_tile = false + game.animating = false game.spawning_tile_scale = 0.0 } } @@ -719,90 +699,90 @@ update_positions :: proc(delta_t: time.Duration) { draw_bg :: proc() { - window_size := zephr.get_window_size(); + window_size := zephr.get_window_size() board_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_width_constraint(&board_con, window_size.x, .FIXED); - zephr.set_height_constraint(&board_con, window_size.y, .FIXED); + zephr.set_width_constraint(&board_con, window_size.x, .FIXED) + zephr.set_height_constraint(&board_con, window_size.y, .FIXED) // bg style := zephr.UiStyle{ bg_color = game.palette.bg_color, align = .TOP_LEFT, - }; - zephr.draw_quad(&board_con, style); + } + zephr.draw_quad(&board_con, style) text_batch: zephr.GlyphInstanceList reserve(&text_batch, 32) // 2048 text - text_con := zephr.DEFAULT_UI_CONSTRAINTS; + text_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_y_constraint(&text_con, 0.05, .RELATIVE); - zephr.set_width_constraint(&text_con, 1.5, .RELATIVE_PIXELS); + zephr.set_y_constraint(&text_con, 0.05, .RELATIVE) + zephr.set_width_constraint(&text_con, 1.5, .RELATIVE_PIXELS) - zephr.add_text_instance(&text_batch, "2048", 100, text_con, zephr.Color{34, 234, 82, 155}, .TOP_CENTER); + zephr.add_text_instance(&text_batch, "2048", 100, text_con, zephr.Color{34, 234, 82, 155}, .TOP_CENTER) // score buf: [24]byte - score := fmt.bprintf(buf[:], "Score %d", game.score); + score := fmt.bprintf(buf[:], "Score %d", game.score) - zephr.set_parent_constraint(&text_con, nil); - zephr.set_x_constraint(&text_con, 0.05, .RELATIVE); - zephr.set_y_constraint(&text_con, 0.8, .RELATIVE); - zephr.add_text_instance(&text_batch, score, 40, text_con, zephr.COLOR_BLACK, .TOP_LEFT); + zephr.set_parent_constraint(&text_con, nil) + zephr.set_x_constraint(&text_con, 0.05, .RELATIVE) + zephr.set_y_constraint(&text_con, 0.8, .RELATIVE) + zephr.add_text_instance(&text_batch, score, 40, text_con, zephr.COLOR_BLACK, .TOP_LEFT) - zephr.draw_text_batch(&text_batch); + zephr.draw_text_batch(&text_batch) } draw_board :: proc() { // board - board_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_y_constraint(&board_con, 0.05, .RELATIVE); - zephr.set_height_constraint(&board_con, 0.6, .RELATIVE); - zephr.set_width_constraint(&board_con, 1, .ASPECT_RATIO); + board_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_y_constraint(&board_con, 0.05, .RELATIVE) + zephr.set_height_constraint(&board_con, 0.6, .RELATIVE) + zephr.set_width_constraint(&board_con, 1, .ASPECT_RATIO) style := zephr.UiStyle{ bg_color = game.palette.board_bg_color, border_radius = board_con.width * 0.02, align = .CENTER, - }; - zephr.draw_quad(&board_con, style); + } + zephr.draw_quad(&board_con, style) // empty tiles - tile_padding := board_con.width * 0.02; - tile_height := board_con.height * 0.225; + tile_padding := board_con.width * 0.02 + tile_height := board_con.height * 0.225 tile_board_radius := tile_height * 0.15 - empty_tile_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_parent_constraint(&empty_tile_con, &board_con); - zephr.set_height_constraint(&empty_tile_con, tile_height, .FIXED); - zephr.set_width_constraint(&empty_tile_con, 1, .ASPECT_RATIO); + empty_tile_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_parent_constraint(&empty_tile_con, &board_con) + zephr.set_height_constraint(&empty_tile_con, tile_height, .FIXED) + zephr.set_width_constraint(&empty_tile_con, 1, .ASPECT_RATIO) style = zephr.UiStyle{ bg_color = zephr.mult_color(game.palette.board_bg_color, 0.85), border_radius = tile_board_radius, align = .TOP_LEFT, - }; + } for i in 0..<4 { - zephr.set_y_constraint(&empty_tile_con, (tile_height + tile_padding) * cast(f32)i + tile_padding, .FIXED); + zephr.set_y_constraint(&empty_tile_con, (tile_height + tile_padding) * cast(f32)i + tile_padding, .FIXED) for j in 0..<4 { - zephr.set_x_constraint(&empty_tile_con, (tile_height + tile_padding) * cast(f32)j + tile_padding, .FIXED); - zephr.draw_quad(&empty_tile_con, style); + zephr.set_x_constraint(&empty_tile_con, (tile_height + tile_padding) * cast(f32)j + tile_padding, .FIXED) + zephr.draw_quad(&empty_tile_con, style) } } // filled tiles - text_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_width_constraint(&text_con, 1.5, .RELATIVE_PIXELS); - tile_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_parent_constraint(&tile_con, &board_con); - zephr.set_height_constraint(&tile_con, tile_height, .FIXED); - zephr.set_width_constraint(&tile_con, 1, .ASPECT_RATIO); + text_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_width_constraint(&text_con, 1.5, .RELATIVE_PIXELS) + tile_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_parent_constraint(&tile_con, &board_con) + zephr.set_height_constraint(&tile_con, tile_height, .FIXED) + zephr.set_width_constraint(&tile_con, 1, .ASPECT_RATIO) for i in 0..<4 { for j in 0..<4 { - tile := &game.board[i][j]; - tile_color := get_tile_color(tile.value); + tile := &game.board[i][j] + tile_color := get_tile_color(tile.value) style := zephr.UiStyle { bg_color = tile_color, border_radius = tile_board_radius, @@ -810,209 +790,209 @@ draw_board :: proc() { } buf: [16]byte value := fmt.bprintf(buf[:], "%d", tile.value) - tile_y_pos := ((tile_height + tile_padding) * cast(f32)i + tile_padding + cast(f32)game.board[i][j].anim_y_offset_relative * board_con.height); - tile_x_pos := ((tile_height + tile_padding) * cast(f32)j + tile_padding + cast(f32)game.board[i][j].anim_x_offset_relative * board_con.height); + tile_y_pos := ((tile_height + tile_padding) * cast(f32)i + tile_padding + cast(f32)game.board[i][j].anim_y_offset_relative * board_con.height) + tile_x_pos := ((tile_height + tile_padding) * cast(f32)j + tile_padding + cast(f32)game.board[i][j].anim_x_offset_relative * board_con.height) - zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED); - zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED); + zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED) + zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED) if (tile.value > 0) { - zephr.draw_quad(&tile_con, style); + zephr.draw_quad(&tile_con, style) // value text - zephr.set_parent_constraint(&text_con, &tile_con); - zephr.set_x_constraint(&text_con, 0, .FIXED); - zephr.set_y_constraint(&text_con, 0, .FIXED); + zephr.set_parent_constraint(&text_con, &tile_con) + zephr.set_x_constraint(&text_con, 0, .FIXED) + zephr.set_y_constraint(&text_con, 0, .FIXED) - zephr.draw_text(value, get_tile_font_size(tile.value), text_con, zephr.mult_color(tile_color, 0.5), .CENTER); + zephr.draw_text(value, get_tile_font_size(tile.value), text_con, zephr.mult_color(tile_color, 0.5), .CENTER) } } } if (game.spawning_new_tile) { // spawning tile - tile_color := get_tile_color(game.spawning_tile_value); + tile_color := get_tile_color(game.spawning_tile_value) style := zephr.UiStyle { bg_color = tile_color, border_radius = tile_board_radius * game.spawning_tile_scale, align = .TOP_LEFT, - }; - tile_x_pos := ((tile_height + tile_padding) * game.spawning_tile_coords.y + tile_padding) + tile_height / 2 - game.spawning_tile_scale * tile_height / 2; - tile_y_pos := ((tile_height + tile_padding) * game.spawning_tile_coords.x + tile_padding) + tile_height / 2 - game.spawning_tile_scale * tile_height / 2; - zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED); - zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED); - zephr.set_height_constraint(&tile_con, tile_height * game.spawning_tile_scale, .FIXED); - zephr.set_width_constraint(&tile_con, 1, .ASPECT_RATIO); - zephr.draw_quad(&tile_con, style); + } + tile_x_pos := ((tile_height + tile_padding) * game.spawning_tile_coords.y + tile_padding) + tile_height / 2 - game.spawning_tile_scale * tile_height / 2 + tile_y_pos := ((tile_height + tile_padding) * game.spawning_tile_coords.x + tile_padding) + tile_height / 2 - game.spawning_tile_scale * tile_height / 2 + zephr.set_x_constraint(&tile_con, tile_x_pos, .FIXED) + zephr.set_y_constraint(&tile_con, tile_y_pos, .FIXED) + zephr.set_height_constraint(&tile_con, tile_height * game.spawning_tile_scale, .FIXED) + zephr.set_width_constraint(&tile_con, 1, .ASPECT_RATIO) + zephr.draw_quad(&tile_con, style) // value text - zephr.set_parent_constraint(&text_con, &tile_con); - zephr.set_x_constraint(&text_con, 0, .FIXED); - zephr.set_y_constraint(&text_con, 0, .FIXED); + zephr.set_parent_constraint(&text_con, &tile_con) + zephr.set_x_constraint(&text_con, 0, .FIXED) + zephr.set_y_constraint(&text_con, 0, .FIXED) buf: [16]byte value := fmt.bprintf(buf[:], "%d", game.spawning_tile_value) - zephr.draw_text(value, cast(u32)(cast(f32)get_tile_font_size(game.spawning_tile_value) * game.spawning_tile_scale), text_con, zephr.mult_color(tile_color, 0.5), .CENTER); + zephr.draw_text(value, cast(u32)(cast(f32)get_tile_font_size(game.spawning_tile_value) * game.spawning_tile_scale), text_con, zephr.mult_color(tile_color, 0.5), .CENTER) } } draw_ui :: proc() { - bg_btns_state: zephr.ButtonState = (game.quit_dialog || game.help_dialog || game.settings_dialog || game.has_lost) ? .INACTIVE : .ACTIVE; - icon_btn_width :: 80; - icon_btn_offset :: 8; - icon_btns_padding :: 10; + bg_btns_state: zephr.ButtonState = (game.quit_dialog || game.help_dialog || game.settings_dialog || game.has_lost) ? .INACTIVE : .ACTIVE + icon_btn_width :: 80 + icon_btn_offset :: 8 + icon_btns_padding :: 10 - btn_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_x_constraint(&btn_con, -icon_btn_offset, .RELATIVE_PIXELS); - zephr.set_y_constraint(&btn_con, icon_btn_offset, .RELATIVE_PIXELS); - zephr.set_width_constraint(&btn_con, icon_btn_width, .RELATIVE_PIXELS); - zephr.set_height_constraint(&btn_con, 1, .ASPECT_RATIO); + btn_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_x_constraint(&btn_con, -icon_btn_offset, .RELATIVE_PIXELS) + zephr.set_y_constraint(&btn_con, icon_btn_offset, .RELATIVE_PIXELS) + zephr.set_width_constraint(&btn_con, icon_btn_width, .RELATIVE_PIXELS) + zephr.set_height_constraint(&btn_con, 1, .ASPECT_RATIO) style := zephr.UiStyle { bg_color = zephr.Color{201, 146, 126, 255}, fg_color = zephr.Color{40, 92, 100, 255}, border_radius = btn_con.height * 0.15, align = .TOP_RIGHT, - }; + } if (zephr.draw_icon_button(&btn_con, game.icon_textures[.HELP_ICON], style, bg_btns_state)) { - game.help_dialog = true; + game.help_dialog = true } - zephr.set_x_constraint(&btn_con, -icon_btn_offset - icon_btn_width - icon_btns_padding, .RELATIVE_PIXELS); + zephr.set_x_constraint(&btn_con, -icon_btn_offset - icon_btn_width - icon_btns_padding, .RELATIVE_PIXELS) if (zephr.draw_icon_button(&btn_con, game.icon_textures[.SETTINGS_ICON], style, bg_btns_state)) { - game.settings_dialog = true; + game.settings_dialog = true } - zephr.set_x_constraint(&btn_con, 0, .RELATIVE_PIXELS); - zephr.set_y_constraint(&btn_con, -0.05, .RELATIVE); - zephr.set_width_constraint(&btn_con, 200, .RELATIVE_PIXELS); - zephr.set_height_constraint(&btn_con, 65, .RELATIVE_PIXELS); + zephr.set_x_constraint(&btn_con, 0, .RELATIVE_PIXELS) + zephr.set_y_constraint(&btn_con, -0.05, .RELATIVE) + zephr.set_width_constraint(&btn_con, 200, .RELATIVE_PIXELS) + zephr.set_height_constraint(&btn_con, 65, .RELATIVE_PIXELS) style = zephr.UiStyle { bg_color = zephr.Color{201, 172, 126, 255}, fg_color = zephr.COLOR_BLACK, border_radius = btn_con.height * 0.25, align = .BOTTOM_CENTER, - }; + } if (zephr.draw_button(&btn_con, "New Game", style, bg_btns_state)) { - reset_game(); + reset_game() } if (game.settings_dialog) { - draw_settings_dialog(); + draw_settings_dialog() } if (game.help_dialog) { - draw_help_dialog(); + draw_help_dialog() } if (game.has_lost) { - draw_game_over(); + draw_game_over() } if (game.quit_dialog) { - draw_quit_dialog(); + draw_quit_dialog() } } draw_settings_dialog :: proc() { dialog_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_width_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_height_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_x_constraint(&dialog_con, 0, .FIXED); - zephr.set_y_constraint(&dialog_con, 0, .FIXED); + zephr.set_width_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_height_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_x_constraint(&dialog_con, 0, .FIXED) + zephr.set_y_constraint(&dialog_con, 0, .FIXED) style := zephr.UiStyle { bg_color = zephr.Color{0, 0, 0, 200}, align = .CENTER, - }; - zephr.draw_quad(&dialog_con, style); + } + zephr.draw_quad(&dialog_con, style) content_card_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&content_card_con, &dialog_con); - zephr.set_width_constraint(&content_card_con, 0.55, .RELATIVE); - zephr.set_height_constraint(&content_card_con, 0.6, .RELATIVE); + zephr.set_parent_constraint(&content_card_con, &dialog_con) + zephr.set_width_constraint(&content_card_con, 0.55, .RELATIVE) + zephr.set_height_constraint(&content_card_con, 0.6, .RELATIVE) style = zephr.UiStyle{ bg_color = zephr.Color{135, 124, 124, 255}, border_radius = 8, align = .CENTER, - }; - zephr.draw_quad(&content_card_con, style); + } + zephr.draw_quad(&content_card_con, style) text_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&text_con, &content_card_con); - zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS); - zephr.set_x_constraint(&text_con, 0, .FIXED); - zephr.set_y_constraint(&text_con, 40, .RELATIVE_PIXELS); - zephr.draw_text("Settings", 64, text_con, zephr.COLOR_YELLOW, .TOP_CENTER); + zephr.set_parent_constraint(&text_con, &content_card_con) + zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS) + zephr.set_x_constraint(&text_con, 0, .FIXED) + zephr.set_y_constraint(&text_con, 40, .RELATIVE_PIXELS) + zephr.draw_text("Settings", 64, text_con, zephr.COLOR_YELLOW, .TOP_CENTER) - zephr.set_x_constraint(&text_con, 30, .RELATIVE_PIXELS); - zephr.set_y_constraint(&text_con, 150, .RELATIVE_PIXELS); - zephr.draw_text("Color Palette", 40, text_con, zephr.COLOR_WHITE, .TOP_LEFT); + zephr.set_x_constraint(&text_con, 30, .RELATIVE_PIXELS) + zephr.set_y_constraint(&text_con, 150, .RELATIVE_PIXELS) + zephr.draw_text("Color Palette", 40, text_con, zephr.COLOR_WHITE, .TOP_LEFT) - bg_color_text := "Background: "; - font_size: u32 = 30; - zephr.set_x_constraint(&text_con, 30, .RELATIVE_PIXELS); - zephr.set_y_constraint(&text_con, 220, .RELATIVE_PIXELS); - zephr.set_height_constraint(&text_con, 0.9, .RELATIVE); - zephr.draw_text(bg_color_text, font_size, text_con, zephr.COLOR_WHITE, .TOP_LEFT); + bg_color_text := "Background: " + font_size: u32 = 30 + zephr.set_x_constraint(&text_con, 30, .RELATIVE_PIXELS) + zephr.set_y_constraint(&text_con, 220, .RELATIVE_PIXELS) + zephr.set_height_constraint(&text_con, 0.9, .RELATIVE) + zephr.draw_text(bg_color_text, font_size, text_con, zephr.COLOR_WHITE, .TOP_LEFT) - text_size := zephr.calculate_text_size(bg_color_text, font_size); + text_size := zephr.calculate_text_size(bg_color_text, font_size) color_picker_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&color_picker_con, &content_card_con); - zephr.set_x_constraint(&color_picker_con, 30 + text_size.x + 20, .RELATIVE_PIXELS); - zephr.set_y_constraint(&color_picker_con, 220 - text_size.y / 2 + 6, .RELATIVE_PIXELS); - zephr.set_width_constraint(&color_picker_con, 100, .RELATIVE_PIXELS); - zephr.set_height_constraint(&color_picker_con, 40, .RELATIVE_PIXELS); - zephr.draw_color_picker(&color_picker_con, &game.palette.bg_color, .TOP_LEFT, .ACTIVE); + zephr.set_parent_constraint(&color_picker_con, &content_card_con) + zephr.set_x_constraint(&color_picker_con, 30 + text_size.x + 20, .RELATIVE_PIXELS) + zephr.set_y_constraint(&color_picker_con, 220 - text_size.y / 2 + 6, .RELATIVE_PIXELS) + zephr.set_width_constraint(&color_picker_con, 100, .RELATIVE_PIXELS) + zephr.set_height_constraint(&color_picker_con, 40, .RELATIVE_PIXELS) + zephr.draw_color_picker(&color_picker_con, &game.palette.bg_color, .TOP_LEFT, .ACTIVE) - zephr.set_y_constraint(&text_con, 270, .RELATIVE_PIXELS); - zephr.draw_text("Board: ", font_size, text_con, zephr.COLOR_WHITE, .TOP_LEFT); + zephr.set_y_constraint(&text_con, 270, .RELATIVE_PIXELS) + zephr.draw_text("Board: ", font_size, text_con, zephr.COLOR_WHITE, .TOP_LEFT) - zephr.set_x_constraint(&color_picker_con, 30 + text_size.x + 20, .RELATIVE_PIXELS); - zephr.set_y_constraint(&color_picker_con, 270 - text_size.y / 2 + 6, .RELATIVE_PIXELS); - zephr.set_width_constraint(&color_picker_con, 100, .RELATIVE_PIXELS); - zephr.set_height_constraint(&color_picker_con, 40, .RELATIVE_PIXELS); - zephr.draw_color_picker(&color_picker_con, &game.palette.board_bg_color, .TOP_LEFT, .ACTIVE); + zephr.set_x_constraint(&color_picker_con, 30 + text_size.x + 20, .RELATIVE_PIXELS) + zephr.set_y_constraint(&color_picker_con, 270 - text_size.y / 2 + 6, .RELATIVE_PIXELS) + zephr.set_width_constraint(&color_picker_con, 100, .RELATIVE_PIXELS) + zephr.set_height_constraint(&color_picker_con, 40, .RELATIVE_PIXELS) + zephr.draw_color_picker(&color_picker_con, &game.palette.board_bg_color, .TOP_LEFT, .ACTIVE) - zephr.set_y_constraint(&text_con, 320, .RELATIVE_PIXELS); - zephr.draw_text("Tiles: ", font_size, text_con, zephr.COLOR_WHITE, .TOP_LEFT); + zephr.set_y_constraint(&text_con, 320, .RELATIVE_PIXELS) + zephr.draw_text("Tiles: ", font_size, text_con, zephr.COLOR_WHITE, .TOP_LEFT) - tile_size: f32 = 60; + tile_size: f32 = 60 for i in 0..<11 { - zephr.set_x_constraint(&color_picker_con, 30 + text_size.x + 20 + (cast(f32)i * (tile_size + 10)), .RELATIVE_PIXELS); - zephr.set_y_constraint(&color_picker_con, 320 - text_size.y / 2 + 6, .RELATIVE_PIXELS); - zephr.set_width_constraint(&color_picker_con, tile_size, .RELATIVE_PIXELS); - zephr.set_height_constraint(&color_picker_con, tile_size, .RELATIVE_PIXELS); - zephr.draw_color_picker(&color_picker_con, &game.palette.tile_colors[i], .TOP_LEFT, .ACTIVE, cast(u32)i); + zephr.set_x_constraint(&color_picker_con, 30 + text_size.x + 20 + (cast(f32)i * (tile_size + 10)), .RELATIVE_PIXELS) + zephr.set_y_constraint(&color_picker_con, 320 - text_size.y / 2 + 6, .RELATIVE_PIXELS) + zephr.set_width_constraint(&color_picker_con, tile_size, .RELATIVE_PIXELS) + zephr.set_height_constraint(&color_picker_con, tile_size, .RELATIVE_PIXELS) + zephr.draw_color_picker(&color_picker_con, &game.palette.tile_colors[i], .TOP_LEFT, .ACTIVE, cast(u32)i) - tile_val := cast(u16)math.pow(2, cast(f32)i + 1); + tile_val := cast(u16)math.pow(2, cast(f32)i + 1) buf: [16]byte - text := fmt.bprintf(buf[:], "%d", tile_val); - font_size := get_tile_font_size(tile_val) / 2; - tile_color := get_tile_color(tile_val); - zephr.set_parent_constraint(&text_con, &color_picker_con); - zephr.set_x_constraint(&text_con, 0, .RELATIVE_PIXELS); - zephr.set_y_constraint(&text_con, 0, .RELATIVE_PIXELS); - zephr.draw_text(text, font_size, text_con, zephr.mult_color(tile_color, 0.5), .CENTER); + text := fmt.bprintf(buf[:], "%d", tile_val) + font_size := get_tile_font_size(tile_val) / 2 + tile_color := get_tile_color(tile_val) + zephr.set_parent_constraint(&text_con, &color_picker_con) + zephr.set_x_constraint(&text_con, 0, .RELATIVE_PIXELS) + zephr.set_y_constraint(&text_con, 0, .RELATIVE_PIXELS) + zephr.draw_text(text, font_size, text_con, zephr.mult_color(tile_color, 0.5), .CENTER) } btn_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&btn_con, &content_card_con); - zephr.set_x_constraint(&btn_con, 0, .RELATIVE_PIXELS); - zephr.set_y_constraint(&btn_con, 400, .RELATIVE_PIXELS); - zephr.set_width_constraint(&btn_con, 150, .RELATIVE_PIXELS); - zephr.set_height_constraint(&btn_con, 0.35, .ASPECT_RATIO); + zephr.set_parent_constraint(&btn_con, &content_card_con) + zephr.set_x_constraint(&btn_con, 0, .RELATIVE_PIXELS) + zephr.set_y_constraint(&btn_con, 400, .RELATIVE_PIXELS) + zephr.set_width_constraint(&btn_con, 150, .RELATIVE_PIXELS) + zephr.set_height_constraint(&btn_con, 0.35, .ASPECT_RATIO) style = zephr.UiStyle{ bg_color = zephr.Color{201, 146, 126, 255}, fg_color = zephr.COLOR_BLACK, border_radius = 8, align = .TOP_CENTER, - }; + } if (zephr.draw_button(&btn_con, "Reset", style, .ACTIVE)) { - reset_palette(); + reset_palette() } - zephr.set_width_constraint(&btn_con, 56, .RELATIVE_PIXELS); - zephr.set_height_constraint(&btn_con, 1, .ASPECT_RATIO); - zephr.set_y_constraint(&btn_con, -24, .RELATIVE_PIXELS); - zephr.set_x_constraint(&btn_con, 24, .RELATIVE_PIXELS); + zephr.set_width_constraint(&btn_con, 56, .RELATIVE_PIXELS) + zephr.set_height_constraint(&btn_con, 1, .ASPECT_RATIO) + zephr.set_y_constraint(&btn_con, -24, .RELATIVE_PIXELS) + zephr.set_x_constraint(&btn_con, 24, .RELATIVE_PIXELS) style = zephr.UiStyle{ bg_color = zephr.mult_color(zephr.COLOR_WHITE, 0.8), fg_color = zephr.Color{232, 28, 36, 255}, @@ -1020,158 +1000,158 @@ draw_settings_dialog :: proc() { align = .TOP_RIGHT, } if (zephr.draw_icon_button(&btn_con, game.icon_textures[.CLOSE_ICON], style, .ACTIVE)) { - game.settings_dialog = false; + game.settings_dialog = false } } draw_help_dialog :: proc() { - dialog_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_width_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_height_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_x_constraint(&dialog_con, 0, .FIXED); - zephr.set_y_constraint(&dialog_con, 0, .FIXED); + dialog_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_width_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_height_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_x_constraint(&dialog_con, 0, .FIXED) + zephr.set_y_constraint(&dialog_con, 0, .FIXED) style := zephr.UiStyle { bg_color = zephr.Color{0, 0, 0, 200}, align = .CENTER, - }; - zephr.draw_quad(&dialog_con, style); + } + zephr.draw_quad(&dialog_con, style) - content_card_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_parent_constraint(&content_card_con, &dialog_con); - zephr.set_width_constraint(&content_card_con, 0.4, .RELATIVE); - zephr.set_height_constraint(&content_card_con, 0.33, .RELATIVE); + content_card_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_parent_constraint(&content_card_con, &dialog_con) + zephr.set_width_constraint(&content_card_con, 0.4, .RELATIVE) + zephr.set_height_constraint(&content_card_con, 0.33, .RELATIVE) style = zephr.UiStyle{ bg_color = zephr.Color{135, 124, 124, 255}, border_radius = 8, align = .CENTER, - }; - zephr.draw_quad(&content_card_con, style); + } + zephr.draw_quad(&content_card_con, style) - text_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_parent_constraint(&text_con, &content_card_con); - zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS); - zephr.set_x_constraint(&text_con, 0, .FIXED); - zephr.set_y_constraint(&text_con, 40, .RELATIVE_PIXELS); - zephr.draw_text("How to play", 64, text_con, zephr.COLOR_YELLOW, .TOP_CENTER); + text_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_parent_constraint(&text_con, &content_card_con) + zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS) + zephr.set_x_constraint(&text_con, 0, .FIXED) + zephr.set_y_constraint(&text_con, 40, .RELATIVE_PIXELS) + zephr.draw_text("How to play", 64, text_con, zephr.COLOR_YELLOW, .TOP_CENTER) - zephr.set_y_constraint(&text_con, 30, .RELATIVE_PIXELS); - zephr.set_height_constraint(&text_con, 0.9, .RELATIVE); + zephr.set_y_constraint(&text_con, 30, .RELATIVE_PIXELS) + zephr.set_height_constraint(&text_con, 0.9, .RELATIVE) zephr.draw_text("Use the arrow keys to move the tiles.\n" + "When two tiles with the same number touch, they\n" + - "merge into one!\n\nYour goal is to reach 2048 without filling all the tiles", 30., text_con, zephr.COLOR_WHITE, .CENTER); + "merge into one!\n\nYour goal is to reach 2048 without filling all the tiles", 30., text_con, zephr.COLOR_WHITE, .CENTER) btn_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&btn_con, &content_card_con); - zephr.set_width_constraint(&btn_con, 56, .RELATIVE_PIXELS); - zephr.set_height_constraint(&btn_con, 1, .ASPECT_RATIO); - zephr.set_y_constraint(&btn_con, -24, .RELATIVE_PIXELS); - zephr.set_x_constraint(&btn_con, 24, .RELATIVE_PIXELS); + zephr.set_parent_constraint(&btn_con, &content_card_con) + zephr.set_width_constraint(&btn_con, 56, .RELATIVE_PIXELS) + zephr.set_height_constraint(&btn_con, 1, .ASPECT_RATIO) + zephr.set_y_constraint(&btn_con, -24, .RELATIVE_PIXELS) + zephr.set_x_constraint(&btn_con, 24, .RELATIVE_PIXELS) style = zephr.UiStyle{ bg_color = zephr.mult_color(zephr.COLOR_WHITE, 0.8), fg_color = zephr.Color{232, 28, 36, 255}, border_radius = btn_con.height * 0.2, align = .TOP_RIGHT, - }; + } if (zephr.draw_icon_button(&btn_con, game.icon_textures[.CLOSE_ICON], style, .ACTIVE)) { - game.help_dialog = false; + game.help_dialog = false } } draw_game_over :: proc() { - dialog_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_width_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_height_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_x_constraint(&dialog_con, 0, .FIXED); - zephr.set_y_constraint(&dialog_con, 0, .FIXED); + dialog_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_width_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_height_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_x_constraint(&dialog_con, 0, .FIXED) + zephr.set_y_constraint(&dialog_con, 0, .FIXED) style := zephr.UiStyle { bg_color = zephr.Color{0, 0, 0, cast(u8)game.game_over_bg_opacity}, align = .CENTER, - }; - zephr.draw_quad(&dialog_con, style); + } + zephr.draw_quad(&dialog_con, style) - content_card_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_parent_constraint(&content_card_con, &dialog_con); - zephr.set_width_constraint(&content_card_con, 0.3, .RELATIVE); - zephr.set_height_constraint(&content_card_con, 0.2, .RELATIVE); + content_card_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_parent_constraint(&content_card_con, &dialog_con) + zephr.set_width_constraint(&content_card_con, 0.3, .RELATIVE) + zephr.set_height_constraint(&content_card_con, 0.2, .RELATIVE) style = zephr.UiStyle{ bg_color = zephr.Color{135, 124, 124, cast(u8)game.game_over_opacity}, border_radius = 8, align = .CENTER, - }; - zephr.draw_quad(&content_card_con, style); + } + zephr.draw_quad(&content_card_con, style) - text_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_parent_constraint(&text_con, &content_card_con); - zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS); - zephr.set_x_constraint(&text_con, 0, .FIXED); - zephr.set_y_constraint(&text_con, 40, .RELATIVE_PIXELS); - zephr.draw_text("Game Over!", 64, text_con, zephr.Color{255, 255, 0, cast(u8)game.game_over_opacity}, .TOP_CENTER); + text_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_parent_constraint(&text_con, &content_card_con) + zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS) + zephr.set_x_constraint(&text_con, 0, .FIXED) + zephr.set_y_constraint(&text_con, 40, .RELATIVE_PIXELS) + zephr.draw_text("Game Over!", 64, text_con, zephr.Color{255, 255, 0, cast(u8)game.game_over_opacity}, .TOP_CENTER) btn_con := zephr.DEFAULT_UI_CONSTRAINTS - btn_state: zephr.ButtonState = game.quit_dialog ? .INACTIVE : game.game_over_opacity > 0 ? .ACTIVE : .INACTIVE; - zephr.set_parent_constraint(&btn_con, &content_card_con); - zephr.set_x_constraint(&btn_con, 0, .RELATIVE_PIXELS); - zephr.set_y_constraint(&btn_con, -24, .RELATIVE_PIXELS); - zephr.set_width_constraint(&btn_con, 0.30, .RELATIVE); - zephr.set_height_constraint(&btn_con, 0.3, .ASPECT_RATIO); + btn_state: zephr.ButtonState = game.quit_dialog ? .INACTIVE : game.game_over_opacity > 0 ? .ACTIVE : .INACTIVE + zephr.set_parent_constraint(&btn_con, &content_card_con) + zephr.set_x_constraint(&btn_con, 0, .RELATIVE_PIXELS) + zephr.set_y_constraint(&btn_con, -24, .RELATIVE_PIXELS) + zephr.set_width_constraint(&btn_con, 0.30, .RELATIVE) + zephr.set_height_constraint(&btn_con, 0.3, .ASPECT_RATIO) style = zephr.UiStyle{ bg_color = zephr.Color{201, 146, 126, cast(u8)game.game_over_opacity}, fg_color = zephr.Color{0, 0, 0, cast(u8)game.game_over_opacity}, border_radius = 8, align = .BOTTOM_CENTER, - }; + } if (zephr.draw_button(&btn_con, "Try Again", style, btn_state)) { - reset_game(); + reset_game() } } draw_quit_dialog :: proc() { - dialog_con := zephr.DEFAULT_UI_CONSTRAINTS; - zephr.set_x_constraint(&dialog_con, 0, .FIXED); - zephr.set_y_constraint(&dialog_con, 0, .FIXED); - zephr.set_width_constraint(&dialog_con, 1, .RELATIVE); - zephr.set_height_constraint(&dialog_con, 1, .RELATIVE); + dialog_con := zephr.DEFAULT_UI_CONSTRAINTS + zephr.set_x_constraint(&dialog_con, 0, .FIXED) + zephr.set_y_constraint(&dialog_con, 0, .FIXED) + zephr.set_width_constraint(&dialog_con, 1, .RELATIVE) + zephr.set_height_constraint(&dialog_con, 1, .RELATIVE) style := zephr.UiStyle { bg_color = zephr.Color{0, 0, 0, 200}, align = .CENTER, - }; - zephr.draw_quad(&dialog_con, style); + } + zephr.draw_quad(&dialog_con, style) - zephr.set_width_constraint(&dialog_con, 0.4, .RELATIVE); - zephr.set_height_constraint(&dialog_con, 0.2, .RELATIVE); + zephr.set_width_constraint(&dialog_con, 0.4, .RELATIVE) + zephr.set_height_constraint(&dialog_con, 0.2, .RELATIVE) style = zephr.UiStyle{ bg_color = zephr.Color{135, 124, 124, 255}, border_radius = 8, align = .CENTER, - }; - zephr.draw_quad(&dialog_con, style); + } + zephr.draw_quad(&dialog_con, style) text_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&text_con, &dialog_con); - zephr.set_x_constraint(&text_con, 0, .FIXED); - zephr.set_y_constraint(&text_con, -0.25, .RELATIVE); - zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS); - zephr.draw_text("Are you sure you want to quit?", 36, text_con, zephr.COLOR_WHITE, .CENTER); + zephr.set_parent_constraint(&text_con, &dialog_con) + zephr.set_x_constraint(&text_con, 0, .FIXED) + zephr.set_y_constraint(&text_con, -0.25, .RELATIVE) + zephr.set_width_constraint(&text_con, 1, .RELATIVE_PIXELS) + zephr.draw_text("Are you sure you want to quit?", 36, text_con, zephr.COLOR_WHITE, .CENTER) btn_con := zephr.DEFAULT_UI_CONSTRAINTS - zephr.set_parent_constraint(&btn_con, &dialog_con); - zephr.set_width_constraint(&btn_con, 0.25, .RELATIVE); - zephr.set_height_constraint(&btn_con, 0.25, .RELATIVE); - zephr.set_y_constraint(&btn_con, -0.10, .RELATIVE); - zephr.set_x_constraint(&btn_con, -0.20, .RELATIVE); + zephr.set_parent_constraint(&btn_con, &dialog_con) + zephr.set_width_constraint(&btn_con, 0.25, .RELATIVE) + zephr.set_height_constraint(&btn_con, 0.25, .RELATIVE) + zephr.set_y_constraint(&btn_con, -0.10, .RELATIVE) + zephr.set_x_constraint(&btn_con, -0.20, .RELATIVE) style = zephr.UiStyle{ bg_color = zephr.mult_color(zephr.COLOR_WHITE, 0.9), fg_color = zephr.COLOR_BLACK, border_radius = btn_con.height * 0.2, align = .BOTTOM_CENTER, - }; + } if (zephr.draw_button(&btn_con, "Yes", style, .ACTIVE)) { - zephr.quit(); + zephr.quit() } - zephr.set_y_constraint(&btn_con, -0.10, .RELATIVE); - zephr.set_x_constraint(&btn_con, 0.20, .RELATIVE); + zephr.set_y_constraint(&btn_con, -0.10, .RELATIVE) + zephr.set_x_constraint(&btn_con, 0.20, .RELATIVE) if (zephr.draw_button(&btn_con, "No", style, .ACTIVE)) { - game.quit_dialog = false; + game.quit_dialog = false } } diff --git a/zephr/shader.odin b/zephr/shader.odin index 966cbb3..3a335c0 100644 --- a/zephr/shader.odin +++ b/zephr/shader.odin @@ -12,46 +12,46 @@ create_shader :: proc(vertex_path: string, fragment_path: string) -> (Shader, bo shader: Shader program, success := gl.load_shaders(vertex_path, fragment_path) - shader.program = program; + shader.program = program - return shader, success; + return shader, success } use_shader :: proc(shader: Shader) { - gl.UseProgram(shader.program); + gl.UseProgram(shader.program) } set_mat4f :: proc(shader: Shader, name: cstring, mat4: Mat4, transpose: bool = false) { mat4 := mat4 - loc := gl.GetUniformLocation(shader.program, name); + loc := gl.GetUniformLocation(shader.program, name) gl.UniformMatrix4fv(loc, 1, transpose, raw_data(&mat4)) } set_float :: proc(shader: Shader, name: cstring, val: f32) { - loc := gl.GetUniformLocation(shader.program, name); - gl.Uniform1f(loc, val); + loc := gl.GetUniformLocation(shader.program, name) + gl.Uniform1f(loc, val) } set_int :: proc(shader: Shader, name: cstring, val: i32) { - loc := gl.GetUniformLocation(shader.program, name); - gl.Uniform1i(loc, val); + loc := gl.GetUniformLocation(shader.program, name) + gl.Uniform1i(loc, val) } set_bool :: proc(shader: Shader, name: cstring, val: bool) { - set_int(shader, name, cast(i32)val); + set_int(shader, name, cast(i32)val) } set_vec2f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32) { - loc := gl.GetUniformLocation(shader.program, name); - gl.Uniform2f(loc, val1, val2); + loc := gl.GetUniformLocation(shader.program, name) + gl.Uniform2f(loc, val1, val2) } set_vec3f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32) { - loc := gl.GetUniformLocation(shader.program, name); - gl.Uniform3f(loc, val1, val2, val3); + loc := gl.GetUniformLocation(shader.program, name) + gl.Uniform3f(loc, val1, val2, val3) } set_vec4f :: proc(shader: Shader, name: cstring, val1: f32, val2: f32, val3: f32, val4: f32) { - loc := gl.GetUniformLocation(shader.program, name); - gl.Uniform4f(loc, val1, val2, val3, val4); + loc := gl.GetUniformLocation(shader.program, name) + gl.Uniform4f(loc, val1, val2, val3, val4) } diff --git a/zephr/text.odin b/zephr/text.odin index 98d1621..224bf10 100644 --- a/zephr/text.odin +++ b/zephr/text.odin @@ -45,14 +45,14 @@ font_instance_vbo : u32 init_freetype :: proc(font_path: cstring) -> i32 { - ft: FT.Library; + ft: FT.Library if (FT.Init_FreeType(&ft) != 0) { - return -1; + return -1 } - face: FT.Face; + face: FT.Face if (FT.New_Face(ft, font_path, 0, &face) != 0) { - return -2; + return -2 } // sets the variable font to be bold @@ -66,301 +66,301 @@ init_freetype :: proc(font_path: cstring) -> i32 { /* FT_Done_MM_Var(ft, mm); */ /* } */ - FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE); + FT.Set_Pixel_Sizes(face, 0, FONT_PIXEL_SIZE) - gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1); + gl.PixelStorei(gl.UNPACK_ALIGNMENT, 1) pen_x, pen_y: u32 /* FT_UInt glyph_idx; */ /* FT_ULong c = FT_Get_First_Char(face, &glyph_idx); */ - tex_width, tex_height: u32; + tex_width, tex_height: u32 for i in 32..<128 { if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) { - fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); + fmt.eprintf("[ERROR]: failed to load glyph for char '0x%x'\n", i) } /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ - tex_width += face.glyph.bitmap.width + 1; - tex_height = max(tex_height, face.glyph.bitmap.rows); + tex_width += face.glyph.bitmap.width + 1 + tex_height = max(tex_height, face.glyph.bitmap.rows) } - pixels := make([dynamic]u8, tex_width * tex_height); + pixels := make([dynamic]u8, tex_width * tex_height) defer delete(pixels) for i in 32..<128 { /* while (glyph_idx) { */ if (FT.Load_Char(face, cast(u64)i, .RENDER) != 0) { - fmt.printf("[ERROR]: failed to load glyph for char '0x%x'\n", i); + fmt.eprintf("[ERROR]: failed to load glyph for char '0x%x'\n", i) } /* FT_Render_Glyph(face->glyph, FT_RENDER_MODE_SDF); */ - bmp := &face.glyph.bitmap; + bmp := &face.glyph.bitmap if (pen_x + bmp.width >= tex_width) { - pen_x = 0; - pen_y += cast(u32)(1 + (face.size.metrics.height >> 6)); + pen_x = 0 + pen_y += cast(u32)(1 + (face.size.metrics.height >> 6)) } for row in 0.. i32 { - font_vbo, font_ebo: u32; + font_vbo, font_ebo: u32 - res := init_freetype(font_path); + res := init_freetype(font_path) if (res != 0) { - return res; + return res } - l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag"); + l_font_shader, success := create_shader("shaders/font.vert", "shaders/font.frag") if (!success) { fmt.println("[FATAL] Failed to create font shader") - return -2; + return -2 } - font_shader = l_font_shader; + font_shader = l_font_shader - gl.GenVertexArrays(1, &font_vao); - gl.GenBuffers(1, &font_vbo); - gl.GenBuffers(1, &font_instance_vbo); - gl.GenBuffers(1, &font_ebo); + gl.GenVertexArrays(1, &font_vao) + gl.GenBuffers(1, &font_vbo) + gl.GenBuffers(1, &font_instance_vbo) + gl.GenBuffers(1, &font_ebo) quad_vertices := [4][2]f32 { {0.0, 1.0}, // top left {1.0, 1.0}, // top right {1.0, 0.0}, // bottom right {0.0, 0.0}, // bottom left - }; + } quad_indices := [6]u32 { 0, 1, 2, 2, 3, 0, - }; + } - gl.BindVertexArray(font_vao); + gl.BindVertexArray(font_vao) // font quad vbo - gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo); - gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW); - gl.EnableVertexAttribArray(0); - gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0); + gl.BindBuffer(gl.ARRAY_BUFFER, font_vbo) + gl.BufferData(gl.ARRAY_BUFFER, size_of(quad_vertices), &quad_vertices, gl.STATIC_DRAW) + gl.EnableVertexAttribArray(0) + gl.VertexAttribPointer(0, 2, gl.FLOAT, gl.FALSE, 2 * size_of(f32), 0) // font instance vbo - gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo); - gl.EnableVertexAttribArray(1); - gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0); - gl.VertexAttribDivisor(1, 1); - gl.EnableVertexAttribArray(2); - gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4)); - gl.VertexAttribDivisor(2, 1); - gl.EnableVertexAttribArray(3); - gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32))); - gl.VertexAttribDivisor(3, 1); - gl.EnableVertexAttribArray(4); - gl.EnableVertexAttribArray(5); - gl.EnableVertexAttribArray(6); - gl.EnableVertexAttribArray(7); - gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32))); - gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32))); - gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32))); - gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32))); - gl.VertexAttribDivisor(4, 1); - gl.VertexAttribDivisor(5, 1); - gl.VertexAttribDivisor(6, 1); - gl.VertexAttribDivisor(7, 1); + gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo) + gl.EnableVertexAttribArray(1) + gl.VertexAttribPointer(1, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), 0) + gl.VertexAttribDivisor(1, 1) + gl.EnableVertexAttribArray(2) + gl.VertexAttribIPointer(2, 1, gl.INT, size_of(GlyphInstance), size_of(Vec4)) + gl.VertexAttribDivisor(2, 1) + gl.EnableVertexAttribArray(3) + gl.VertexAttribPointer(3, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) + size_of(i32))) + gl.VertexAttribDivisor(3, 1) + gl.EnableVertexAttribArray(4) + gl.EnableVertexAttribArray(5) + gl.EnableVertexAttribArray(6) + gl.EnableVertexAttribArray(7) + gl.VertexAttribPointer(4, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 2 + size_of(i32))) + gl.VertexAttribPointer(5, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 3 + size_of(i32))) + gl.VertexAttribPointer(6, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 4 + size_of(i32))) + gl.VertexAttribPointer(7, 4, gl.FLOAT, gl.FALSE, size_of(GlyphInstance), (size_of(Vec4) * 5 + size_of(i32))) + gl.VertexAttribDivisor(4, 1) + gl.VertexAttribDivisor(5, 1) + gl.VertexAttribDivisor(6, 1) + gl.VertexAttribDivisor(7, 1) // font ebo - gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo); - gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW); + gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, font_ebo) + gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(quad_indices), &quad_indices, gl.STATIC_DRAW) - use_shader(font_shader); + use_shader(font_shader) for i in 32..<128 { for j in 0..<4 { buf: [24]byte - text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j); - set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y); + text := fmt.bprintf(buf[:], "texcoords[%d]", (i - 32) * 4 + j) + set_vec2f(font_shader, cstring(raw_data(text)), zephr_ctx.font.characters[i].tex_coords[j].x, zephr_ctx.font.characters[i].tex_coords[j].y) } } - gl.BindBuffer(gl.ARRAY_BUFFER, 0); - gl.BindVertexArray(0); + gl.BindBuffer(gl.ARRAY_BUFFER, 0) + gl.BindVertexArray(0) - return 0; + return 0 } calculate_text_size :: proc(text: string, font_size: u32) -> Vec2 { - scale := cast(f32)font_size / FONT_PIXEL_SIZE; + scale := cast(f32)font_size / FONT_PIXEL_SIZE size : Vec2 = --- - w: f32 = 0; - h: f32 = 0; - max_bearing_h: f32 = 0; + w: f32 = 0 + h: f32 = 0 + max_bearing_h: f32 = 0 // NOTE: I don't like looping through the characters twice, but it's fine for now for i in 0.. 1) { - w -= ch.bearing.x; + w -= ch.bearing.x } // remove the trailing width of the last character if (i == len(text) - 1) { - w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x)); + w -= ((cast(f32)ch.advance / 64) - (ch.bearing.x + ch.size.x)) } // if we only have one character in the text, then remove the bearing width if (len(text) == 1) { - w -= (ch.bearing.x); + w -= (ch.bearing.x) } - h = max(h, max_bearing_h - ch.bearing.y + ch.size.y); + h = max(h, max_bearing_h - ch.bearing.y + ch.size.y) if (text[i] == '\n') { - w = 0; - h += max_bearing_h; + w = 0 + h += max_bearing_h } } - size.x = w * scale; - size.y = h * scale; + size.x = w * scale + size.y = h * scale - return size; + return size } draw_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) { - glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment); + glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment) - gl.ActiveTexture(gl.TEXTURE0); - gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id); - gl.BindVertexArray(font_vao); + gl.ActiveTexture(gl.TEXTURE0) + gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id) + gl.BindVertexArray(font_vao) - gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo); - gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW); + gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo) + gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(glyph_instance_list), raw_data(glyph_instance_list), gl.DYNAMIC_DRAW) gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(glyph_instance_list)) - gl.BindVertexArray(0); - gl.BindTexture(gl.TEXTURE_2D, 0); + gl.BindVertexArray(0) + gl.BindTexture(gl.TEXTURE_2D, 0) delete(glyph_instance_list) } draw_text_batch :: proc(batch: ^GlyphInstanceList) { - gl.ActiveTexture(gl.TEXTURE0); - gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id); - gl.BindVertexArray(font_vao); + gl.ActiveTexture(gl.TEXTURE0) + gl.BindTexture(gl.TEXTURE_2D, zephr_ctx.font.atlas_tex_id) + gl.BindVertexArray(font_vao) - gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo); - gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW); + gl.BindBuffer(gl.ARRAY_BUFFER, font_instance_vbo) + gl.BufferData(gl.ARRAY_BUFFER, size_of(GlyphInstance) * len(batch), raw_data(batch^), gl.DYNAMIC_DRAW) gl.DrawElementsInstanced(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil, cast(i32)len(batch)) - gl.BindVertexArray(0); - gl.BindTexture(gl.TEXTURE_2D, 0); + gl.BindVertexArray(0) + gl.BindTexture(gl.TEXTURE_2D, 0) delete(batch^) } get_glyph_instance_list_from_text :: proc(text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) -> GlyphInstanceList { constraints := constraints - use_shader(font_shader); - text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 }; + use_shader(font_shader) + text_color := Vec4{ cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255 } set_mat4f(font_shader, "projection", zephr_ctx.projection) rect: Rect - apply_constraints(&constraints, &rect.pos, &rect.size); + apply_constraints(&constraints, &rect.pos, &rect.size) - text_size := calculate_text_size(text, FONT_PIXEL_SIZE); - font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x; + text_size := calculate_text_size(text, FONT_PIXEL_SIZE) + font_scale := cast(f32)font_size / FONT_PIXEL_SIZE * rect.size.x - apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos); + apply_alignment(alignment, &constraints, Vec2{ text_size.x * font_scale, text_size.y * font_scale }, &rect.pos) model := identity() - scale(&model, Vec3{font_scale, font_scale, 1}); + scale(&model, Vec3{font_scale, font_scale, 1}) // rotate around the center point of the text - translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0}); - rotate(&model, constraints.rotation, Vec3{0, 0, 1}); - translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0}); + translate(&model, Vec3{-text_size.x * font_scale / 2, -text_size.y * font_scale / 2, 0}) + rotate(&model, constraints.rotation, Vec3{0, 0, 1}) + translate(&model, Vec3{text_size.x *font_scale / 2, text_size.y * font_scale / 2, 0}) - translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}); + translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}) - max_bearing_h: f32 = 0; + max_bearing_h: f32 = 0 for i in 0..> 6); - c += 1; + x += (ch.advance >> 6) + c += 1 } - return glyph_instance_list; + return glyph_instance_list } add_text_instance :: proc(batch: ^GlyphInstanceList, text: string, font_size: u32, constraints: UiConstraints, color: Color, alignment: Alignment) { - glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment); + glyph_instance_list := get_glyph_instance_list_from_text(text, font_size, constraints, color, alignment) append(batch, ..glyph_instance_list[:]) - delete(glyph_instance_list); + delete(glyph_instance_list) } diff --git a/zephr/texture.odin b/zephr/texture.odin index 1efa36f..0cd460d 100644 --- a/zephr/texture.odin +++ b/zephr/texture.odin @@ -6,40 +6,40 @@ import stb "vendor:stb/image" TextureId :: u32 load_texture :: proc(path: cstring) -> TextureId { - texture_id: TextureId; + texture_id: TextureId width, height, channels: i32 data := stb.load(path, &width, &height, &channels, 4) // RGBA defer stb.image_free(data) - format := gl.RGBA; + format := gl.RGBA switch (channels) { case 1: - format = gl.RED; - break; + format = gl.RED + break case 2: - format = gl.RG; - break; + format = gl.RG + break case 3: - format = gl.RGB; - break; + format = gl.RGB + break case 4: - format = gl.RGBA; - break; + format = gl.RGBA + break } - gl.GenTextures(1, &texture_id); - gl.BindTexture(gl.TEXTURE_2D, texture_id); + gl.GenTextures(1, &texture_id) + gl.BindTexture(gl.TEXTURE_2D, texture_id) - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT) - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR); - gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR) + gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR) - gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data); + gl.TexImage2D(gl.TEXTURE_2D, 0, cast(i32)format, width, height, 0, cast(u32)format, gl.UNSIGNED_BYTE, data) - gl.GenerateMipmap(gl.TEXTURE_2D); + gl.GenerateMipmap(gl.TEXTURE_2D) - return texture_id; + return texture_id } diff --git a/zephr/ui.odin b/zephr/ui.odin index 6181a7f..2e24df6 100644 --- a/zephr/ui.odin +++ b/zephr/ui.odin @@ -55,8 +55,15 @@ UiStyle :: struct { align: Alignment, } +UiElement :: struct { + id: UiIdHash, + rect: Rect, +} + Ui :: struct { + hovered_element: UiIdHash, active_element: UiIdHash, + elements: [dynamic]UiElement, popup_open: bool, popup_parent_hash: UiIdHash, popup_rect: Rect, @@ -85,12 +92,12 @@ DEFAULT_UI_CONSTRAINTS :: UiConstraints{ @private ui_init :: proc(font_path: cstring) { - res := init_fonts(font_path); + res := init_fonts(font_path) if (res == -1) { - fmt.printf("[ERROR]: could not initialize freetype library\n"); + fmt.eprintf("[ERROR]: could not initialize freetype library\n") os.exit(1) } else if (res == -2) { - fmt.printf("[ERROR]: could not load font file: \"%s\"\n", font_path); + fmt.eprintf("[ERROR]: could not load font file: \"%s\"\n", font_path) os.exit(1) } else if (res != 0) { os.exit(1) @@ -103,25 +110,25 @@ ui_init :: proc(font_path: cstring) { color_chooser_shader = l_color_chooser_shader if !success1 || !success2 { - fmt.printf("[ERROR]: could not load ui shaders\n"); + fmt.eprintf("[ERROR]: could not load ui shaders\n") os.exit(1) } - gl.GenVertexArrays(1, &ui_vao); - gl.GenBuffers(1, &ui_vbo); + gl.GenVertexArrays(1, &ui_vao) + gl.GenBuffers(1, &ui_vbo) - gl.BindVertexArray(ui_vao); - gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo); - gl.BufferData(gl.ARRAY_BUFFER, size_of(f32) * 6 * 4, nil, gl.DYNAMIC_DRAW); - gl.EnableVertexAttribArray(0); - gl.VertexAttribPointer(0, 4, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 0); + gl.BindVertexArray(ui_vao) + gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo) + gl.BufferData(gl.ARRAY_BUFFER, size_of(f32) * 6 * 4, nil, gl.DYNAMIC_DRAW) + gl.EnableVertexAttribArray(0) + gl.VertexAttribPointer(0, 4, gl.FLOAT, gl.FALSE, 4 * size_of(f32), 0) - gl.BindBuffer(gl.ARRAY_BUFFER, 0); - gl.BindVertexArray(0); + gl.BindBuffer(gl.ARRAY_BUFFER, 0) + gl.BindVertexArray(0) } set_parent_constraint :: proc(constraints: ^UiConstraints, parent_constraints: ^UiConstraints) { - constraints.parent = parent_constraints; + constraints.parent = parent_constraints } set_x_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) { @@ -129,19 +136,19 @@ set_x_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstr case .ASPECT_RATIO: // no-op. fall back to FIXED fallthrough case .FIXED: - constraints.x = value; + constraints.x = value case .RELATIVE: if (constraints.parent != nil) { - constraints.x = (value * constraints.parent.width); + constraints.x = (value * constraints.parent.width) } else { - constraints.x = (value * zephr_ctx.window.size.x); + constraints.x = (value * zephr_ctx.window.size.x) } case .RELATIVE_PIXELS: - constraints.x = zephr_ctx.window.size.x / zephr_ctx.screen_size.x * value; + constraints.x = zephr_ctx.window.size.x / zephr_ctx.screen_size.x * value } if (constraints.parent != nil) { - constraints.x += constraints.parent.x; + constraints.x += constraints.parent.x } } @@ -150,102 +157,102 @@ set_y_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstr case .ASPECT_RATIO: // no-op. fall back to FIXED fallthrough case .FIXED: - constraints.y = value; + constraints.y = value case .RELATIVE: if (constraints.parent != nil) { - constraints.y = (value * constraints.parent.height); + constraints.y = (value * constraints.parent.height) } else { - constraints.y = (value * zephr_ctx.window.size.y); + constraints.y = (value * zephr_ctx.window.size.y) } case .RELATIVE_PIXELS: - constraints.y = zephr_ctx.window.size.y / zephr_ctx.screen_size.y * value; + constraints.y = zephr_ctx.window.size.y / zephr_ctx.screen_size.y * value } if (constraints.parent != nil) { - constraints.y += constraints.parent.y; + constraints.y += constraints.parent.y } } set_width_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) { switch (type) { case .FIXED: - constraints.width = value; + constraints.width = value case .RELATIVE: if (constraints.parent != nil) { - constraints.width = (value * constraints.parent.width); + constraints.width = (value * constraints.parent.width) } else { - constraints.width = (value * zephr_ctx.window.size.x); + constraints.width = (value * zephr_ctx.window.size.x) } case .RELATIVE_PIXELS: - constraints.width = zephr_ctx.window.size.x / zephr_ctx.screen_size.x * value; + constraints.width = zephr_ctx.window.size.x / zephr_ctx.screen_size.x * value case .ASPECT_RATIO: - constraints.width = constraints.height * value; + constraints.width = constraints.height * value } } set_height_constraint :: proc(constraints: ^UiConstraints, value: f32, type: UiConstraint) { switch (type) { case .FIXED: - constraints.height = value; + constraints.height = value case .RELATIVE: if (constraints.parent != nil) { - constraints.height = (value * constraints.parent.height); + constraints.height = (value * constraints.parent.height) } else { - constraints.height = (value * zephr_ctx.window.size.y); + constraints.height = (value * zephr_ctx.window.size.y) } case .RELATIVE_PIXELS: - constraints.height = zephr_ctx.window.size.y / zephr_ctx.screen_size.y * value; + constraints.height = zephr_ctx.window.size.y / zephr_ctx.screen_size.y * value case .ASPECT_RATIO: - constraints.height = constraints.width * value; + constraints.height = constraints.width * value } } set_rotation_constraint :: proc(constraints: ^UiConstraints, angle_d: f32) { - constraints.rotation = angle_d; + constraints.rotation = angle_d } @private apply_constraints :: proc(constraints: ^UiConstraints, pos: ^Vec2, size: ^Vec2) { - pos^ = Vec2{constraints.x, constraints.y}; - size^ = Vec2{constraints.width, constraints.height}; + pos^ = Vec2{constraints.x, constraints.y} + size^ = Vec2{constraints.width, constraints.height} } @private apply_alignment :: proc(align: Alignment, constraints: ^UiConstraints, size: Vec2, pos: ^Vec2) { // if we don't have a parent then we're a top level element and we should align against the window - parent_size := Vec2{zephr_ctx.window.size.x, zephr_ctx.window.size.y}; + parent_size := Vec2{zephr_ctx.window.size.x, zephr_ctx.window.size.y} if (constraints.parent != nil) { - parent_size = Vec2{constraints.parent.width, constraints.parent.height}; + parent_size = Vec2{constraints.parent.width, constraints.parent.height} } switch (align) { case .TOP_LEFT: - pos.x = pos.x; - pos.y = pos.y; + pos.x = pos.x + pos.y = pos.y case .TOP_CENTER: - pos.x += parent_size.x / 2 - size.x / 2; - pos.y = pos.y; + pos.x += parent_size.x / 2 - size.x / 2 + pos.y = pos.y case .TOP_RIGHT: - pos.x += parent_size.x - size.x; - pos.y = pos.y; + pos.x += parent_size.x - size.x + pos.y = pos.y case .BOTTOM_LEFT: - pos.x = pos.x; - pos.y += parent_size.y - size.y; + pos.x = pos.x + pos.y += parent_size.y - size.y case .BOTTOM_CENTER: - pos.x += parent_size.x / 2 - size.x / 2; - pos.y += parent_size.y - size.y; + pos.x += parent_size.x / 2 - size.x / 2 + pos.y += parent_size.y - size.y case .BOTTOM_RIGHT: - pos.x += parent_size.x - size.x; - pos.y += parent_size.y - size.y; + pos.x += parent_size.x - size.x + pos.y += parent_size.y - size.y case .LEFT_CENTER: - pos.x = pos.x; - pos.y += parent_size.y / 2 - size.y / 2; + pos.x = pos.x + pos.y += parent_size.y / 2 - size.y / 2 case .RIGHT_CENTER: - pos.x += parent_size.x - size.x; - pos.y += parent_size.y / 2 - size.y / 2; + pos.x += parent_size.x - size.x + pos.y += parent_size.y / 2 - size.y / 2 case .CENTER: - pos.x += parent_size.x / 2 - size.x / 2; - pos.y += parent_size.y / 2 - size.y / 2; + pos.x += parent_size.x / 2 - size.x / 2 + pos.y += parent_size.y / 2 - size.y / 2 } } @@ -253,7 +260,7 @@ apply_alignment :: proc(align: Alignment, constraints: ^UiConstraints, size: Vec // TODO: make this handle rotated and scaled rects too inside_rect :: proc(rect: Rect, point: Vec2) -> bool { return point.x >= rect.pos.x && point.x <= rect.pos.x + rect.size.x && - point.y >= rect.pos.y && point.y <= rect.pos.y + rect.size.y; + point.y >= rect.pos.y && point.y <= rect.pos.y + rect.size.y } draw_quad :: proc(constraints: ^UiConstraints, style: UiStyle) { @@ -271,21 +278,21 @@ draw_quad :: proc(constraints: ^UiConstraints, style: UiStyle) { apply_alignment(style.align, constraints, rect.size, &rect.pos) // set the positions after applying alignment so children can use them - constraints.x = rect.pos.x; - constraints.y = rect.pos.y; + constraints.x = rect.pos.x + constraints.y = rect.pos.y - model := identity(); - translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}); - scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1}); - translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}); + model := identity() + translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}) + scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1}) + translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}) - translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}); - rotate(&model, constraints.rotation, Vec3{0, 0, 1}); - translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}); + translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}) + rotate(&model, constraints.rotation, Vec3{0, 0, 1}) + translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}) - translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}); + translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}) - set_mat4f(ui_shader, "model", model); + set_mat4f(ui_shader, "model", model) gl.BindVertexArray(ui_vao) @@ -299,112 +306,112 @@ draw_quad :: proc(constraints: ^UiConstraints, style: UiStyle) { {0, 0 + rect.size.y, 0.0, 1.0}, {0 + rect.size.x, 0, 1.0, 0.0}, {0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0}, - }; + } - gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo); - gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices)); - gl.BindBuffer(gl.ARRAY_BUFFER, 0); + gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo) + gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices)) + gl.BindBuffer(gl.ARRAY_BUFFER, 0) - gl.DrawArrays(gl.TRIANGLES, 0, 6); + gl.DrawArrays(gl.TRIANGLES, 0, 6) - gl.BindVertexArray(0); + gl.BindVertexArray(0) } draw_circle :: proc(constraints: ^UiConstraints, style: UiStyle) { - radius: f32 = 0; + radius: f32 = 0 if (constraints.width > constraints.height) { - radius = constraints.height / 2; + radius = constraints.height / 2 } else { - radius = constraints.width / 2; + radius = constraints.width / 2 } - new_style := style; - new_style.border_radius = radius; + new_style := style + new_style.border_radius = radius - draw_quad(constraints, new_style); + draw_quad(constraints, new_style) } draw_triangle :: proc(constraints: ^UiConstraints, style: UiStyle) { - use_shader(ui_shader); + use_shader(ui_shader) - set_vec4f(ui_shader, "aColor", cast(f32)style.bg_color.r / 255, cast(f32)style.bg_color.g / 255, cast(f32)style.bg_color.b / 255, cast(f32)style.bg_color.a / 255); - set_float(ui_shader, "borderRadius", 0); + set_vec4f(ui_shader, "aColor", cast(f32)style.bg_color.r / 255, cast(f32)style.bg_color.g / 255, cast(f32)style.bg_color.b / 255, cast(f32)style.bg_color.a / 255) + set_float(ui_shader, "borderRadius", 0) set_mat4f(ui_shader, "projection", zephr_ctx.projection) rect: Rect = --- - apply_constraints(constraints, &rect.pos, &rect.size); - apply_alignment(style.align, constraints, rect.size, &rect.pos); + apply_constraints(constraints, &rect.pos, &rect.size) + apply_alignment(style.align, constraints, rect.size, &rect.pos) // set the positions after applying alignment so children can use them - constraints.x = rect.pos.x; - constraints.y = rect.pos.y; + constraints.x = rect.pos.x + constraints.y = rect.pos.y - model := identity(); - translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}); - scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1}); - translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}); + model := identity() + translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}) + scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1}) + translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}) - translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}); - rotate(&model, constraints.rotation, Vec3{0, 0, 1}); - translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}); + translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}) + rotate(&model, constraints.rotation, Vec3{0, 0, 1}) + translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}) - translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}); + translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}) - set_mat4f(ui_shader, "model", model); + set_mat4f(ui_shader, "model", model) - gl.BindVertexArray(ui_vao); + gl.BindVertexArray(ui_vao) vertices := [3][4]f32 { {0 + rect.size.x, 0, 0.0, 0.0}, {0, 0, 0.0, 0.0}, {0 + rect.size.x / 2, 0 + rect.size.y, 0.0, 0.0}, - }; + } - gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo); + gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo) gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices)) - gl.BindBuffer(gl.ARRAY_BUFFER, 0); + gl.BindBuffer(gl.ARRAY_BUFFER, 0) - gl.DrawArrays(gl.TRIANGLES, 0, 3); + gl.DrawArrays(gl.TRIANGLES, 0, 3) - gl.BindVertexArray(0); + gl.BindVertexArray(0) } draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color: Color, radius: f32, align: Alignment) { - use_shader(ui_shader); + use_shader(ui_shader) - set_vec4f(ui_shader, "aColor", cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255); - set_float(ui_shader, "uiWidth", constraints.width); - set_float(ui_shader, "uiHeight", constraints.height); - set_float(ui_shader, "borderRadius", radius); - set_bool(ui_shader, "hasTexture", true); + set_vec4f(ui_shader, "aColor", cast(f32)color.r / 255, cast(f32)color.g / 255, cast(f32)color.b / 255, cast(f32)color.a / 255) + set_float(ui_shader, "uiWidth", constraints.width) + set_float(ui_shader, "uiHeight", constraints.height) + set_float(ui_shader, "borderRadius", radius) + set_bool(ui_shader, "hasTexture", true) set_mat4f(ui_shader, "projection", zephr_ctx.projection) rect: Rect = --- - apply_constraints(constraints, &rect.pos, &rect.size); - apply_alignment(align, constraints, rect.size, &rect.pos); + apply_constraints(constraints, &rect.pos, &rect.size) + apply_alignment(align, constraints, rect.size, &rect.pos) // set the positions after applying alignment so children can use them - constraints.x = rect.pos.x; - constraints.y = rect.pos.y; + constraints.x = rect.pos.x + constraints.y = rect.pos.y - model := identity(); - translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}); - scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1}); - translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}); + model := identity() + translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}) + scale(&model, Vec3{constraints.scale.x, constraints.scale.y, 1}) + translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}) - translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}); - rotate(&model, constraints.rotation, Vec3{0, 0, 1}); - translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}); + translate(&model, Vec3{-rect.size.x / 2, -rect.size.y / 2, 0}) + rotate(&model, constraints.rotation, Vec3{0, 0, 1}) + translate(&model, Vec3{rect.size.x / 2, rect.size.y / 2, 0}) - translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}); + translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}) set_mat4f(ui_shader, "model", model) - gl.BindVertexArray(ui_vao); + gl.BindVertexArray(ui_vao) vertices := [6][4]f32 { // bottom left tri @@ -416,19 +423,19 @@ draw_texture :: proc(constraints: ^UiConstraints, texture_id: TextureId, color: {0, 0 + rect.size.y, 0.0, 1.0}, {0 + rect.size.x, 0, 1.0, 0.0}, {0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0}, - }; + } - gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo); + gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo) gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices)) - gl.BindBuffer(gl.ARRAY_BUFFER, 0); + gl.BindBuffer(gl.ARRAY_BUFFER, 0) - gl.ActiveTexture(gl.TEXTURE0); - gl.BindTexture(gl.TEXTURE_2D, texture_id); + gl.ActiveTexture(gl.TEXTURE0) + gl.BindTexture(gl.TEXTURE_2D, texture_id) - gl.DrawArrays(gl.TRIANGLES, 0, 6); + gl.DrawArrays(gl.TRIANGLES, 0, 6) - gl.BindVertexArray(0); - set_bool(ui_shader, "hasTexture", false); + gl.BindVertexArray(0) + set_bool(ui_shader, "hasTexture", false) } draw_button :: proc(constraints: ^UiConstraints, text: string, style: UiStyle, state: ButtonState, caller := #caller_location) -> bool { @@ -440,75 +447,82 @@ draw_button :: proc(constraints: ^UiConstraints, text: string, style: UiStyle, s style := style rect: Rect = --- - apply_constraints(constraints, &rect.pos, &rect.size); - apply_alignment(style.align, constraints, rect.size, &rect.pos); + apply_constraints(constraints, &rect.pos, &rect.size) + apply_alignment(style.align, constraints, rect.size, &rect.pos) - is_hovered := inside_rect(rect, zephr_ctx.mouse.pos); + is_hovered := zephr_ctx.ui.hovered_element == hash is_held := zephr_ctx.ui.active_element == hash - left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT; - left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT; - clicked := false; + left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT + left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT + clicked := false if (zephr_ctx.ui.active_element == 0) { if (is_hovered && left_mouse_pressed) { - zephr_ctx.ui.active_element = hash; + zephr_ctx.ui.active_element = hash } } else if (zephr_ctx.ui.active_element == hash) { if (left_mouse_released) { - zephr_ctx.ui.active_element = 0; + zephr_ctx.ui.active_element = 0 if (is_hovered) { - clicked = true; + clicked = true } } } if (is_hovered && state == .ACTIVE) { - set_cursor(Cursor.HAND); + set_cursor(Cursor.HAND) if (is_held) { - style.bg_color = mult_color(style.bg_color, 0.8); - draw_quad(constraints, style); + style.bg_color = mult_color(style.bg_color, 0.8) + draw_quad(constraints, style) } else { - style.bg_color = mult_color(style.bg_color, 0.9); - draw_quad(constraints, style); + style.bg_color = mult_color(style.bg_color, 0.9) + draw_quad(constraints, style) } } else if (state == .DISABLED) { if (is_hovered) { - set_cursor(Cursor.DISABLED); + set_cursor(Cursor.DISABLED) } - style.bg_color.a = 100; - style.fg_color.a = 100; - draw_quad(constraints, style); + style.bg_color.a = 100 + style.fg_color.a = 100 + draw_quad(constraints, style) } else { - draw_quad(constraints, style); + draw_quad(constraints, style) } if (text != "") { text_constraints := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&text_constraints, constraints); - set_x_constraint(&text_constraints, 0, .RELATIVE_PIXELS); - set_y_constraint(&text_constraints, 0, .RELATIVE_PIXELS); - set_width_constraint(&text_constraints, 1.0, .RELATIVE_PIXELS); + set_parent_constraint(&text_constraints, constraints) + set_x_constraint(&text_constraints, 0, .RELATIVE_PIXELS) + set_y_constraint(&text_constraints, 0, .RELATIVE_PIXELS) + set_width_constraint(&text_constraints, 1.0, .RELATIVE_PIXELS) - font_size := ((constraints.width) / (text_constraints.width * 0.1 * 65)); + font_size := ((constraints.width) / (text_constraints.width * 0.1 * 65)) // TODO: need to adjust the font size based on the text height - draw_text(text, cast(u32)font_size, text_constraints, style.fg_color, .CENTER); + draw_text(text, cast(u32)font_size, text_constraints, style.fg_color, .CENTER) } + element := UiElement{ + id = hash, + rect = rect, + } + + append(&zephr_ctx.ui.elements, element) + if (clicked && state == .ACTIVE) { - return true; + return true } - return false; + return false } draw_icon_button :: proc(constraints: ^UiConstraints, icon_tex_id: TextureId, style: UiStyle, state: ButtonState, caller := #caller_location) -> bool { - assert(icon_tex_id != 0, "draw_icon_button() requires that you provide an icon texture"); + assert(icon_tex_id != 0, "draw_icon_button() requires that you provide an icon texture") line := caller.line line_bytes := mem.byte_slice(&line, size_of(line)) @@ -518,64 +532,71 @@ draw_icon_button :: proc(constraints: ^UiConstraints, icon_tex_id: TextureId, st style := style rect: Rect = --- - apply_constraints(constraints, &rect.pos, &rect.size); - apply_alignment(style.align, constraints, rect.size, &rect.pos); + apply_constraints(constraints, &rect.pos, &rect.size) + apply_alignment(style.align, constraints, rect.size, &rect.pos) - is_held := zephr_ctx.ui.active_element == hash; - is_hovered := inside_rect(rect, zephr_ctx.mouse.pos); - left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT; - left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT; - clicked := false; + is_held := zephr_ctx.ui.active_element == hash + is_hovered := zephr_ctx.ui.hovered_element == hash + left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT + left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT + clicked := false if (zephr_ctx.ui.active_element == 0) { if (is_hovered && left_mouse_pressed) { - zephr_ctx.ui.active_element = hash; + zephr_ctx.ui.active_element = hash } } else if (zephr_ctx.ui.active_element == hash) { if (left_mouse_released) { - zephr_ctx.ui.active_element = 0; + zephr_ctx.ui.active_element = 0 if (is_hovered) { - clicked = true; + clicked = true } } } if (is_hovered && state == .ACTIVE) { - set_cursor(.HAND); + set_cursor(.HAND) if (is_held) { - style.bg_color = mult_color(style.bg_color, 0.8); - style.fg_color = mult_color(style.fg_color, 0.8); + style.bg_color = mult_color(style.bg_color, 0.8) + style.fg_color = mult_color(style.fg_color, 0.8) } else { - style.bg_color = mult_color(style.bg_color, 0.9); - style.fg_color = mult_color(style.fg_color, 0.9); + style.bg_color = mult_color(style.bg_color, 0.9) + style.fg_color = mult_color(style.fg_color, 0.9) } } else if (state == .DISABLED) { if (is_hovered) { - set_cursor(.DISABLED); + set_cursor(.DISABLED) } - style.fg_color.a = 100; - style.bg_color.a = 100; + style.fg_color.a = 100 + style.bg_color.a = 100 } - draw_quad(constraints, style); + draw_quad(constraints, style) icon_constraints := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&icon_constraints, constraints); - set_x_constraint(&icon_constraints, 0, .RELATIVE_PIXELS); - set_y_constraint(&icon_constraints, 0, .RELATIVE_PIXELS); - set_width_constraint(&icon_constraints, constraints.width * 0.8, .FIXED); - set_height_constraint(&icon_constraints, constraints.height * 0.8, .FIXED); - draw_texture(&icon_constraints, icon_tex_id, style.fg_color, 0, .CENTER); + set_parent_constraint(&icon_constraints, constraints) + set_x_constraint(&icon_constraints, 0, .RELATIVE_PIXELS) + set_y_constraint(&icon_constraints, 0, .RELATIVE_PIXELS) + set_width_constraint(&icon_constraints, constraints.width * 0.8, .FIXED) + set_height_constraint(&icon_constraints, constraints.height * 0.8, .FIXED) + draw_texture(&icon_constraints, icon_tex_id, style.fg_color, 0, .CENTER) if (clicked && state == .ACTIVE) { - return true; + return true } - return false; + element := UiElement{ + id = hash, + rect = rect + } + + append(&zephr_ctx.ui.elements, element) + + return false } @private @@ -587,49 +608,49 @@ draw_color_picker_slider :: proc(constraints: ^UiConstraints, align: Alignment, line := caller.line line_bytes := mem.byte_slice(&line, size_of(line)) - hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT); - hash = fnv_hash32(line_bytes, size_of(caller.line), hash); + hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT) + hash = fnv_hash32(line_bytes, size_of(caller.line), hash) - use_shader(color_chooser_shader); + use_shader(color_chooser_shader) - set_bool(color_chooser_shader, "isSlider", true); - set_mat4f(color_chooser_shader, "projection", zephr_ctx.projection); + set_bool(color_chooser_shader, "isSlider", true) + set_mat4f(color_chooser_shader, "projection", zephr_ctx.projection) rect: Rect = --- - apply_constraints(constraints, &rect.pos, &rect.size); - apply_alignment(align, constraints, rect.size, &rect.pos); + apply_constraints(constraints, &rect.pos, &rect.size) + apply_alignment(align, constraints, rect.size, &rect.pos) // set the positions after applying alignment so children can use them - constraints.x = rect.pos.x; - constraints.y = rect.pos.y; + constraints.x = rect.pos.x + constraints.y = rect.pos.y - is_hovered := inside_rect(rect, zephr_ctx.mouse.pos); - left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT; - left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT; + is_hovered := zephr_ctx.ui.hovered_element == hash + left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT + left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT if (zephr_ctx.ui.active_element == 0) { if (is_hovered && left_mouse_pressed) { - zephr_ctx.ui.active_element = hash; - dragging = true; + zephr_ctx.ui.active_element = hash + dragging = true } } else if (zephr_ctx.ui.active_element == hash) { if (left_mouse_released) { - zephr_ctx.ui.active_element = 0; - dragging = false; + zephr_ctx.ui.active_element = 0 + dragging = false } } if (dragging) { - slider_selection = clamp((zephr_ctx.mouse.pos.y - constraints.y) / constraints.height, 0, 1); + slider_selection = clamp((zephr_ctx.mouse.pos.y - constraints.y) / constraints.height, 0, 1) } - model := identity(); + model := identity() translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}) - set_mat4f(color_chooser_shader, "model", model); + set_mat4f(color_chooser_shader, "model", model) - gl.BindVertexArray(ui_vao); + gl.BindVertexArray(ui_vao) vertices := [6][4]f32 { // bottom left tri @@ -641,98 +662,105 @@ draw_color_picker_slider :: proc(constraints: ^UiConstraints, align: Alignment, {0, 0 + rect.size.y, 0.0, 1.0}, {0 + rect.size.x, 0, 1.0, 0.0}, {0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0}, - }; + } - gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo); + gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo) gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices)) - gl.BindBuffer(gl.ARRAY_BUFFER, 0); + gl.BindBuffer(gl.ARRAY_BUFFER, 0) - gl.DrawArrays(gl.TRIANGLES, 0, 6); + gl.DrawArrays(gl.TRIANGLES, 0, 6) - gl.BindVertexArray(0); - set_bool(color_chooser_shader, "isSlider", false); + gl.BindVertexArray(0) + set_bool(color_chooser_shader, "isSlider", false) // draw the selection triangle_con := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&triangle_con, constraints); - set_x_constraint(&triangle_con, -11, .RELATIVE_PIXELS); - set_y_constraint(&triangle_con, slider_selection, .RELATIVE); - set_rotation_constraint(&triangle_con, -90); - set_width_constraint(&triangle_con, 14, .RELATIVE_PIXELS); - set_height_constraint(&triangle_con, 0.5, .ASPECT_RATIO); - triangle_con.y += -(triangle_con.height / 2); + set_parent_constraint(&triangle_con, constraints) + set_x_constraint(&triangle_con, -11, .RELATIVE_PIXELS) + set_y_constraint(&triangle_con, slider_selection, .RELATIVE) + set_rotation_constraint(&triangle_con, -90) + set_width_constraint(&triangle_con, 14, .RELATIVE_PIXELS) + set_height_constraint(&triangle_con, 0.5, .ASPECT_RATIO) + triangle_con.y += -(triangle_con.height / 2) tri_style := UiStyle { bg_color = COLOR_BLACK, border_radius = 0, align = .TOP_LEFT, - }; - draw_triangle(&triangle_con, tri_style); + } + draw_triangle(&triangle_con, tri_style) - set_x_constraint(&triangle_con, 1, .RELATIVE); - triangle_con.x -= triangle_con.height / 2; - set_rotation_constraint(&triangle_con, 90); - draw_triangle(&triangle_con, tri_style); + set_x_constraint(&triangle_con, 1, .RELATIVE) + triangle_con.x -= triangle_con.height / 2 + set_rotation_constraint(&triangle_con, 90) + draw_triangle(&triangle_con, tri_style) - return slider_selection; + element := UiElement{ + id = hash, + rect = rect + } + + append(&zephr_ctx.ui.elements, element) + + return slider_selection } @private draw_color_picker_canvas :: proc(constraints: ^UiConstraints, slider_percentage: f32, align: Alignment, caller := #caller_location) -> Color { @static - dragging := false; + dragging := false @static - canvas_pos := Vec2{0, 0}; + canvas_pos := Vec2{0, 0} line := caller.line line_bytes := mem.byte_slice(&line, size_of(line)) - hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT); - hash = fnv_hash32(line_bytes, size_of(caller.line), hash); + hash := fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), FNV_HASH32_INIT) + hash = fnv_hash32(line_bytes, size_of(caller.line), hash) - use_shader(color_chooser_shader); + use_shader(color_chooser_shader) - set_bool(color_chooser_shader, "isSlider", false); - set_float(color_chooser_shader, "sliderPercentage", slider_percentage); - set_mat4f(color_chooser_shader, "projection", zephr_ctx.projection); + set_bool(color_chooser_shader, "isSlider", false) + set_float(color_chooser_shader, "sliderPercentage", slider_percentage) + set_mat4f(color_chooser_shader, "projection", zephr_ctx.projection) rect: Rect = --- - apply_constraints(constraints, &rect.pos, &rect.size); - apply_alignment(align, constraints, rect.size, &rect.pos); + apply_constraints(constraints, &rect.pos, &rect.size) + apply_alignment(align, constraints, rect.size, &rect.pos) // set the positions after applying alignment so children can use them - constraints.x = rect.pos.x; - constraints.y = rect.pos.y; + constraints.x = rect.pos.x + constraints.y = rect.pos.y - is_hovered := inside_rect(rect, zephr_ctx.mouse.pos); - left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT; - left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT; + is_hovered := zephr_ctx.ui.hovered_element == hash + left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT + left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT if (zephr_ctx.ui.active_element == 0) { if (is_hovered && left_mouse_pressed) { - zephr_ctx.ui.active_element = hash; - dragging = true; + zephr_ctx.ui.active_element = hash + dragging = true } } else if (zephr_ctx.ui.active_element == hash) { if (left_mouse_released) { - zephr_ctx.ui.active_element = 0; - dragging = false; + zephr_ctx.ui.active_element = 0 + dragging = false } } if (dragging) { - x := (zephr_ctx.mouse.pos.x - constraints.x) / constraints.width; - y := (zephr_ctx.mouse.pos.y - constraints.y) / constraints.height; + x := (zephr_ctx.mouse.pos.x - constraints.x) / constraints.width + y := (zephr_ctx.mouse.pos.y - constraints.y) / constraints.height - canvas_pos.x = clamp(x, 0, 1); - canvas_pos.y = clamp(y, 0, 1); + canvas_pos.x = clamp(x, 0, 1) + canvas_pos.y = clamp(y, 0, 1) } - model := identity(); - translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}); + model := identity() + translate(&model, Vec3{rect.pos.x, rect.pos.y, 0}) - set_mat4f(color_chooser_shader, "model", model); + set_mat4f(color_chooser_shader, "model", model) - gl.BindVertexArray(ui_vao); + gl.BindVertexArray(ui_vao) vertices := [6][4]f32 { // bottom left tri @@ -744,88 +772,109 @@ draw_color_picker_canvas :: proc(constraints: ^UiConstraints, slider_percentage: {0, 0 + rect.size.y, 0.0, 1.0}, {0 + rect.size.x, 0, 1.0, 0.0}, {0 + rect.size.x, 0 + rect.size.y, 1.0, 1.0}, - }; + } - gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo); + gl.BindBuffer(gl.ARRAY_BUFFER, ui_vbo) gl.BufferSubData(gl.ARRAY_BUFFER, 0, size_of(vertices), raw_data(&vertices)) - gl.BindBuffer(gl.ARRAY_BUFFER, 0); + gl.BindBuffer(gl.ARRAY_BUFFER, 0) - gl.DrawArrays(gl.TRIANGLES, 0, 6); + gl.DrawArrays(gl.TRIANGLES, 0, 6) - gl.BindVertexArray(0); + gl.BindVertexArray(0) - selected_color := hsv2rgb(slider_percentage * 360, canvas_pos.x, 1.0 - canvas_pos.y); + selected_color := hsv2rgb(slider_percentage * 360, canvas_pos.x, 1.0 - canvas_pos.y) // draw the selection triangle_con := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&triangle_con, constraints); - set_x_constraint(&triangle_con, canvas_pos.x, .RELATIVE); - set_y_constraint(&triangle_con, canvas_pos.y, .RELATIVE); - set_width_constraint(&triangle_con, 14, .RELATIVE_PIXELS); - set_height_constraint(&triangle_con, 0.5, .ASPECT_RATIO); - triangle_con.x -= triangle_con.height; - triangle_con.y -= triangle_con.height + triangle_con.height / 2 + triangle_con.width / 2; - set_rotation_constraint(&triangle_con, 0); + set_parent_constraint(&triangle_con, constraints) + set_x_constraint(&triangle_con, canvas_pos.x, .RELATIVE) + set_y_constraint(&triangle_con, canvas_pos.y, .RELATIVE) + set_width_constraint(&triangle_con, 14, .RELATIVE_PIXELS) + set_height_constraint(&triangle_con, 0.5, .ASPECT_RATIO) + triangle_con.x -= triangle_con.height + triangle_con.y -= triangle_con.height + triangle_con.height / 2 + triangle_con.width / 2 + set_rotation_constraint(&triangle_con, 0) tri_style := UiStyle { bg_color = determine_color_contrast(selected_color), border_radius = 0, align = .TOP_LEFT, - }; - draw_triangle(&triangle_con, tri_style); + } + draw_triangle(&triangle_con, tri_style) - triangle_con.x += triangle_con.height * 2; - triangle_con.y += triangle_con.height * 2; - set_rotation_constraint(&triangle_con, 90); - draw_triangle(&triangle_con, tri_style); + triangle_con.x += triangle_con.height * 2 + triangle_con.y += triangle_con.height * 2 + set_rotation_constraint(&triangle_con, 90) + draw_triangle(&triangle_con, tri_style) - triangle_con.x -= triangle_con.height * 2; - triangle_con.y += triangle_con.height * 2; - set_rotation_constraint(&triangle_con, 180); - draw_triangle(&triangle_con, tri_style); + triangle_con.x -= triangle_con.height * 2 + triangle_con.y += triangle_con.height * 2 + set_rotation_constraint(&triangle_con, 180) + draw_triangle(&triangle_con, tri_style) - triangle_con.x -= triangle_con.height * 2; - triangle_con.y -= triangle_con.height * 2; - set_rotation_constraint(&triangle_con, 270); - draw_triangle(&triangle_con, tri_style); + triangle_con.x -= triangle_con.height * 2 + triangle_con.y -= triangle_con.height * 2 + set_rotation_constraint(&triangle_con, 270) + draw_triangle(&triangle_con, tri_style) - return selected_color; + element := UiElement{ + id = hash, + rect = rect + } + + append(&zephr_ctx.ui.elements, element) + + return selected_color } -// TODO: consume mouse events on the popup @private draw_color_picker_popup :: proc(picker_button_con: ^UiConstraints) { + id := 023467 + id_bytes := mem.byte_slice(&id, size_of(id)) + hash := fnv_hash32(id_bytes, size_of(id), zephr_ctx.ui.popup_parent_hash) popup_con := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&popup_con, picker_button_con); - set_x_constraint(&popup_con, 0, .FIXED); - set_y_constraint(&popup_con, 1.4, .RELATIVE); - set_width_constraint(&popup_con, 450, .RELATIVE_PIXELS); - set_height_constraint(&popup_con, 480, .RELATIVE_PIXELS); + set_parent_constraint(&popup_con, picker_button_con) + set_x_constraint(&popup_con, 0, .FIXED) + set_y_constraint(&popup_con, 1.4, .RELATIVE) + set_width_constraint(&popup_con, 450, .RELATIVE_PIXELS) + set_height_constraint(&popup_con, 480, .RELATIVE_PIXELS) popup_style := UiStyle { bg_color = mult_color(COLOR_WHITE, 0.94), border_radius = popup_con.width * 0.02, align = .TOP_LEFT, - }; - draw_quad(&popup_con, popup_style); + } + draw_quad(&popup_con, popup_style) + + rect := Rect { + {popup_con.x, popup_con.y}, + {popup_con.width, popup_con.height}, + } + + element := UiElement{ + id = hash, + rect = rect + } + + append(&zephr_ctx.ui.elements, element) color_slider_con := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&color_slider_con, &popup_con); - set_x_constraint(&color_slider_con, -0.04, .RELATIVE); - set_y_constraint(&color_slider_con, 0.05, .RELATIVE); - set_height_constraint(&color_slider_con, 400 * 0.9, .RELATIVE_PIXELS); - set_width_constraint(&color_slider_con, 0.1, .ASPECT_RATIO); - slider_selection := draw_color_picker_slider(&color_slider_con, .TOP_RIGHT); + set_parent_constraint(&color_slider_con, &popup_con) + set_x_constraint(&color_slider_con, -0.04, .RELATIVE) + set_y_constraint(&color_slider_con, 0.05, .RELATIVE) + set_height_constraint(&color_slider_con, 400 * 0.9, .RELATIVE_PIXELS) + set_width_constraint(&color_slider_con, 0.1, .ASPECT_RATIO) + slider_selection := draw_color_picker_slider(&color_slider_con, .TOP_RIGHT) - set_x_constraint(&color_slider_con, 0.04, .RELATIVE); - set_y_constraint(&color_slider_con, 0.05, .RELATIVE); - set_width_constraint(&color_slider_con, popup_con.width * 0.8, .FIXED); - selected_color := draw_color_picker_canvas(&color_slider_con, slider_selection, .TOP_LEFT); + set_x_constraint(&color_slider_con, 0.04, .RELATIVE) + set_y_constraint(&color_slider_con, 0.05, .RELATIVE) + set_width_constraint(&color_slider_con, popup_con.width * 0.8, .FIXED) + selected_color := draw_color_picker_canvas(&color_slider_con, slider_selection, .TOP_LEFT) button_con := DEFAULT_UI_CONSTRAINTS - set_parent_constraint(&button_con, &popup_con); - set_x_constraint(&button_con, 0.04, .RELATIVE); - set_y_constraint(&button_con, -0.05, .RELATIVE); - set_width_constraint(&button_con, 0.43, .RELATIVE); - set_height_constraint(&button_con, 0.3, .ASPECT_RATIO); + set_parent_constraint(&button_con, &popup_con) + set_x_constraint(&button_con, 0.04, .RELATIVE) + set_y_constraint(&button_con, -0.05, .RELATIVE) + set_width_constraint(&button_con, 0.43, .RELATIVE) + set_height_constraint(&button_con, 0.3, .ASPECT_RATIO) button_style := UiStyle { bg_color = zephr_ctx.ui.popup_revert_color^, fg_color = determine_color_contrast(zephr_ctx.ui.popup_revert_color^), @@ -833,27 +882,22 @@ draw_color_picker_popup :: proc(picker_button_con: ^UiConstraints) { align = .BOTTOM_LEFT, } if (draw_button(&button_con, "Revert", button_style, .ACTIVE)) { - zephr_ctx.ui.popup_open = false; - zephr_ctx.ui.popup_parent_hash = 0; + zephr_ctx.ui.popup_open = false + zephr_ctx.ui.popup_parent_hash = 0 } - button_style.align = .BOTTOM_RIGHT; - button_style.fg_color = determine_color_contrast(selected_color); - set_x_constraint(&button_con, -0.04, .RELATIVE); - set_y_constraint(&button_con, -0.05, .RELATIVE); - button_style.bg_color = selected_color; + button_style.align = .BOTTOM_RIGHT + button_style.fg_color = determine_color_contrast(selected_color) + set_x_constraint(&button_con, -0.04, .RELATIVE) + set_y_constraint(&button_con, -0.05, .RELATIVE) + button_style.bg_color = selected_color if (draw_button(&button_con, "Apply", button_style, .ACTIVE)) { - zephr_ctx.ui.popup_revert_color^ = selected_color; - zephr_ctx.ui.popup_open = false; - zephr_ctx.ui.popup_parent_hash = 0; + zephr_ctx.ui.popup_revert_color^ = selected_color + zephr_ctx.ui.popup_open = false + zephr_ctx.ui.popup_parent_hash = 0 } - rect := Rect { - {popup_con.x, popup_con.y}, - {popup_con.width, popup_con.height}, - }; - - zephr_ctx.ui.popup_rect = rect; + zephr_ctx.ui.popup_rect = rect } draw_color_picker :: proc(constraints: ^UiConstraints, color: ^Color, align: Alignment, state: ButtonState, id: u32 = 0, caller := #caller_location) { @@ -861,78 +905,79 @@ draw_color_picker :: proc(constraints: ^UiConstraints, color: ^Color, align: Ali line_bytes := mem.byte_slice(&line, size_of(line)) id := id id_bytes := mem.byte_slice(&id, size_of(id)) - hash := fnv_hash32(id_bytes, size_of(id), FNV_HASH32_INIT); - hash = fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), hash); - hash = fnv_hash32(line_bytes, size_of(line), hash); + hash := fnv_hash32(id_bytes, size_of(id), FNV_HASH32_INIT) + hash = fnv_hash32(transmute([]byte)caller.file_path, cast(u32)len(caller.file_path), hash) + hash = fnv_hash32(line_bytes, size_of(line), hash) rect: Rect = --- - display_color := color^; + display_color := color^ - con := constraints^; + con := constraints^ - apply_constraints(&con, &rect.pos, &rect.size); - apply_alignment(align, &con, rect.size, &rect.pos); + apply_constraints(&con, &rect.pos, &rect.size) + apply_alignment(align, &con, rect.size, &rect.pos) is_held := zephr_ctx.ui.active_element == hash - is_hovered := inside_rect(rect, zephr_ctx.mouse.pos); - left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT; - left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT; - clicked := false; + is_hovered := zephr_ctx.ui.hovered_element == hash + left_mouse_pressed := zephr_ctx.mouse.pressed && zephr_ctx.mouse.button == .BUTTON_LEFT + left_mouse_released := zephr_ctx.mouse.released && zephr_ctx.mouse.button == .BUTTON_LEFT + clicked := false if (zephr_ctx.ui.active_element == 0) { if (is_hovered && left_mouse_pressed) { - zephr_ctx.ui.active_element = hash; + zephr_ctx.ui.active_element = hash } } else if (zephr_ctx.ui.active_element == hash) { if (left_mouse_released) { - zephr_ctx.ui.active_element = 0; + zephr_ctx.ui.active_element = 0 if (is_hovered) { - clicked = true; + clicked = true } } } if (is_hovered && state == .ACTIVE) { - set_cursor(.HAND); - con.scale.y *= 1.08; - con.scale.x *= 1.08; + set_cursor(.HAND) + con.scale.y *= 1.08 + con.scale.x *= 1.08 if (is_held) { - display_color = mult_color(display_color, 0.8); + display_color = mult_color(display_color, 0.8) } else { - display_color = mult_color(display_color, 0.9); + display_color = mult_color(display_color, 0.9) } } else if (state == .DISABLED) { if (is_hovered) { - set_cursor(.DISABLED); + set_cursor(.DISABLED) } - display_color.a = 100; + display_color.a = 100 } style := UiStyle { bg_color = display_color, border_radius = con.width * 0.08, align = align, - }; - draw_quad(&con, style); + } + draw_quad(&con, style) if (clicked && state == .ACTIVE) { - zephr_ctx.ui.popup_open = true; - zephr_ctx.ui.popup_parent_constraints = con; - zephr_ctx.ui.popup_revert_color = color; - zephr_ctx.ui.popup_parent_hash = hash; + zephr_ctx.ui.popup_open = true + zephr_ctx.ui.popup_parent_constraints = con + zephr_ctx.ui.popup_revert_color = color + zephr_ctx.ui.popup_parent_hash = hash } if (zephr_ctx.ui.popup_parent_hash == hash) { - zephr_ctx.ui.popup_open = true; - zephr_ctx.ui.popup_parent_constraints = con; + zephr_ctx.ui.popup_open = true + zephr_ctx.ui.popup_parent_constraints = con } - /* if (is_hovered && state == BUTTON_STATE_ACTIVE && zephr_ctx->mouse.released && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { */ - /* return true; */ - /* } */ + element := UiElement{ + rect = rect, + id = hash + } - /* return false; */ + append(&zephr_ctx.ui.elements, element) } diff --git a/zephr/zephr.odin b/zephr/zephr.odin index e4e3dac..d99950c 100644 --- a/zephr/zephr.odin +++ b/zephr/zephr.odin @@ -379,6 +379,8 @@ XA_CARDINAL :: x11.Atom(6) FNV_HASH32_INIT :: 0x811c9dc5 @private FNV_HASH32_PRIME :: 0x01000193 +@private +INIT_UI_STACK_SIZE :: 256 COLOR_BLACK :: Color{0, 0, 0, 255} COLOR_WHITE :: Color{255, 255, 255, 255} @@ -651,64 +653,64 @@ x11_go_fullscreen :: proc() { // remove the resizing constraint before going fullscreen so WMs such as gnome // can add the _NET_WM_ACTION_FULLSCREEN action to the _NET_WM_ALLOWED_ACTIONS atom // and properly fullscreen the window - size_hints := x11.XAllocSizeHints(); + size_hints := x11.XAllocSizeHints() - zephr_ctx.window.pre_fullscreen_size.x = zephr_ctx.window.size.x; - zephr_ctx.window.pre_fullscreen_size.y = zephr_ctx.window.size.y; + zephr_ctx.window.pre_fullscreen_size.x = zephr_ctx.window.size.x + zephr_ctx.window.pre_fullscreen_size.y = zephr_ctx.window.size.y if (size_hints != nil) { - size_hints.flags = {.PPosition, .PSize}; - size_hints.width = cast(i32)zephr_ctx.window.size.x; - size_hints.height = cast(i32)zephr_ctx.window.size.y; - x11.XSetWMNormalHints(x11_display, x11_window, size_hints); - x11.XFree(size_hints); + size_hints.flags = {.PPosition, .PSize} + size_hints.width = cast(i32)zephr_ctx.window.size.x + size_hints.height = cast(i32)zephr_ctx.window.size.y + x11.XSetWMNormalHints(x11_display, x11_window, size_hints) + x11.XFree(size_hints) } - xev: x11.XEvent; - wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false); - fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false); - xev.type = .ClientMessage; - xev.xclient.window = x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD - xev.xclient.data.l[1] = cast(int)fullscreen; - xev.xclient.data.l[2] = 0; + xev: x11.XEvent + wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false) + fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false) + xev.type = .ClientMessage + xev.xclient.window = x11_window + xev.xclient.message_type = wm_state + xev.xclient.format = 32 + xev.xclient.data.l[0] = 1 // _NET_WM_STATE_ADD + xev.xclient.data.l[1] = cast(int)fullscreen + xev.xclient.data.l[2] = 0 x11.XSendEvent(x11_display, x11.XDefaultRootWindow(x11_display), false, - {.SubstructureNotify, .SubstructureRedirect}, &xev); + {.SubstructureNotify, .SubstructureRedirect}, &xev) } @private x11_return_fullscreen :: proc() { - xev: x11.XEvent; - wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false); - fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false); - xev.type = .ClientMessage; - xev.xclient.window = x11_window; - xev.xclient.message_type = wm_state; - xev.xclient.format = 32; - xev.xclient.data.l[0] = 0; // _NET_WM_STATE_REMOVE - xev.xclient.data.l[1] = cast(int)fullscreen; - xev.xclient.data.l[2] = 0; + xev: x11.XEvent + wm_state := x11.XInternAtom(x11_display, "_NET_WM_STATE", false) + fullscreen := x11.XInternAtom(x11_display, "_NET_WM_STATE_FULLSCREEN", false) + xev.type = .ClientMessage + xev.xclient.window = x11_window + xev.xclient.message_type = wm_state + xev.xclient.format = 32 + xev.xclient.data.l[0] = 0 // _NET_WM_STATE_REMOVE + xev.xclient.data.l[1] = cast(int)fullscreen + xev.xclient.data.l[2] = 0 x11.XSendEvent(x11_display, x11.XDefaultRootWindow(x11_display), false, - {.SubstructureNotify, .SubstructureRedirect}, &xev); + {.SubstructureNotify, .SubstructureRedirect}, &xev) // restore the resizing constraint as well as the pre-fullscreen window size // when returning from fullscreen - size_hints := x11.XAllocSizeHints(); + size_hints := x11.XAllocSizeHints() if (size_hints != nil) { - size_hints.flags = {.PPosition, .PSize}; - size_hints.width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x; - size_hints.height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y; + size_hints.flags = {.PPosition, .PSize} + size_hints.width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x + size_hints.height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y if (zephr_ctx.window.non_resizable) { - size_hints.flags |= {.PMinSize, .PMaxSize}; - size_hints.min_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x; - size_hints.min_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y; - size_hints.max_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x; - size_hints.max_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y; + size_hints.flags |= {.PMinSize, .PMaxSize} + size_hints.min_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x + size_hints.min_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y + size_hints.max_width = cast(i32)zephr_ctx.window.pre_fullscreen_size.x + size_hints.max_height = cast(i32)zephr_ctx.window.pre_fullscreen_size.y } - x11.XSetWMNormalHints(x11_display, x11_window, size_hints); - x11.XFree(size_hints); + x11.XSetWMNormalHints(x11_display, x11_window, size_hints) + x11.XFree(size_hints) } } @@ -727,30 +729,30 @@ x11_toggle_fullscreen :: proc(fullscreen: bool) { @private x11_assign_window_icon :: proc(icon_path: cstring) { icon_width, icon_height: i32 - icon_data := image.load(icon_path, &icon_width, &icon_height, nil, 4); + icon_data := image.load(icon_path, &icon_width, &icon_height, nil, 4) defer image.image_free(icon_data) assert(icon_data != nil, "Failed to load icon image") - target_size := 2 + icon_width * icon_height; + target_size := 2 + icon_width * icon_height data := make([]u64, target_size * size_of(u64)) // first two elements are width and height - data[0] = cast(u64)icon_width; - data[1] = cast(u64)icon_height; + data[0] = cast(u64)icon_width + data[1] = cast(u64)icon_height for i in 0..<(icon_width*icon_height) { data[i + 2] = cast(u64)((icon_data[i * 4] << 16) | (icon_data[i * 4 + 1] << 8) | (icon_data[i * 4 + 2] << 0) | (icon_data[i * 4 + 3] << 24)) } - net_wm_icon := x11.XInternAtom(x11_display, "_NET_WM_ICON", false); + net_wm_icon := x11.XInternAtom(x11_display, "_NET_WM_ICON", false) - x11.XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, raw_data(data), target_size); + x11.XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, raw_data(data), target_size) } @private x11_get_screen_size :: proc() -> Vec2 { - screen := x11.XDefaultScreenOfDisplay(x11_display); + screen := x11.XDefaultScreenOfDisplay(x11_display) return Vec2{cast(f32)screen.width, cast(f32)screen.height} } @@ -792,7 +794,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c {.CWColormap, .CWEventMask}, &attributes) if (icon_path != "") { - x11_assign_window_icon(icon_path); + x11_assign_window_icon(icon_path) } // Hints to the WM that the window is a normal window @@ -803,7 +805,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c wm_delete_window := x11.XInternAtom(x11_display, "WM_DELETE_WINDOW", false) x11.XSetWMProtocols(x11_display, x11_window, &wm_delete_window, 1) - zephr_ctx.window_delete_atom = wm_delete_window; + zephr_ctx.window_delete_atom = wm_delete_window // set window name { @@ -840,11 +842,11 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c size_hints.width = cast(i32)window_size.x size_hints.height = cast(i32)window_size.y if (window_non_resizable) { - size_hints.flags |= {.PMinSize, .PMaxSize}; - size_hints.min_width = cast(i32)window_size.x; - size_hints.min_height = cast(i32)window_size.y; - size_hints.max_width = cast(i32)window_size.x; - size_hints.max_height = cast(i32)window_size.y; + size_hints.flags |= {.PMinSize, .PMaxSize} + size_hints.min_width = cast(i32)window_size.x + size_hints.min_height = cast(i32)window_size.y + size_hints.max_width = cast(i32)window_size.x + size_hints.max_height = cast(i32)window_size.y } x11.XSetWMNormalHints(x11_display, x11_window, size_hints) x11.XFree(size_hints) @@ -877,16 +879,16 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c glx.CONTEXT_MINOR_VERSION_ARB, 3, glx.CONTEXT_PROFILE_MASK_ARB, glx.CONTEXT_CORE_PROFILE_BIT_ARB, x11.None, - }; + } - glx_context = glx.CreateContextAttribsARB(x11_display, fbc[0], nil, true, raw_data(context_attributes)); + glx_context = glx.CreateContextAttribsARB(x11_display, fbc[0], nil, true, raw_data(context_attributes)) if glx_context == nil { fmt.eprintf("Failed to create GLX context\n") return } - glx.MakeCurrent(x11_display, x11_window, glx_context); + glx.MakeCurrent(x11_display, x11_window, glx_context) // TODO: find a way to properly get the GL version gl_major: i32 = --- @@ -896,7 +898,7 @@ x11_create_window :: proc(window_title: cstring, window_size: Vec2, icon_path: c fmt.printf("GL version: %d.%d\n", gl_major, gl_minor) - glx.SwapIntervalEXT(x11_display, x11_window, 1); + glx.SwapIntervalEXT(x11_display, x11_window, 1) // we enable blending for text gl.Enable(gl.BLEND) gl.Enable(gl.MULTISAMPLE) @@ -913,12 +915,12 @@ x11_init :: proc(window_title: cstring, window_size: Vec2, icon_path: cstring, w @private x11_close :: proc() { - glx.MakeCurrent(x11_display, 0, nil); - glx.DestroyContext(x11_display, glx_context); + glx.MakeCurrent(x11_display, 0, nil) + glx.DestroyContext(x11_display, glx_context) - x11.XDestroyWindow(x11_display, x11_window); - x11.XFreeColormap(x11_display, x11_colormap); - x11.XCloseDisplay(x11_display); + x11.XDestroyWindow(x11_display, x11_window) + x11.XFreeColormap(x11_display, x11_colormap) + x11.XCloseDisplay(x11_display) } @@ -938,22 +940,24 @@ init :: proc(font_path: cstring, icon_path: cstring, window_title: cstring, wind ui_init(font_path) + zephr_ctx.ui.elements = make([dynamic]UiElement, INIT_UI_STACK_SIZE) + zephr_ctx.mouse.pos = Vec2{-1, -1} zephr_ctx.window.size = window_size zephr_ctx.window.non_resizable = window_non_resizable zephr_ctx.projection = orthographic_projection_2d(0, window_size.x, window_size.y, 0) - zephr_ctx.cursors[.ARROW] = x11.XCreateFontCursor(x11_display, .XC_left_ptr); - zephr_ctx.cursors[.IBEAM] = x11.XCreateFontCursor(x11_display, .XC_xterm); - zephr_ctx.cursors[.CROSSHAIR] = x11.XCreateFontCursor(x11_display, .XC_crosshair); - zephr_ctx.cursors[.HAND] = x11.XCreateFontCursor(x11_display, .XC_hand1); - zephr_ctx.cursors[.HRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_h_double_arrow); - zephr_ctx.cursors[.VRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_v_double_arrow); + zephr_ctx.cursors[.ARROW] = x11.XCreateFontCursor(x11_display, .XC_left_ptr) + zephr_ctx.cursors[.IBEAM] = x11.XCreateFontCursor(x11_display, .XC_xterm) + zephr_ctx.cursors[.CROSSHAIR] = x11.XCreateFontCursor(x11_display, .XC_crosshair) + zephr_ctx.cursors[.HAND] = x11.XCreateFontCursor(x11_display, .XC_hand1) + zephr_ctx.cursors[.HRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_h_double_arrow) + zephr_ctx.cursors[.VRESIZE] = x11.XCreateFontCursor(x11_display, .XC_sb_v_double_arrow) // non-standard cursors - zephr_ctx.cursors[.DISABLED] = xcursor.LibraryLoadCursor(x11_display, "crossed_circle"); + zephr_ctx.cursors[.DISABLED] = xcursor.LibraryLoadCursor(x11_display, "crossed_circle") - zephr_ctx.screen_size = x11_get_screen_size(); - start_internal_timer(); + zephr_ctx.screen_size = x11_get_screen_size() + start_internal_timer() } deinit :: proc() { @@ -965,24 +969,42 @@ should_quit :: proc() -> bool { gl.ClearColor(0, 0, 0, 1) gl.Clear(gl.COLOR_BUFFER_BIT) - zephr_ctx.cursor = .ARROW; - zephr_ctx.mouse.released = false; - zephr_ctx.mouse.pressed = false; + zephr_ctx.cursor = .ARROW + zephr_ctx.mouse.released = false + zephr_ctx.mouse.pressed = false //audio_update(); - return zephr_ctx.should_quit; + return zephr_ctx.should_quit } quit :: proc() { - zephr_ctx.should_quit = true; + zephr_ctx.should_quit = true +} + +@private +consume_mouse_events :: proc() -> bool { + defer clear(&zephr_ctx.ui.elements) + + #reverse for e in zephr_ctx.ui.elements { + if (inside_rect(e.rect, zephr_ctx.mouse.pos)) { + zephr_ctx.ui.hovered_element = e.id + return false + } + } + + return true } swap_buffers :: proc() { if (zephr_ctx.ui.popup_open) { - draw_color_picker_popup(&zephr_ctx.ui.popup_parent_constraints); + draw_color_picker_popup(&zephr_ctx.ui.popup_parent_constraints) + } + zephr_ctx.ui.popup_open = false + + if consume_mouse_events() { + zephr_ctx.ui.hovered_element = 0 } - zephr_ctx.ui.popup_open = false; glx.SwapBuffers(x11_display, x11_window) x11.XDefineCursor(x11_display, x11_window, zephr_ctx.cursors[zephr_ctx.cursor]) @@ -1010,77 +1032,77 @@ iter_events :: proc(e_out: ^Event) -> bool { } } else if xev.type == .DestroyNotify { // window destroy event - e_out.type = .WINDOW_CLOSED; + e_out.type = .WINDOW_CLOSED - return true; + return true } else if xev.type == .ClientMessage { // window close event if (cast(x11.Atom)xev.xclient.data.l[0] == zephr_ctx.window_delete_atom) { - e_out.type = .WINDOW_CLOSED; + e_out.type = .WINDOW_CLOSED - return true; + return true } } else if xev.type == .KeyPress { - xke := xev.xkey; + xke := xev.xkey - evdev_keycode := xke.keycode - 8; - scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode]; + evdev_keycode := xke.keycode - 8 + scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode] - e_out.type = .KEY_PRESSED; - e_out.key.scancode = scancode; + e_out.type = .KEY_PRESSED + e_out.key.scancode = scancode //e_out.key.code = keycode; - e_out.key.mods = x11_mods_to_zephr_mods(scancode, true); + e_out.key.mods = x11_mods_to_zephr_mods(scancode, true) return true } else if xev.type == .KeyRelease { - xke := xev.xkey; + xke := xev.xkey - evdev_keycode := xke.keycode - 8; - scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode]; + evdev_keycode := xke.keycode - 8 + scancode := evdev_scancode_to_zephr_scancode_map[evdev_keycode] - e_out.type = .KEY_RELEASED; + e_out.type = .KEY_RELEASED e_out.key.scancode = scancode //e_out.key.code = keycode; - e_out.key.mods = x11_mods_to_zephr_mods(scancode, false); + e_out.key.mods = x11_mods_to_zephr_mods(scancode, false) return true } else if xev.type == .ButtonPress { - e_out.type = .MOUSE_BUTTON_PRESSED; - e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y}; - zephr_ctx.mouse.pressed = true; + e_out.type = .MOUSE_BUTTON_PRESSED + e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y} + zephr_ctx.mouse.pressed = true switch (xev.xbutton.button) { case .Button1: - e_out.mouse.button = .BUTTON_LEFT; - zephr_ctx.mouse.button = .BUTTON_LEFT; + e_out.mouse.button = .BUTTON_LEFT + zephr_ctx.mouse.button = .BUTTON_LEFT case .Button2: - e_out.mouse.button = .BUTTON_MIDDLE; - zephr_ctx.mouse.button = .BUTTON_MIDDLE; + e_out.mouse.button = .BUTTON_MIDDLE + zephr_ctx.mouse.button = .BUTTON_MIDDLE case .Button3: - e_out.mouse.button = .BUTTON_RIGHT; - zephr_ctx.mouse.button = .BUTTON_RIGHT; + e_out.mouse.button = .BUTTON_RIGHT + zephr_ctx.mouse.button = .BUTTON_RIGHT case .Button4: - e_out.type = .MOUSE_SCROLL; - e_out.mouse.scroll_direction = .UP; + e_out.type = .MOUSE_SCROLL + e_out.mouse.scroll_direction = .UP case .Button5: - e_out.type = .MOUSE_SCROLL; - e_out.mouse.scroll_direction = .DOWN; + e_out.type = .MOUSE_SCROLL + e_out.mouse.scroll_direction = .DOWN case cast(x11.MouseButton)8: // Back - e_out.mouse.button = .BUTTON_BACK; - zephr_ctx.mouse.button = .BUTTON_BACK; + e_out.mouse.button = .BUTTON_BACK + zephr_ctx.mouse.button = .BUTTON_BACK case cast(x11.MouseButton)9: // Forward - e_out.mouse.button = .BUTTON_FORWARD; - zephr_ctx.mouse.button = .BUTTON_FORWARD; + e_out.mouse.button = .BUTTON_FORWARD + zephr_ctx.mouse.button = .BUTTON_FORWARD case: - fmt.printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button); + fmt.printf("[WARN] Unknown mouse button pressed: %d\n", xev.xbutton.button) } return true } else if xev.type == .ButtonRelease { - e_out.type = .MOUSE_BUTTON_RELEASED; - e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y}; - zephr_ctx.mouse.released = true; - zephr_ctx.mouse.pressed = false; + e_out.type = .MOUSE_BUTTON_RELEASED + e_out.mouse.pos = Vec2{cast(f32)xev.xbutton.x, cast(f32)xev.xbutton.y} + zephr_ctx.mouse.released = true + zephr_ctx.mouse.pressed = false switch (xev.xbutton.button) { case .Button1: @@ -1090,34 +1112,34 @@ iter_events :: proc(e_out: ^Event) -> bool { case .Button3: e_out.mouse.button = .BUTTON_RIGHT case .Button4: - e_out.type = .MOUSE_SCROLL; - e_out.mouse.scroll_direction = .UP; + e_out.type = .MOUSE_SCROLL + e_out.mouse.scroll_direction = .UP case .Button5: - e_out.type = .MOUSE_SCROLL; - e_out.mouse.scroll_direction = .DOWN; + e_out.type = .MOUSE_SCROLL + e_out.mouse.scroll_direction = .DOWN case cast(x11.MouseButton)8: // Back - e_out.mouse.button = .BUTTON_BACK; - zephr_ctx.mouse.button = .BUTTON_BACK; + e_out.mouse.button = .BUTTON_BACK + zephr_ctx.mouse.button = .BUTTON_BACK case cast(x11.MouseButton)9: // Forward - e_out.mouse.button = .BUTTON_FORWARD; - zephr_ctx.mouse.button = .BUTTON_FORWARD; + e_out.mouse.button = .BUTTON_FORWARD + zephr_ctx.mouse.button = .BUTTON_FORWARD } - return true; + return true } else if xev.type == .MappingNotify { // input device mapping changed if (xev.xmapping.request != .MappingKeyboard) { - break; + break } - x11.XRefreshKeyboardMapping(&xev.xmapping); + x11.XRefreshKeyboardMapping(&xev.xmapping) /* x11_keyboard_map_update(); */ - break; + break } else if xev.type == .MotionNotify { - e_out.type = .MOUSE_MOVED; - e_out.mouse.pos = Vec2{cast(f32)xev.xmotion.x, cast(f32)xev.xmotion.y}; - zephr_ctx.mouse.pos = e_out.mouse.pos; + e_out.type = .MOUSE_MOVED + e_out.mouse.pos = Vec2{cast(f32)xev.xmotion.x, cast(f32)xev.xmotion.y} + zephr_ctx.mouse.pos = e_out.mouse.pos - return true; + return true } } @@ -1125,17 +1147,18 @@ iter_events :: proc(e_out: ^Event) -> bool { } get_window_size :: proc() -> Vec2 { - return zephr_ctx.window.size; + return zephr_ctx.window.size } toggle_fullscreen :: proc() { - x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen); + x11_toggle_fullscreen(zephr_ctx.window.is_fullscreen) - zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen; + zephr_ctx.window.is_fullscreen = !zephr_ctx.window.is_fullscreen } +@private set_cursor :: proc(cursor: Cursor) { - zephr_ctx.cursor = cursor; + zephr_ctx.cursor = cursor } @@ -1153,8 +1176,8 @@ fnv_hash32 :: proc(data: []byte, size: u32, hash: u32) -> u32 { hash := hash for i in 0.. KeyMod { // TODO: figure out a better way of doing this if (is_press) { if (scancode == .LEFT_SHIFT) { - mods |= {.LEFT_SHIFT, .SHIFT}; + mods |= {.LEFT_SHIFT, .SHIFT} } if (scancode == .RIGHT_SHIFT) { - mods |= {.RIGHT_SHIFT, .SHIFT}; + mods |= {.RIGHT_SHIFT, .SHIFT} } if (scancode == .LEFT_CTRL) { - mods |= {.LEFT_CTRL, .CTRL}; + mods |= {.LEFT_CTRL, .CTRL} } if (scancode == .RIGHT_CTRL) { - mods |= {.RIGHT_CTRL, .CTRL}; + mods |= {.RIGHT_CTRL, .CTRL} } if (scancode == .LEFT_ALT) { - mods |= {.LEFT_ALT, .ALT}; + mods |= {.LEFT_ALT, .ALT} } if (scancode == .RIGHT_ALT) { - mods |= {.RIGHT_ALT, .ALT}; + mods |= {.RIGHT_ALT, .ALT} } if (scancode == .LEFT_META) { - mods |= {.LEFT_META, .META}; + mods |= {.LEFT_META, .META} } if (scancode == .RIGHT_META) { - mods |= {.RIGHT_META, .META}; + mods |= {.RIGHT_META, .META} } if (scancode == .CAPS_LOCK) { - mods |= {.CAPS_LOCK}; + mods |= {.CAPS_LOCK} } if (scancode == .NUM_LOCK_OR_CLEAR) { - mods |= {.NUM_LOCK}; + mods |= {.NUM_LOCK} } } else { if (scancode == .LEFT_SHIFT) { - mods &= ~{.LEFT_SHIFT}; + mods &= ~{.LEFT_SHIFT} } if (scancode == .RIGHT_SHIFT) { - mods &= ~{.RIGHT_SHIFT}; + mods &= ~{.RIGHT_SHIFT} } if (!(.RIGHT_SHIFT in mods) && !(.LEFT_SHIFT in mods)) { - mods &= ~{.SHIFT}; + mods &= ~{.SHIFT} } if (scancode == .LEFT_CTRL) { - mods &= ~{.LEFT_CTRL}; + mods &= ~{.LEFT_CTRL} } if (scancode == .RIGHT_CTRL) { - mods &= ~{.RIGHT_CTRL}; + mods &= ~{.RIGHT_CTRL} } if (!(.RIGHT_CTRL in mods) && !(.LEFT_CTRL in mods)) { - mods &= ~{.CTRL}; + mods &= ~{.CTRL} } if (scancode == .LEFT_ALT) { - mods &= ~{.LEFT_ALT}; + mods &= ~{.LEFT_ALT} } if (scancode == .RIGHT_ALT) { - mods &= ~{.RIGHT_ALT}; + mods &= ~{.RIGHT_ALT} } if (!(.RIGHT_ALT in mods) && !(.LEFT_ALT in mods)) { - mods &= ~{.ALT}; + mods &= ~{.ALT} } if (scancode == .LEFT_META) { - mods &= ~{.LEFT_META}; + mods &= ~{.LEFT_META} } if (scancode == .RIGHT_META) { - mods &= ~{.RIGHT_META}; + mods &= ~{.RIGHT_META} } if (!(.RIGHT_META in mods) && !(.LEFT_META in mods)) { - mods &= ~{.META}; + mods &= ~{.META} } if (scancode == .CAPS_LOCK) { - mods &= ~{.CAPS_LOCK}; + mods &= ~{.CAPS_LOCK} } if (scancode == .NUM_LOCK_OR_CLEAR) { - mods &= ~{.NUM_LOCK}; + mods &= ~{.NUM_LOCK} } } zephr_ctx.keyboard.mods = mods - return mods; + return mods } + diff --git a/zephr/zephr_math.odin b/zephr/zephr_math.odin index 93d1a92..1200af0 100644 --- a/zephr/zephr_math.odin +++ b/zephr/zephr_math.odin @@ -12,16 +12,16 @@ Mat4 :: matrix[4, 4]f32 Color :: struct { r, g, b, a: u8 } orthographic_projection_2d :: proc(left, right, bottom, top: f32) -> Mat4 { - result := identity(); + result := identity() - result[0][0] = 2 / (right - left); - result[3][0] = -(right + left) / (right - left); - result[1][1] = 2 / (top - bottom); - result[3][1] = -(top + bottom) / (top - bottom); - result[2][2] = -1; - result[3][3] = 1; + result[0][0] = 2 / (right - left) + result[3][0] = -(right + left) / (right - left) + result[1][1] = 2 / (top - bottom) + result[3][1] = -(top + bottom) / (top - bottom) + result[2][2] = -1 + result[3][3] = 1 - return result; + return result } identity :: proc() -> Mat4 { @@ -83,9 +83,9 @@ mult_color :: proc(color: Color, scalar: f32) -> Color { } hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color { - c := v * s; - x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1))); - m := v - c; + c := v * s + x := (c * (1 - abs(math.mod(h / 60.0, 2) - 1))) + m := v - c r, g, b: f32 @@ -112,30 +112,30 @@ hsv2rgb :: proc(h: f32, s: f32, v: f32) -> Color { } else { r = c g = 0 - b = x; + b = x } - return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255}; + return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255} } determine_color_contrast :: proc(bg: Color) -> Color { - white_contrast := get_contrast(bg, COLOR_WHITE); - black_contrast := get_contrast(bg, COLOR_BLACK); + white_contrast := get_contrast(bg, COLOR_WHITE) + black_contrast := get_contrast(bg, COLOR_BLACK) - return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK; + return white_contrast > black_contrast ? COLOR_WHITE : COLOR_BLACK } @private get_srgb :: proc(component: f32) -> f32 { return (component / 255 <= 0.03928 ? component / 255 / 12.92 - : math.pow((component / 255 + 0.055) / 1.055, 2.4)); + : math.pow((component / 255 + 0.055) / 1.055, 2.4)) } get_luminance :: proc(color: Color) -> f32 { return ((0.2126 * get_srgb(cast(f32)color.r)) + (0.7152 * get_srgb(cast(f32)color.g)) + - (0.0722 * get_srgb(cast(f32)color.b))) / 255; + (0.0722 * get_srgb(cast(f32)color.b))) / 255 } @private @@ -143,5 +143,5 @@ get_contrast :: proc(fg: Color, bg: Color) -> f32 { l1 := get_luminance(fg) l2 := get_luminance(bg) - return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05); + return (max(l1, l2) + 0.05) / (min(l1, l2) + 0.05) }