From def19703ae126fcc73f38abacc3cfd41a515302a Mon Sep 17 00:00:00 2001
From: talha <sarcxd@gmail.com>
Date: Sun, 6 Apr 2025 13:51:01 +0500
Subject: Added dropdown button

---
 source/main.cpp              | 330 ++++++++++++++++++++++++++++++-------------
 source/renderer/renderer.cpp |   3 +-
 2 files changed, 235 insertions(+), 98 deletions(-)

diff --git a/source/main.cpp b/source/main.cpp
index 2280e94..214ddff 100755
--- a/source/main.cpp
+++ b/source/main.cpp
@@ -532,7 +532,7 @@ struct UiButton {
 // @description: This is a very scrappy function that goes through the button render
 // logic and pre-computes items like text dimensions
 // It does not support, tabs and newlines
-Vec2 ui_button_get_text_dims(GLRenderer renderer, char *text, r32 font_size) {
+Vec2 ui_get_text_dims(GLRenderer renderer, char *text, r32 font_size) {
     Vec2 max_dims = Vec2{0, 0};
 
     u32 running_index = 0;
@@ -591,7 +591,7 @@ ButtonState ui_button(GameState state, UiButton button) {
     if (!font_size) {
 	font_size = 0.6f * quad_size.y;
     }
-    Vec2 txt_dims = ui_button_get_text_dims(state.renderer, 
+    Vec2 txt_dims = ui_get_text_dims(state.renderer, 
 					    button.text.buffer, 
 					    font_size);
     r32 txt_base_offsety = -5.0f*state.render_scale.y;
@@ -650,6 +650,197 @@ ButtonState ui_button(GameState state, UiButton button) {
     return btn_state;
 }
 
+struct UiDropdownButton {
+    r32 font_size;
+    Vec3 position;
+    Vec2 size;
+    b8 is_toggled;
+    u32 selected_option_index;
+    u32 option_count;
+    Str256 *options;
+
+    Vec3 bgd_color_primary;
+    Vec3 bgd_color_secondary;
+    Vec3 bgd_color_hover;
+    Vec3 bgd_color_pressed;
+};
+
+Vec2 ui_center_text(
+	GameState state, 
+	Str256 text, 
+	r32 font_size, 
+	Vec2 container_pos,
+	Vec2 container_size) 
+{
+    // @step: ui position text
+    Vec2 text_dims = ui_get_text_dims(
+	    state.renderer,
+	    text.buffer,
+	    font_size);
+
+    r32 txt_base_offsety = -5.0f*state.render_scale.y;
+    Vec2 txt_center_offset = Vec2{
+	(container_size.x - text_dims.x)/2.0f,
+	(container_size.y - text_dims.y)/2.0f + txt_base_offsety
+    };
+    Vec2 txt_pos = container_pos + txt_center_offset;
+
+    return txt_pos;
+}
+
+void ui_dropdown_button(GameState state, UiDropdownButton *dropdown) {
+    r32 font_size = dropdown->font_size * state.render_scale.y;
+
+    // @component: value_box
+    Vec3 value_pos = Vec3{
+	dropdown->position.x*state.render_scale.x,
+	dropdown->position.y*state.render_scale.y,
+	dropdown->position.z
+    };
+
+    Vec2 value_size = dropdown->size*state.render_scale;
+
+    Vec3 value_pos_adjusted = value_pos;
+    value_pos_adjusted.x += value_size.x/2.0f;
+    value_pos_adjusted.y += value_size.y/2.0f;
+    
+    gl_draw_quad(
+	    state.renderer.quad,
+	    &state.renderer.ui_cam,
+	    value_pos_adjusted,
+	    value_size,
+	    dropdown->bgd_color_primary
+	    );
+
+    // @component: toggle_button
+    Vec3 toggle_pos = Vec3{
+	value_pos.x + value_size.x,
+	value_pos.y,
+	value_pos.z
+    };
+    Vec2 toggle_size = Vec2{40.0f*state.render_scale.x, value_size.y};
+    Vec3 toggle_pos_adjusted = toggle_pos;
+    toggle_pos_adjusted.x += toggle_size.x/2.0f;
+    toggle_pos_adjusted.y += toggle_size.y/2.0f;
+
+    // @step: check if mouse on button
+    b8 is_mouse_on_button = 0;
+    {
+	Rect btn_rect = rect(toggle_pos.v2(), toggle_size);
+	is_mouse_on_button = (
+		(state.mouse_position.x >= btn_rect.lb.x && 
+		state.mouse_position.y >= btn_rect.lb.y) &&
+		(state.mouse_position.x <= btn_rect.rt.x &&
+		state.mouse_position.y <= btn_rect.rt.y)
+		);
+    }
+    Vec3 toggle_color = dropdown->bgd_color_secondary;
+    if (is_mouse_on_button) {
+	if (state.mouse_down) {
+	    toggle_color = dropdown->bgd_color_pressed;
+	} else if (state.mouse_up) {
+	    dropdown->is_toggled = !dropdown->is_toggled;
+	} else {
+	    toggle_color = dropdown->bgd_color_hover;
+	}
+    }
+
+    Str256 value_text = dropdown->options[dropdown->selected_option_index];
+    Vec2 value_text_pos = ui_center_text(
+	    state,
+	    value_text,
+	    dropdown->font_size,
+	    value_pos.v2(),
+	    value_size);
+
+    gl_draw_quad(
+	    state.renderer.quad,
+	    &state.renderer.ui_cam,
+	    toggle_pos_adjusted,
+	    toggle_size,
+	    toggle_color
+	    );
+    gl_render_text(
+	    &state.renderer,
+	    value_text.buffer,
+	    Vec3{
+		value_text_pos.x,
+		value_text_pos.y,
+		value_pos.z},
+	    Vec3{0.0f, 0.0f, 0.0f},
+	    dropdown->font_size);
+
+    // @component: dropdown_option
+    if (dropdown->is_toggled) {
+	Vec2 option_size = value_size;
+	for (int i = 0; i < dropdown->option_count; i++) {
+	    Vec3 option_pos = Vec3{
+		value_pos.x,
+		value_pos.y - (option_size.y*(i+1)),
+		value_pos.z
+	    };
+	    Vec3 option_color = i%2 ? 
+		Vec3{0.8f, 0.3f, 0.2f} : 
+		Vec3{0.2f, 0.3f, 0.8f};
+
+	    b8 is_mouse_on_option = 0;
+	    {
+		Rect btn_rect = rect(option_pos.v2(), option_size);
+		is_mouse_on_option = (
+			(state.mouse_position.x >= btn_rect.lb.x && 
+			 state.mouse_position.y >= btn_rect.lb.y) &&
+			(state.mouse_position.x <= btn_rect.rt.x &&
+			 state.mouse_position.y <= btn_rect.rt.y));
+	    }
+	    b8 is_option_clicked = 0;
+	    if (is_mouse_on_option) {
+		if (state.mouse_down) {
+		    option_color = dropdown->bgd_color_pressed;
+		} else if (state.mouse_up) {
+		    option_color = dropdown->bgd_color_pressed;
+		    is_option_clicked = 1;
+		} else {
+		    option_color = dropdown->bgd_color_hover;
+		}
+	    }
+
+	    if (is_option_clicked) {
+		dropdown->selected_option_index = i;
+		dropdown->is_toggled = 0;
+	    }
+
+	    Str256 dropdown_text = dropdown->options[i];
+	    Vec2 txt_pos = ui_center_text(
+		    state,
+		    dropdown_text,
+		    dropdown->font_size,
+		    option_pos.v2(),
+		    option_size);
+
+	    gl_draw_quad(
+		    state.renderer.quad,
+		    &state.renderer.ui_cam,
+		    Vec3{
+			option_pos.x + option_size.x/2.0f,
+			option_pos.y + option_size.y/2.0f,
+			option_pos.z},
+		    option_size,
+		    option_color
+		    );
+
+	    gl_render_text(
+		    &state.renderer, 
+		    dropdown_text.buffer,
+		    Vec3{
+			txt_pos.x,
+			txt_pos.y,
+			option_pos.z},
+		    Vec3{0.0f, 0.0f, 0.0f},
+		    dropdown->font_size);
+	}
+    }
+}
+
 // @section: main
 int main(int argc, char* argv[])
 {
@@ -692,7 +883,7 @@ int main(int argc, char* argv[])
                                         SDL_WINDOWPOS_UNDEFINED,
                                         render_dims.x, render_dims.y,
                                         SDL_WINDOW_OPENGL
-					| SDL_WINDOW_FULLSCREEN_DESKTOP
+//					| SDL_WINDOW_FULLSCREEN_DESKTOP
 					);
 
   SDL_GLContext context = SDL_GL_CreateContext(window);
@@ -807,6 +998,27 @@ int main(int argc, char* argv[])
   renderer->ui_cam.view = renderer->cam_view;
   renderer->ui_cam.proj = renderer->cam_proj;
 
+  // ui stuff
+
+    UiDropdownButton resolution_select = {0};
+    resolution_select.font_size = 24.0f;
+    resolution_select.size = Vec2{120.0f, 40.0f};
+    resolution_select.bgd_color_primary = Vec3{1.0f, 1.0f, 1.0f};
+    resolution_select.bgd_color_secondary = Vec3{0.6f, 0.6f, 0.6f};
+    resolution_select.bgd_color_hover = Vec3{0.8f, 0.8f, 0.8f};
+    resolution_select.bgd_color_pressed = Vec3{0.4f, 0.4f, 0.4f};
+    resolution_select.option_count = 3;
+    // @todo: Remove this memory allocation method
+    // Use a lifetime tied arena
+    resolution_select.options =  (Str256 *)malloc(
+	    resolution_select.option_count *
+	    sizeof(Str256)
+	    );
+    resolution_select.selected_option_index = 1;
+    resolution_select.options[0] = str256("2560 x 1440");
+    resolution_select.options[1] = str256("1920 x 1080");
+    resolution_select.options[2] = str256("1280 x 720");
+
   // @thinking: level object handling
   // there should be a most smallest supported unit
   // smallest_size: 16x16
@@ -959,6 +1171,7 @@ int main(int argc, char* argv[])
 		    game_screen = PAUSE_MENU;
 		} else if (game_screen == SETTINGS_MENU) {
 		    game_screen = PAUSE_MENU;
+		    // clean_up_settings_menu
 		} else if (game_screen == PAUSE_MENU) {
 		    game_screen = GAMEPLAY;
 		}
@@ -1686,105 +1899,28 @@ int main(int argc, char* argv[])
 
 	    // settings menu
 	    if (game_screen == SETTINGS_MENU) {
-		UiButton back_button = button;
-
-		back_button.text = str256("Apply");
-		back_button.position = Vec3{30.0f, 40.0f, entity_z[TEXT]};
+		// resolution button
+		//
+		// @api:
+		// draw_ms_button(state)
+		// draw_ms_options(options)
+		//
+		// VS
+		//
+		// draw_ms_button(state, options)
+		u32 ms_font_size = 24.0f * state.render_scale.y;
 		gl_render_text(
 		    renderer, "Resolution", 
 		    Vec3{800, 800, entity_z[TEXT]}, 
-		    Vec3{0.0f, 0.0f, 0.0f}, 24.0f*state.render_scale.y
+		    Vec3{0.0f, 0.0f, 0.0f}, ms_font_size
 		);
-		{
-		    // @params
-		    Vec3 ms_value_pos = Vec3{1000.0f, 800.0f, entity_z[TEXT]};
-		    Vec2 ms_value_size = Vec2{120.0f, 40.0f};
-
-		    Vec3 ms_value_pos_adjusted = ms_value_pos; 
-		    ms_value_pos_adjusted.x += ms_value_size.x/2.0f;
-		    ms_value_pos_adjusted.y += ms_value_size.y/2.0f;
-
-		    // draw multi select value box
-		    gl_draw_quad(
-			state.renderer.quad,
-			&state.renderer.ui_cam,
-			ms_value_pos_adjusted,
-			ms_value_size,
-			Vec3{1.0f, 1.0f, 1.0f}
-		    );
-		    static bool is_toggle_open = false;
-		    {
-			Vec3 ms_toggle_pos = Vec3{
-			    ms_value_pos.x + ms_value_size.x, 
-			    ms_value_pos.y, 
-			    ms_value_pos.z
-			};
-			Vec2 ms_toggle_size = Vec2{40.0f, ms_value_size.y};
-			Vec3 ms_toggle_pos_adjusted = ms_toggle_pos;
-			ms_toggle_pos_adjusted.x += ms_toggle_size.x/2.0f;
-			ms_toggle_pos_adjusted.y += ms_toggle_size.y/2.0f;
-
-			b8 is_mouse_on_button = 0;
-			{
-				// check_if_mouse_on_button
-				Rect btn_rect = rect(ms_toggle_pos.v2(), ms_toggle_size);
-				is_mouse_on_button = (
-				    (state.mouse_position.x >= btn_rect.lb.x && 
-				    state.mouse_position.y >= btn_rect.lb.y) &&
-				    (state.mouse_position.x <= btn_rect.rt.x &&
-				    state.mouse_position.y <= btn_rect.rt.y)
-				);
-			}
-			Vec3 ms_toggle_color = {0.8f, 0.8f, 0.8f};
-			if (is_mouse_on_button) {
-			    if (state.mouse_down) {
-			        // pressed
-			        //btn_state = ButtonState::PRESSED;
-				ms_toggle_color = {0.8f, 0.8f, 0.8f};
-			    } else if (state.mouse_up) {
-			        //btn_state = ButtonState::CLICK;
-				is_toggle_open = !is_toggle_open;
-			    } else {
-			        // hover
-			        //btn_state = ButtonState::HOVER;
-				ms_toggle_color = {0.6f, 0.6f, 0.6f};
-			    }
-			}
-			gl_draw_quad(
-			    state.renderer.quad,
-			    &state.renderer.ui_cam,
-			    ms_toggle_pos_adjusted,
-			    ms_toggle_size,
-			    ms_toggle_color
-			);
-			if (is_toggle_open) {
-			    {
-				// draw toggle option
-				Vec2 ms_option_size = Vec2{ms_value_size.x + ms_toggle_size.x, ms_value_size.y};
-				Vec3 ms_option_pos = Vec3{ms_value_pos.x, ms_value_pos.y - ms_option_size.y, ms_value_pos.z};
-
-				gl_draw_quad(
-				    state.renderer.quad,
-				    &state.renderer.ui_cam,
-				    Vec3{ms_option_pos.x + ms_option_size.x/2.0f,
-					ms_option_pos.y + ms_option_size.y/2.0f,
-					ms_option_pos.z},
-				    ms_option_size,
-				    Vec3{1.0f, 0.0f, 0.0f}
-				);
-			    }
-			}
-		    }
 
-		    // multi-select drop down
-		    Str256 dropdown_options[] = {
-			str256("2560x1440"),
-			str256("1920x1080"),
-			str256("1280x720")
-		    };
-
-
-		}
+		resolution_select.position = Vec3{
+		    1000.0f,
+		    800.0f,
+		    entity_z[TEXT]
+		};
+		ui_dropdown_button(state, &resolution_select);
 	    }
     }
 
diff --git a/source/renderer/renderer.cpp b/source/renderer/renderer.cpp
index d61d85a..eabf57f 100644
--- a/source/renderer/renderer.cpp
+++ b/source/renderer/renderer.cpp
@@ -524,7 +524,8 @@ void gl_render_text(
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
     glUseProgram(renderer->ui_text.sp);
-    if (renderer->ui_cam.update) {
+    if (renderer->ui_cam.update) 
+    {
 
 	glUniformMatrix4fv(
 		glGetUniformLocation(
-- 
cgit v1.2.3