diff --git a/game.c b/game.c index f9978c2..1212c1a 100644 --- a/game.c +++ b/game.c @@ -64,29 +64,29 @@ void print_new_board() { Color get_tile_color(u16 value) { switch (value) { case 2: - return ColorRGBA(238, 228, 218, 255); + return game.palette.tile_colors[0]; case 4: - return ColorRGBA(237, 224, 200, 255); + return game.palette.tile_colors[1]; case 8: - return ColorRGBA(242, 177, 121, 255); + return game.palette.tile_colors[2]; case 16: - return ColorRGBA(245, 149, 99, 255); + return game.palette.tile_colors[3]; case 32: - return ColorRGBA(246, 124, 95, 255); + return game.palette.tile_colors[4]; case 64: - return ColorRGBA(246, 94, 59, 255); + return game.palette.tile_colors[5]; case 128: - return ColorRGBA(237, 207, 114, 255); + return game.palette.tile_colors[6]; case 256: - return ColorRGBA(237, 204, 97, 255); + return game.palette.tile_colors[7]; case 512: - return ColorRGBA(237, 200, 80, 255); + return game.palette.tile_colors[8]; case 1024: - return ColorRGBA(237, 197, 63, 255); + return game.palette.tile_colors[9]; case 2048: - return ColorRGBA(237, 194, 46, 255); + return game.palette.tile_colors[10]; default: - return ColorRGBA(237, 194, 46, 255); + return game.palette.tile_colors[10]; } } @@ -399,6 +399,7 @@ void reset_game(void) { 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; @@ -421,9 +422,29 @@ void reset_game(void) { void game_attempt_quit(void) { game.help_dialog = false; + game.settings_dialog = false; game.quit_dialog = true; } +void reset_palette(void) { + game.palette = (ColorPalette){ + .bg_color = ColorRGBA(248, 250, 230, 255), + .board_bg_color = ColorRGBA(140, 154, 147, 255), + + .tile_colors[0] = ColorRGBA(238, 228, 218, 255), // 2 + .tile_colors[1] = ColorRGBA(237, 224, 200, 255), // 4 + .tile_colors[2] = ColorRGBA(242, 177, 121, 255), // 8 + .tile_colors[3] = ColorRGBA(245, 149, 99, 255), // 16 + .tile_colors[4] = ColorRGBA(246, 124, 95, 255), // 32 + .tile_colors[5] = ColorRGBA(246, 94, 59, 255), // 64 + .tile_colors[6] = ColorRGBA(237, 207, 114, 255), // 128 + .tile_colors[7] = ColorRGBA(237, 204, 97, 255), // 256 + .tile_colors[8] = ColorRGBA(237, 200, 80, 255), // 512 + .tile_colors[9] = ColorRGBA(237, 197, 63, 255), // 1024 + .tile_colors[10] = ColorRGBA(237, 194, 46, 255), // 2048 + }; +} + void game_init(void) { srand(time(NULL)); @@ -431,6 +452,8 @@ void game_init(void) { game.icon_textures[SETTINGS_ICON] = load_texture("assets/icons/settings.png"); game.icon_textures[CLOSE_ICON] = load_texture("assets/icons/close.png"); + reset_palette(); + for (int i = 0; i < 2; i++) { spawn_random_tile(); } @@ -447,37 +470,39 @@ void game_init(void) { void draw_bg(void) { Size window_size = zephr_get_window_size(); - UIConstraints board_con = {0}; + UIConstraints board_con = default_constraints; set_width_constraint(&board_con, window_size.width, UI_CONSTRAINT_FIXED); set_height_constraint(&board_con, window_size.height, UI_CONSTRAINT_FIXED); // bg - draw_quad(&board_con, ColorRGBA(248, 250, 230, 255), 0, ALIGN_TOP_LEFT); + draw_quad(&board_con, game.palette.bg_color, 0, ALIGN_TOP_LEFT); + + GlyphInstanceList batch; + new_glyph_instance_list(&batch, 32); // 2048 text - UIConstraints text_con = {0}; + UIConstraints text_con = default_constraints; set_y_constraint(&text_con, 0.05f, UI_CONSTRAINT_RELATIVE); set_width_constraint(&text_con, 1.5, UI_CONSTRAINT_RELATIVE_PIXELS); - draw_text("2048", 100, text_con, ColorRGBA(34, 234, 82, 155), ALIGN_TOP_CENTER); + add_text_instance(&batch, "2048", 100, text_con, ColorRGBA(34, 234, 82, 155), ALIGN_TOP_CENTER); // score - char score[16]; - sprintf(score, "%d", game.score); + char score[24]; + sprintf(score, "Score %d", game.score); set_parent_constraint(&text_con, NULL); set_x_constraint(&text_con, 0.05f, UI_CONSTRAINT_RELATIVE); set_y_constraint(&text_con, 0.8f, UI_CONSTRAINT_RELATIVE); - draw_text("Score", 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT); + add_text_instance(&batch, score, 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT); - set_x_constraint(&text_con, 0.15f, UI_CONSTRAINT_RELATIVE); - draw_text(score, 40, text_con, COLOR_BLACK, ALIGN_TOP_LEFT); + draw_text_batch(&batch); } void draw_quit_dialog(void) { - UIConstraints dialog_con = {0}; + UIConstraints dialog_con = default_constraints; set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); @@ -488,45 +513,45 @@ void draw_quit_dialog(void) { set_height_constraint(&dialog_con, 0.2f, UI_CONSTRAINT_RELATIVE); draw_quad(&dialog_con, ColorRGBA(135, 124, 124, 255), 8, ALIGN_CENTER); - UIConstraints text_con = {0}; + UIConstraints text_con = default_constraints; set_parent_constraint(&text_con, &dialog_con); set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED); - set_y_constraint(&text_con, -0.04f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&text_con, -0.25f, UI_CONSTRAINT_RELATIVE); set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS); draw_text("Are you sure you want to quit?", 36.f, text_con, COLOR_WHITE, ALIGN_CENTER); - UIConstraints btn_con = {0}; + UIConstraints btn_con = default_constraints; set_parent_constraint(&btn_con, &dialog_con); - set_width_constraint(&btn_con, 0.12f, UI_CONSTRAINT_RELATIVE); - set_height_constraint(&btn_con, 0.05f, UI_CONSTRAINT_RELATIVE); - set_y_constraint(&btn_con, -0.02f, UI_CONSTRAINT_RELATIVE); - set_x_constraint(&btn_con, -0.07f, UI_CONSTRAINT_RELATIVE); + set_width_constraint(&btn_con, 0.25f, UI_CONSTRAINT_RELATIVE); + set_height_constraint(&btn_con, 0.25f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&btn_con, -0.10f, UI_CONSTRAINT_RELATIVE); + set_x_constraint(&btn_con, -0.20f, UI_CONSTRAINT_RELATIVE); if (draw_button(&btn_con, mult_color(COLOR_WHITE, 0.9f), "Yes", btn_con.height * 0.20f, ALIGN_BOTTOM_CENTER, BUTTON_STATE_ACTIVE)) { zephr_quit(); } - set_y_constraint(&btn_con, -0.02f, UI_CONSTRAINT_RELATIVE); - set_x_constraint(&btn_con, 0.07f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&btn_con, -0.10f, UI_CONSTRAINT_RELATIVE); + set_x_constraint(&btn_con, 0.20f, UI_CONSTRAINT_RELATIVE); if (draw_button(&btn_con, mult_color(COLOR_WHITE, 0.9f), "No", btn_con.height * 0.20f, ALIGN_BOTTOM_CENTER, BUTTON_STATE_ACTIVE)) { game.quit_dialog = false; } } void draw_help_dialog(void) { - UIConstraints dialog_con = {0}; + UIConstraints dialog_con = default_constraints; set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); set_height_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); draw_quad(&dialog_con, ColorRGBA(0, 0, 0, 200), 0, ALIGN_CENTER); - UIConstraints content_card_con = {0}; + UIConstraints content_card_con = default_constraints; set_parent_constraint(&content_card_con, &dialog_con); set_width_constraint(&content_card_con, 0.4f, UI_CONSTRAINT_RELATIVE); - set_height_constraint(&content_card_con, 0.3f, UI_CONSTRAINT_RELATIVE); + set_height_constraint(&content_card_con, 0.33f, UI_CONSTRAINT_RELATIVE); draw_quad(&content_card_con, ColorRGBA(135, 124, 124, 255), 8, ALIGN_CENTER); - UIConstraints text_con = {0}; + UIConstraints text_con = default_constraints; set_parent_constraint(&text_con, &content_card_con); set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS); set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED); @@ -539,7 +564,7 @@ void draw_help_dialog(void) { "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.f, text_con, COLOR_WHITE, ALIGN_CENTER); - UIConstraints btn_con = {0}; + UIConstraints btn_con = default_constraints; set_parent_constraint(&btn_con, &content_card_con); set_width_constraint(&btn_con, 56, UI_CONSTRAINT_RELATIVE_PIXELS); set_height_constraint(&btn_con, 1, UI_CONSTRAINT_ASPECT_RATIO); @@ -550,33 +575,124 @@ void draw_help_dialog(void) { } } +void draw_settings_dialog(void) { + UIConstraints dialog_con = default_constraints; + set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); + set_height_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); + set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); + set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); + draw_quad(&dialog_con, ColorRGBA(0, 0, 0, 200), 0, ALIGN_CENTER); + + UIConstraints content_card_con = default_constraints; + set_parent_constraint(&content_card_con, &dialog_con); + set_width_constraint(&content_card_con, 0.55f, UI_CONSTRAINT_RELATIVE); + set_height_constraint(&content_card_con, 0.6f, UI_CONSTRAINT_RELATIVE); + draw_quad(&content_card_con, ColorRGBA(135, 124, 124, 255), 8, ALIGN_CENTER); + + UIConstraints text_con = default_constraints; + set_parent_constraint(&text_con, &content_card_con); + set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS); + set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED); + set_y_constraint(&text_con, 40, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_text("Settings", 64.f, text_con, COLOR_YELLOW, ALIGN_TOP_CENTER); + + set_x_constraint(&text_con, 30, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&text_con, 150, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_text("Color Palette", 40.f, text_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + const char *bg_color_text = "Background: "; + const u8 font_size = 30.f; + set_x_constraint(&text_con, 30, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&text_con, 220, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&text_con, 0.9f, UI_CONSTRAINT_RELATIVE); + draw_text(bg_color_text, font_size, text_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + Sizef text_size = calculate_text_size(bg_color_text, font_size); + UIConstraints color_picker_con = default_constraints; + set_parent_constraint(&color_picker_con, &content_card_con); + set_x_constraint(&color_picker_con, 30 + text_size.width + 20, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&color_picker_con, 220 - text_size.height / 2.f + 6, UI_CONSTRAINT_RELATIVE_PIXELS); + set_width_constraint(&color_picker_con, 100, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&color_picker_con, 40, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_color_picker(&color_picker_con, &game.palette.bg_color, ALIGN_TOP_LEFT, BUTTON_STATE_ACTIVE); + + set_y_constraint(&text_con, 270, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_text("Board: ", font_size, text_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + set_x_constraint(&color_picker_con, 30 + text_size.width + 20, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&color_picker_con, 270 - text_size.height / 2.f + 6, UI_CONSTRAINT_RELATIVE_PIXELS); + set_width_constraint(&color_picker_con, 100, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&color_picker_con, 40, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_color_picker(&color_picker_con, &game.palette.board_bg_color, ALIGN_TOP_LEFT, BUTTON_STATE_ACTIVE); + + set_y_constraint(&text_con, 320, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_text("Tiles: ", font_size, text_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + const u8 tile_size = 60; + for (u8 i = 0; i < 11; i++) { + set_x_constraint(&color_picker_con, 30 + text_size.width + 20 + (i * (tile_size + 10)), UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&color_picker_con, 320 - text_size.height / 2.f + 6, UI_CONSTRAINT_RELATIVE_PIXELS); + set_width_constraint(&color_picker_con, tile_size, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&color_picker_con, tile_size, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_color_picker(&color_picker_con, &game.palette.tile_colors[i], ALIGN_TOP_LEFT, BUTTON_STATE_ACTIVE); + + u16 tile_val = (u16)pow(2, i + 1); + char text[16]; + sprintf(text, "%d", tile_val); + const f32 font_size = get_tile_font_size(tile_val) * 0.5f; + const Color tile_color = get_tile_color(tile_val); + set_parent_constraint(&text_con, &color_picker_con); + set_x_constraint(&text_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&text_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_text(text, (u8)font_size, text_con, mult_color(tile_color, 0.5f), ALIGN_CENTER); + } + + UIConstraints btn_con = default_constraints; + set_parent_constraint(&btn_con, &content_card_con); + set_x_constraint(&btn_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&btn_con, 400, UI_CONSTRAINT_RELATIVE_PIXELS); + set_width_constraint(&btn_con, 150, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&btn_con, 0.35f, UI_CONSTRAINT_ASPECT_RATIO); + if (draw_button(&btn_con, ColorRGBA(201, 146, 126, 255), "Reset", 8.f, ALIGN_TOP_CENTER, BUTTON_STATE_ACTIVE)) { + reset_palette(); + } + + set_width_constraint(&btn_con, 56, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&btn_con, 1, UI_CONSTRAINT_ASPECT_RATIO); + set_y_constraint(&btn_con, -24, UI_CONSTRAINT_RELATIVE_PIXELS); + set_x_constraint(&btn_con, 24, UI_CONSTRAINT_RELATIVE_PIXELS); + if (draw_icon_button(&btn_con, mult_color(COLOR_WHITE, 0.8f), game.icon_textures[CLOSE_ICON], btn_con.height * 0.20f, ALIGN_TOP_RIGHT, BUTTON_STATE_ACTIVE)) { + game.settings_dialog = false; + } +} + void draw_game_over(void) { - UIConstraints dialog_con = {0}; + UIConstraints dialog_con = default_constraints; set_width_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); set_height_constraint(&dialog_con, 1.f, UI_CONSTRAINT_RELATIVE); set_x_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); set_y_constraint(&dialog_con, 0, UI_CONSTRAINT_FIXED); draw_quad(&dialog_con, ColorRGBA(0, 0, 0, (u8)game.game_over_bg_opacity), 0, ALIGN_CENTER); - UIConstraints content_card_con = {0}; + UIConstraints content_card_con = default_constraints; set_parent_constraint(&content_card_con, &dialog_con); set_width_constraint(&content_card_con, 0.3f, UI_CONSTRAINT_RELATIVE); set_height_constraint(&content_card_con, 0.2f, UI_CONSTRAINT_RELATIVE); draw_quad(&content_card_con, ColorRGBA(135, 124, 124, (u8)game.game_over_opacity), 8, ALIGN_CENTER); - UIConstraints text_con = {0}; + UIConstraints text_con = default_constraints; set_parent_constraint(&text_con, &content_card_con); set_width_constraint(&text_con, 1, UI_CONSTRAINT_RELATIVE_PIXELS); set_x_constraint(&text_con, 0, UI_CONSTRAINT_FIXED); set_y_constraint(&text_con, 40, UI_CONSTRAINT_RELATIVE_PIXELS); draw_text("Game Over!", 64.f, text_con, ColorRGBA(255, 255, 0, (u8)game.game_over_opacity), ALIGN_TOP_CENTER); - UIConstraints btn_con = {0}; + UIConstraints btn_con = default_constraints; const ButtonState btn_state = game.quit_dialog ? BUTTON_STATE_INACTIVE : game.game_over_opacity > 0 ? BUTTON_STATE_ACTIVE : BUTTON_STATE_INACTIVE; set_parent_constraint(&btn_con, &content_card_con); set_x_constraint(&btn_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS); set_y_constraint(&btn_con, -24, UI_CONSTRAINT_RELATIVE_PIXELS); - set_width_constraint(&btn_con, 0.10f, UI_CONSTRAINT_RELATIVE); + set_width_constraint(&btn_con, 0.30f, UI_CONSTRAINT_RELATIVE); set_height_constraint(&btn_con, 0.3f, UI_CONSTRAINT_ASPECT_RATIO); if (draw_button(&btn_con, ColorRGBA(201, 146, 126, (u8)game.game_over_opacity), "Try Again", 8.f, ALIGN_BOTTOM_CENTER, btn_state)) { reset_game(); @@ -584,14 +700,14 @@ void draw_game_over(void) { } void draw_ui(void) { - ButtonState bg_btns_state = game.quit_dialog || game.help_dialog || game.has_lost + ButtonState bg_btns_state = game.quit_dialog || game.help_dialog || game.settings_dialog || game.has_lost ? BUTTON_STATE_INACTIVE : BUTTON_STATE_ACTIVE; const int icon_btn_width = 80; const int icon_btn_offset = 8; const int icon_btns_padding = 10; - UIConstraints btn_con = {0}; + UIConstraints btn_con = default_constraints; set_x_constraint(&btn_con, -icon_btn_offset, UI_CONSTRAINT_RELATIVE_PIXELS); set_y_constraint(&btn_con, icon_btn_offset, UI_CONSTRAINT_RELATIVE_PIXELS); set_width_constraint(&btn_con, icon_btn_width, UI_CONSTRAINT_RELATIVE_PIXELS); @@ -603,7 +719,7 @@ void draw_ui(void) { set_x_constraint(&btn_con, -icon_btn_offset - icon_btn_width - icon_btns_padding, UI_CONSTRAINT_RELATIVE_PIXELS); if (draw_icon_button(&btn_con, ColorRGBA(201, 146, 126, 255), game.icon_textures[SETTINGS_ICON], btn_con.height * 0.15f, ALIGN_TOP_RIGHT, bg_btns_state)) { - printf("TODO: settings\n"); + game.settings_dialog = true; } set_x_constraint(&btn_con, 0, UI_CONSTRAINT_RELATIVE_PIXELS); @@ -614,6 +730,10 @@ void draw_ui(void) { reset_game(); } + if (game.settings_dialog) { + draw_settings_dialog(); + } + if (game.help_dialog) { draw_help_dialog(); } @@ -629,19 +749,19 @@ void draw_ui(void) { void draw_board(void) { // board - UIConstraints board_con = {0}; + UIConstraints board_con = default_constraints; set_y_constraint(&board_con, 0.05f, UI_CONSTRAINT_RELATIVE); set_height_constraint(&board_con, 0.6f, UI_CONSTRAINT_RELATIVE); set_width_constraint(&board_con, 1, UI_CONSTRAINT_ASPECT_RATIO); const float board_border_radius = board_con.width * 0.02f; - draw_quad(&board_con, ColorRGBA(140, 154, 147, 255), board_border_radius, ALIGN_CENTER); + draw_quad(&board_con, game.palette.board_bg_color, board_border_radius, ALIGN_CENTER); // empty tiles const float tile_padding = board_con.width * 0.02f; const float tile_height = board_con.height * 0.225f; const float tile_board_radius = tile_height * 0.15f; - UIConstraints empty_tile_con = {0}; + UIConstraints empty_tile_con = default_constraints; set_parent_constraint(&empty_tile_con, &board_con); set_height_constraint(&empty_tile_con, tile_height, UI_CONSTRAINT_FIXED); set_width_constraint(&empty_tile_con, 1, UI_CONSTRAINT_ASPECT_RATIO); @@ -650,14 +770,14 @@ void draw_board(void) { set_y_constraint(&empty_tile_con, (tile_height + tile_padding) * i + tile_padding, UI_CONSTRAINT_FIXED); for (int j = 0; j < 4; j++) { set_x_constraint(&empty_tile_con, (tile_height + tile_padding) * j + tile_padding, UI_CONSTRAINT_FIXED); - draw_quad(&empty_tile_con, ColorRGBA(120, 133, 126, 255), tile_board_radius, ALIGN_TOP_LEFT); + draw_quad(&empty_tile_con, mult_color(game.palette.board_bg_color, 0.85f), tile_board_radius, ALIGN_TOP_LEFT); } } // filled tiles - UIConstraints text_con = {0}; + UIConstraints text_con = default_constraints; set_width_constraint(&text_con, 1.5, UI_CONSTRAINT_RELATIVE_PIXELS); - UIConstraints tile_con = {0}; + UIConstraints tile_con = default_constraints; set_parent_constraint(&tile_con, &board_con); set_height_constraint(&tile_con, tile_height, UI_CONSTRAINT_FIXED); set_width_constraint(&tile_con, 1, UI_CONSTRAINT_ASPECT_RATIO); @@ -863,6 +983,7 @@ void handle_keyboard_input(ZephrEvent e) { } else if (e.key.code == ZEPHR_KEYCODE_ESCAPE) { game.quit_dialog = false; game.help_dialog = false; + game.settings_dialog = false; } else if (e.key.code == ZEPHR_KEYCODE_F11) { zephr_toggle_fullscreen(); } else if (e.key.code == ZEPHR_KEYCODE_UP) { diff --git a/game.h b/game.h index 0a0fb3a..d941a6d 100644 --- a/game.h +++ b/game.h @@ -18,6 +18,12 @@ typedef enum IconTexture { ICON_TEXTURE_COUNT, } IconTexture; +typedef struct ColorPalette { + Color bg_color; + Color board_bg_color; + Color tile_colors[11]; +} ColorPalette; + typedef struct Tile { u16 value; u16 new_value; @@ -41,9 +47,12 @@ typedef struct Game { Vec2 spawning_tile_coords; u16 spawning_tile_value; - TextureId icon_textures[ICON_TEXTURE_COUNT]; bool quit_dialog; bool help_dialog; + bool settings_dialog; + + TextureId icon_textures[ICON_TEXTURE_COUNT]; + ColorPalette palette; } Game; void draw_board(void); diff --git a/shaders/color_chooser.frag b/shaders/color_chooser.frag new file mode 100644 index 0000000..0d41bb9 --- /dev/null +++ b/shaders/color_chooser.frag @@ -0,0 +1,32 @@ +#version 330 core + +in vec2 v_TexCoords; +out vec4 FragColor; + +uniform bool isSlider; +uniform float sliderPercentage; + +//vec3 rgb2hsv(vec3 c){ +// vec4 K = vec4(0.0,-1.0 / 3.0,2.0 / 3.0,-1.0); +// vec4 p = mix(vec4(c.bg,K.wz),vec4(c.gb,K.xy),step(c.b,c.g)); +// vec4 q = mix(vec4(p.xyw,c.r),vec4(c.r,p.yzx),step(p.x,c.r)); +// float d = q.x - min(q.w,q.y); +// float e = 1.0e-10; +// return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)),d / (q.x + e),q.x); +//} + +vec3 hsv2rgb(vec3 color){ + vec3 rgb = clamp(abs(mod(color.r * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0); + rgb = rgb * rgb * (3.0 - 2.0 * rgb); + return color.b * mix(vec3(1.0), rgb, color.g); +} + +void main() { + if (isSlider) { + vec3 color = hsv2rgb(vec3(v_TexCoords.y, 1.0, 1.0)); + FragColor = vec4(color, 1.0); + } else { + vec3 color = hsv2rgb(vec3(sliderPercentage, v_TexCoords.x, 1.0 - v_TexCoords.y)); + FragColor = vec4(color, 1.0); + } +} diff --git a/text.c b/text.c index f28cc2f..f8aa0c8 100644 --- a/text.c +++ b/text.c @@ -10,7 +10,7 @@ #include "text.h" #include "zephr.h" -#define FONT_PIXEL_SIZE 128 +#define FONT_PIXEL_SIZE 100 #define LINE_HEIGHT 2.0f Shader font_shader; @@ -100,7 +100,7 @@ int init_freetype(const char* font_path) { tex_height = CORE_MAX(tex_height, face->glyph->bitmap.rows); } - char *pixels = (char *)calloc(tex_width * tex_height, 1); + char *pixels = (char *)malloc(tex_width * tex_height); for (u32 i = 32; i < 128; i++) { /* while (glyph_idx) { */ diff --git a/ui.c b/ui.c index 2e4ca2e..f1cb9c1 100644 --- a/ui.c +++ b/ui.c @@ -1,3 +1,4 @@ +#include #include #include @@ -7,9 +8,14 @@ #include "zephr.h" Shader ui_shader; +Shader color_chooser_shader; unsigned int ui_vao; unsigned int ui_vbo; +const UIConstraints default_constraints = { + .scale = {.height = 1, .width = 1}, +}; + int init_ui(const char* font_path) { int res = init_fonts(font_path); if (res == -1) { @@ -21,6 +27,7 @@ int init_ui(const char* font_path) { } ui_shader = create_shader("shaders/ui.vert", "shaders/ui.frag"); + color_chooser_shader = create_shader("shaders/ui.vert", "shaders/color_chooser.frag"); glGenVertexArrays(1, &ui_vao); glGenBuffers(1, &ui_vbo); @@ -49,7 +56,11 @@ void set_x_constraint(UIConstraints *constraints, float value, UIConstraint type constraints->x = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->x = (value * zephr_ctx->window.size.width); + if (constraints->parent) { + constraints->x = (value * constraints->parent->width); + } else { + constraints->x = (value * zephr_ctx->window.size.width); + } break; case UI_CONSTRAINT_RELATIVE_PIXELS: constraints->x = zephr_ctx->window.size.width / (float)zephr_ctx->screen_size.width * value; @@ -69,7 +80,11 @@ void set_y_constraint(UIConstraints *constraints, float value, UIConstraint type constraints->y = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->y = (value * zephr_ctx->window.size.height); + if (constraints->parent) { + constraints->y = (value * constraints->parent->height); + } else { + constraints->y = (value * zephr_ctx->window.size.height); + } break; case UI_CONSTRAINT_RELATIVE_PIXELS: constraints->y = zephr_ctx->window.size.height / (float)zephr_ctx->screen_size.height * value; @@ -87,7 +102,11 @@ void set_width_constraint(UIConstraints *constraints, float value, UIConstraint constraints->width = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->width = (value * zephr_ctx->window.size.width); + if (constraints->parent) { + constraints->width = (value * constraints->parent->width); + } else { + constraints->width = (value * zephr_ctx->window.size.width); + } break; case UI_CONSTRAINT_RELATIVE_PIXELS: constraints->width = zephr_ctx->window.size.width / (float)zephr_ctx->screen_size.width * value; @@ -104,7 +123,11 @@ void set_height_constraint(UIConstraints *constraints, float value, UIConstraint constraints->height = value; break; case UI_CONSTRAINT_RELATIVE: - constraints->height = (value * zephr_ctx->window.size.height); + if (constraints->parent) { + constraints->height = (value * constraints->parent->height); + } else { + constraints->height = (value * zephr_ctx->window.size.height); + } break; case UI_CONSTRAINT_RELATIVE_PIXELS: constraints->height = zephr_ctx->window.size.height / (float)zephr_ctx->screen_size.height * value; @@ -177,8 +200,15 @@ void apply_alignment(Alignment align, UIConstraints *constraints, Vec2f *pos, Si } bool inside_rect(Rect *rect, Vec2 *point) { - if (point->x >= rect->pos.x && point->x <= rect->pos.x + rect->size.width && - point->y >= rect->pos.y && point->y <= rect->pos.y + rect->size.height) { + /* bool outside_popup = true; */ + /* if (rect != &zephr_ctx->ui.popup_rect) { */ + /* outside_popup = !inside_rect(&zephr_ctx->ui.popup_rect, point); */ + /* } else { */ + /* outside_popup = false; */ + /* } */ + + if ((point->x >= rect->pos.x && point->x <= rect->pos.x + rect->size.width && + point->y >= rect->pos.y && point->y <= rect->pos.y + rect->size.height)) { return true; } return false; @@ -204,6 +234,10 @@ void draw_quad(UIConstraints *constraints, const Color color, float border_radiu constraints->y = pos.y; Matrix4x4 model = identity(); + apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f}); + apply_scale(&model, constraints->scale); + apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f}); + apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f}); apply_rotation(&model, to_radians(constraints->rotation)); apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f}); @@ -250,6 +284,7 @@ void draw_triangle(UIConstraints *constraints, const Color color, Alignment alig use_shader(ui_shader); set_vec4f(ui_shader, "aColor", color.r / 255.f, color.g / 255.f, color.b / 255.f, color.a / 255.f); + set_float(ui_shader, "borderRadius", 0.f); set_mat4f(ui_shader, "projection", (float *)zephr_ctx->projection.m); Vec2f pos = { 0.f, 0.f }; @@ -263,6 +298,10 @@ void draw_triangle(UIConstraints *constraints, const Color color, Alignment alig constraints->y = pos.y; Matrix4x4 model = identity(); + apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f}); + apply_scale(&model, constraints->scale); + apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f}); + apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f}); apply_rotation(&model, to_radians(constraints->rotation)); apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f}); @@ -310,6 +349,10 @@ void draw_texture(UIConstraints *constraints, const TextureId texture_id, const constraints->y = pos.y; Matrix4x4 model = identity(); + apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f}); + apply_scale(&model, constraints->scale); + apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f}); + apply_translation(&model, (Vec2f){-size.width / 2.f, -size.height / 2.f}); apply_rotation(&model, to_radians(constraints->rotation)); apply_translation(&model, (Vec2f){size.width / 2.f, size.height / 2.f}); @@ -380,7 +423,7 @@ bool draw_button(UIConstraints *constraints, const Color color, const char *text } if (text) { - UIConstraints text_constraints = {0}; + UIConstraints text_constraints = default_constraints; set_parent_constraint(&text_constraints, constraints); set_x_constraint(&text_constraints, 0, UI_CONSTRAINT_RELATIVE_PIXELS); @@ -434,7 +477,7 @@ bool draw_icon_button(UIConstraints *constraints, Color btn_color, const Texture draw_quad(constraints, btn_color, radius, align); - UIConstraints icon_constraints = {0}; + UIConstraints icon_constraints = default_constraints; set_parent_constraint(&icon_constraints, constraints); set_x_constraint(&icon_constraints, 0, UI_CONSTRAINT_RELATIVE_PIXELS); @@ -450,6 +493,243 @@ bool draw_icon_button(UIConstraints *constraints, Color btn_color, const Texture return false; } +void draw_color_picker_slider(UIConstraints *constraints, Alignment align) { + use_shader(color_chooser_shader); + + set_bool(color_chooser_shader, "isSlider", true); + set_mat4f(color_chooser_shader, "projection", (float *)zephr_ctx->projection.m); + + Vec2f pos = { 0.f, 0.f }; + Sizef size = { 0.f, 0.f }; + + apply_constraints(constraints, &pos, &size); + apply_alignment(align, constraints, &pos, size); + + // set the positions after applying alignment so children can use them + constraints->x = pos.x; + constraints->y = pos.y; + + Matrix4x4 model = identity(); + apply_translation(&model, pos); + + set_mat4f(color_chooser_shader, "model", (float *)model.m); + + glBindVertexArray(ui_vao); + + float vertices[6][4] = { + // bottom left tri + {0, 0 + size.height, 0.0, 1.0}, + {0, 0, 0.0, 0.0}, + {0 + size.width, 0, 1.0, 0.0}, + + // top right tri + {0, 0 + size.height, 0.0, 1.0}, + {0 + size.width, 0, 1.0, 0.0}, + {0 + size.width, 0 + size.height, 1.0, 1.0}, + }; + + glBindBuffer(GL_ARRAY_BUFFER, ui_vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glBindVertexArray(0); + set_bool(color_chooser_shader, "isSlider", false); +} + +void draw_color_picker_canvas(UIConstraints *constraints, f32 slider_percentage, Alignment align) { + 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", (float *)zephr_ctx->projection.m); + + Vec2f pos = { 0.f, 0.f }; + Sizef size = { 0.f, 0.f }; + + apply_constraints(constraints, &pos, &size); + apply_alignment(align, constraints, &pos, size); + + // set the positions after applying alignment so children can use them + constraints->x = pos.x; + constraints->y = pos.y; + + Matrix4x4 model = identity(); + apply_translation(&model, pos); + + set_mat4f(color_chooser_shader, "model", (float *)model.m); + + glBindVertexArray(ui_vao); + + float vertices[6][4] = { + // bottom left tri + {0, 0 + size.height, 0.0, 1.0}, + {0, 0, 0.0, 0.0}, + {0 + size.width, 0, 1.0, 0.0}, + + // top right tri + {0, 0 + size.height, 0.0, 1.0}, + {0 + size.width, 0, 1.0, 0.0}, + {0 + size.width, 0 + size.height, 1.0, 1.0}, + }; + + glBindBuffer(GL_ARRAY_BUFFER, ui_vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); + + glDrawArrays(GL_TRIANGLES, 0, 6); + + glBindVertexArray(0); +} + +// TODO: consume mouse events on the popup +void draw_color_picker_popup(UIConstraints *picker_button_con) { + UIConstraints popup_con = default_constraints; + set_parent_constraint(&popup_con, picker_button_con); + set_x_constraint(&popup_con, 0, UI_CONSTRAINT_FIXED); + set_y_constraint(&popup_con, picker_button_con->height + 20, UI_CONSTRAINT_FIXED); + set_width_constraint(&popup_con, 450, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&popup_con, 480, UI_CONSTRAINT_RELATIVE_PIXELS); + draw_quad(&popup_con, mult_color(COLOR_WHITE, 0.94f), 8, ALIGN_TOP_LEFT); + + UIConstraints color_slider_con = default_constraints; + set_parent_constraint(&color_slider_con, &popup_con); + set_x_constraint(&color_slider_con, -0.04f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&color_slider_con, 0.05f, UI_CONSTRAINT_RELATIVE); + set_height_constraint(&color_slider_con, 400 * 0.9f, UI_CONSTRAINT_RELATIVE_PIXELS); + set_width_constraint(&color_slider_con, 0.1f, UI_CONSTRAINT_ASPECT_RATIO); + draw_color_picker_slider(&color_slider_con, ALIGN_TOP_RIGHT); + + static f32 slider_selection = 0; + static Vec2f canvas_pos = {0}; + + if (inside_rect(&(Rect){{color_slider_con.x, color_slider_con.y}, {color_slider_con.width, color_slider_con.height}}, &zephr_ctx->mouse.pos) && + zephr_ctx->mouse.pressed && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { + slider_selection = (zephr_ctx->mouse.pos.y - color_slider_con.y) / color_slider_con.height; + } + + UIConstraints triangle_con = default_constraints; + set_parent_constraint(&triangle_con, &color_slider_con); + set_x_constraint(&triangle_con, -11, UI_CONSTRAINT_RELATIVE_PIXELS); + set_y_constraint(&triangle_con, slider_selection, UI_CONSTRAINT_RELATIVE); + set_rotation_constraint(&triangle_con, -90); + set_width_constraint(&triangle_con, 14, UI_CONSTRAINT_RELATIVE_PIXELS); + set_height_constraint(&triangle_con, 0.5f, UI_CONSTRAINT_ASPECT_RATIO); + triangle_con.y += -(triangle_con.height / 2.f); + draw_triangle(&triangle_con, COLOR_BLACK, ALIGN_TOP_LEFT); + + set_x_constraint(&triangle_con, 1, UI_CONSTRAINT_RELATIVE); + triangle_con.x -= triangle_con.height / 2; + set_rotation_constraint(&triangle_con, 90); + draw_triangle(&triangle_con, COLOR_BLACK, ALIGN_TOP_LEFT); + + set_x_constraint(&color_slider_con, 0.04f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&color_slider_con, 0.05f, UI_CONSTRAINT_RELATIVE); + set_width_constraint(&color_slider_con, popup_con.width * 0.8f, UI_CONSTRAINT_FIXED); + draw_color_picker_canvas(&color_slider_con, slider_selection, ALIGN_TOP_LEFT); + + if (inside_rect(&(Rect){{color_slider_con.x, color_slider_con.y}, {color_slider_con.width, color_slider_con.height}}, &zephr_ctx->mouse.pos) && + zephr_ctx->mouse.pressed && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { + f32 x = (zephr_ctx->mouse.pos.x - color_slider_con.x) / color_slider_con.width; + f32 y = (zephr_ctx->mouse.pos.y - color_slider_con.y) / color_slider_con.height; + + canvas_pos.x = x; + canvas_pos.y = y; + } + + set_parent_constraint(&triangle_con, &color_slider_con); + set_x_constraint(&triangle_con, canvas_pos.x, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&triangle_con, canvas_pos.y, UI_CONSTRAINT_RELATIVE); + triangle_con.x -= triangle_con.height; + triangle_con.y -= triangle_con.height * 2; + set_rotation_constraint(&triangle_con, 0); + draw_triangle(&triangle_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + triangle_con.x += triangle_con.height * 2; + triangle_con.y += triangle_con.height * 2; + set_rotation_constraint(&triangle_con, 90); + draw_triangle(&triangle_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + triangle_con.x -= triangle_con.height * 2; + triangle_con.y += triangle_con.height * 2; + set_rotation_constraint(&triangle_con, 180); + draw_triangle(&triangle_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + triangle_con.x -= triangle_con.height * 2; + triangle_con.y -= triangle_con.height * 2; + set_rotation_constraint(&triangle_con, 270); + draw_triangle(&triangle_con, COLOR_WHITE, ALIGN_TOP_LEFT); + + UIConstraints button_con = default_constraints; + set_parent_constraint(&button_con, &popup_con); + set_x_constraint(&button_con, 0.04f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&button_con, -0.05f, UI_CONSTRAINT_RELATIVE); + set_width_constraint(&button_con, 0.43f, UI_CONSTRAINT_RELATIVE); + set_height_constraint(&button_con, 0.3f, UI_CONSTRAINT_ASPECT_RATIO); + if (draw_button(&button_con, *zephr_ctx->ui.popup_revert_color, "Revert", 4, ALIGN_BOTTOM_LEFT, BUTTON_STATE_ACTIVE)) { + zephr_ctx->ui.popup_open = false; + } + + Color selected_color = hsv2rgb(slider_selection * 360.f, canvas_pos.x, 1.0f - canvas_pos.y); + + set_x_constraint(&button_con, -0.04f, UI_CONSTRAINT_RELATIVE); + set_y_constraint(&button_con, -0.05f, UI_CONSTRAINT_RELATIVE); + if (draw_button(&button_con, selected_color, "Apply", 4, ALIGN_BOTTOM_RIGHT, BUTTON_STATE_ACTIVE)) { + *zephr_ctx->ui.popup_revert_color = selected_color; + zephr_ctx->ui.popup_open = false; + } + + zephr_ctx->ui.popup_rect = (Rect){{popup_con.x, popup_con.y}, {popup_con.width, popup_con.height}}; +} + +void draw_color_picker(UIConstraints *constraints, Color *color, Alignment align, ButtonState state) { + Rect rect = {0}; + Color display_color = *color; + + UIConstraints con = *constraints; + + apply_constraints(&con, &rect.pos, &rect.size); + apply_alignment(align, &con, &rect.pos, rect.size); + + bool is_hovered = inside_rect(&rect, &zephr_ctx->mouse.pos); + bool left_mouse_pressed = zephr_ctx->mouse.pressed && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT; + + if (is_hovered && state == BUTTON_STATE_ACTIVE) { + zephr_set_cursor(ZEPHR_CURSOR_HAND); + con.scale.height *= 1.08f; + con.scale.width *= 1.08f; + + if (left_mouse_pressed) { + display_color = mult_color(display_color, 0.8f); + } else { + display_color = mult_color(display_color, 0.9f); + } + } else if (state == BUTTON_STATE_DISABLED) { + if (is_hovered) { + zephr_set_cursor(ZEPHR_CURSOR_DISABLED); + } + + display_color.a = 100; + } + + draw_quad(&con, display_color, con.width * 0.08f, align); + + if (is_hovered && state == BUTTON_STATE_ACTIVE && zephr_ctx->mouse.released && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { + zephr_ctx->ui.popup_open = true; + zephr_ctx->ui.popup_parent_constraints = con; + zephr_ctx->ui.popup_revert_color = color; + /* zephr_ctx->ui.popup_rect = (Rect){rect.pos, rect.size}; */ + /* draw_color_picker_overlay(constraints); */ + } + + /* if (is_hovered && state == BUTTON_STATE_ACTIVE && zephr_ctx->mouse.released && zephr_ctx->mouse.button == ZEPHR_MOUSE_BUTTON_LEFT) { */ + /* return true; */ + /* } */ + + /* return false; */ +} + /* enum Element { */ /* Rect, */ diff --git a/ui.h b/ui.h index 31a550f..aef22f2 100644 --- a/ui.h +++ b/ui.h @@ -3,11 +3,6 @@ #include "texture.h" #include "zephr_math.h" -typedef struct Rect { - Vec2f pos; - Sizef size; -} Rect; - typedef enum Alignment { ALIGN_TOP_LEFT, ALIGN_TOP_CENTER, @@ -33,9 +28,24 @@ typedef struct UIConstraints { float width; float height; float rotation; + Sizef scale; struct UIConstraints *parent; } UIConstraints; +extern const UIConstraints default_constraints; + +typedef struct Rect { + Vec2f pos; + Sizef size; +} Rect; + +typedef struct UI { + bool popup_open; + Rect popup_rect; + UIConstraints popup_parent_constraints; + Color *popup_revert_color; +} UI; + typedef enum ButtonState { BUTTON_STATE_ACTIVE, BUTTON_STATE_INACTIVE, @@ -51,8 +61,11 @@ void set_height_constraint(UIConstraints *constraints, float value, UIConstraint void set_rotation_constraint(UIConstraints *constraints, float angle_d); void apply_constraints(UIConstraints *constraints, Vec2f *pos, Sizef *size); void apply_alignment(Alignment align, UIConstraints *constraints, Vec2f *pos, Sizef size); +bool inside_rect(Rect *rect, Vec2 *point); void draw_quad(UIConstraints *constraints, const Color color, float border_radius, Alignment align); void draw_circle(UIConstraints *constraints, const Color color, Alignment align); void draw_triangle(UIConstraints *constraints, const Color color, Alignment align); bool draw_button(UIConstraints *constraints, Color color, const char *text, f32 radius, Alignment align, ButtonState state); bool draw_icon_button(UIConstraints *constraints, Color color, const TextureId icon_tex_id, f32 radius, Alignment align, ButtonState state); +void draw_color_picker_popup(UIConstraints *picker_button_con); +void draw_color_picker(UIConstraints *constraints, Color *color, Alignment align, ButtonState state); diff --git a/zephr.c b/zephr.c index ce065ab..3714e33 100644 --- a/zephr.c +++ b/zephr.c @@ -539,7 +539,7 @@ void x11_assign_window_icon(const char *icon_path) { u32 target_size = 2 + icon_width * icon_height; - u64 *data = malloc(target_size * sizeof(u64)); + u64 *data = alloca(target_size * sizeof(u64)); u64 *target = data; // first two elements are width and height @@ -554,7 +554,6 @@ void x11_assign_window_icon(const char *icon_path) { XChangeProperty(x11_display, x11_window, net_wm_icon, XA_CARDINAL, 32, PropModeReplace, (unsigned char*)data, target_size); - free(data); stbi_image_free(icon_data); } @@ -809,6 +808,10 @@ bool zephr_should_quit(void) { void zephr_swap_buffers(void) { CORE_DEBUG_ASSERT(zephr_ctx, "Zephr context not initialized"); + if (zephr_ctx->ui.popup_open) { + draw_color_picker_popup(&zephr_ctx->ui.popup_parent_constraints); + } + glXSwapBuffers(x11_display, x11_window); XDefineCursor(x11_display, x11_window, zephr_ctx->cursors[zephr_ctx->cursor]); } @@ -1016,12 +1019,21 @@ bool zephr_iter_events(ZephrEvent *event_out) { switch (xev.xbutton.button) { case Button1: event_out->mouse.button = ZEPHR_MOUSE_BUTTON_LEFT; + if (!inside_rect(&zephr_ctx->ui.popup_rect, &event_out->mouse.position)) { + zephr_ctx->ui.popup_open = false; + } break; case Button2: event_out->mouse.button = ZEPHR_MOUSE_BUTTON_MIDDLE; + if (!inside_rect(&zephr_ctx->ui.popup_rect, &event_out->mouse.position)) { + zephr_ctx->ui.popup_open = false; + } break; case Button3: event_out->mouse.button = ZEPHR_MOUSE_BUTTON_RIGHT; + if (!inside_rect(&zephr_ctx->ui.popup_rect, &event_out->mouse.position)) { + zephr_ctx->ui.popup_open = false; + } break; } diff --git a/zephr.h b/zephr.h index b0de73a..6bedc22 100644 --- a/zephr.h +++ b/zephr.h @@ -380,6 +380,8 @@ typedef struct ZephrContext { ZephrMouse mouse; ZephrCursor cursor; ZephrCursor cursors[ZEPHR_CURSOR_COUNT]; + UI ui; + /* ZephrKeyboard keyboard; */ /* XkbDescPtr xkb; */ /* XIM xim; */ diff --git a/zephr_math.c b/zephr_math.c index 1b4ceb3..1c2c898 100644 --- a/zephr_math.c +++ b/zephr_math.c @@ -84,3 +84,26 @@ Color mult_color(Color color, float amount) { return result; } +Color hsv2rgb(float h, float s, float v) { + float c = v * s; + float x = (float)(c * (1 - fabs(fmod(h / 60.0, 2) - 1))); + float m = v - c; + + float r, g, b; + + if (h >= 0 && h < 60) { + r = c, g = x, b = 0; + } else if (h >= 60 && h < 120) { + r = x, g = c, b = 0; + } else if (h >= 120 && h < 180) { + r = 0, g = c, b = x; + } else if (h >= 180 && h < 240) { + r = 0, g = x, b = c; + } else if (h >= 240 && h < 300) { + r = x, g = 0, b = c; + } else { + r = c, g = 0, b = x; + } + + return (Color){(u8)((r + m) * 255), (u8)((g + m) * 255), (u8)((b + m) * 255), 255}; +} diff --git a/zephr_math.h b/zephr_math.h index a1acea9..e9d1c0b 100644 --- a/zephr_math.h +++ b/zephr_math.h @@ -55,3 +55,4 @@ void apply_rotation(Matrix4x4 *model, float angle); void apply_translation(Matrix4x4 *matrix, Vec2f pos); Matrix4x4 orthographic_projection_2d(float left, float right, float bottom, float top); Color mult_color(Color color, float amount); +Color hsv2rgb(float h, float s, float v);