From d37f63234415c6f8a4a42f9dd8b95a0e78a3c1a1 Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Sun, 5 Oct 2025 00:44:23 +0100 Subject: [PATCH] [Refactor] Implement batched shape rendering. This mirrors the previous refactor of the TextRenderer. All calls to draw_rect and draw_triangle now buffer vertex data (position and color). A begin()/flush() system is introduced to manage batching. --- assets/shaders/shape.frag | 4 +- assets/shaders/shape.vert | 4 ++ client/src/game_state.cpp | 17 ++++++- client/src/game_state.h | 21 ++++---- client/src/gfx/shape_renderer.cpp | 82 ++++++++++++++++--------------- client/src/gfx/shape_renderer.h | 14 ++++++ client/src/gfx/txt_renderer.cpp | 4 ++ client/src/main.cpp | 13 ++++- client/src/ui/desktop.cpp | 35 ++++++++----- client/src/ui/launcher.cpp | 6 +++ client/src/ui/main_menu.cpp | 2 + client/src/ui/menu_bar.cpp | 16 +++++- client/src/ui/taskbar.cpp | 56 ++++++++++++++------- client/src/ui/taskbar.h | 18 ++++--- client/src/ui/ui_renderer.cpp | 10 ++++ client/src/ui/ui_renderer.h | 3 ++ client/src/ui/ui_window.cpp | 3 ++ 17 files changed, 217 insertions(+), 91 deletions(-) diff --git a/assets/shaders/shape.frag b/assets/shaders/shape.frag index 63e8e10..8155be4 100644 --- a/assets/shaders/shape.frag +++ b/assets/shaders/shape.frag @@ -1,8 +1,8 @@ #version 330 core out vec4 FragColor; -uniform vec3 objectColor; +in vec3 ourColor; void main() { - FragColor = vec4(objectColor, 1.0); + FragColor = vec4(ourColor, 1.0); } diff --git a/assets/shaders/shape.vert b/assets/shaders/shape.vert index e044e00..f9b3476 100644 --- a/assets/shaders/shape.vert +++ b/assets/shaders/shape.vert @@ -1,8 +1,12 @@ #version 330 core layout (location = 0) in vec2 aPos; +layout (location = 1) in vec3 aColor; uniform mat4 projection; +out vec3 ourColor; + void main() { gl_Position = projection * vec4(aPos.x, aPos.y, 0.0, 1.0); + ourColor = aColor; } diff --git a/client/src/game_state.cpp b/client/src/game_state.cpp index 9c0ade4..52a3c69 100644 --- a/client/src/game_state.cpp +++ b/client/src/game_state.cpp @@ -13,8 +13,10 @@ #include "ui/i_window_content.h" #include "ui/ui_window.h" #include "ui/editor.h" +#include #include #include +#include "debug/debug_overlay.h" void GameState::_init_desktop(void) { _desktop = std::make_unique(_screen_width, _screen_height, _network.get()); @@ -46,7 +48,8 @@ GameState::GameState(void) : _current_screen(Screen::MAIN_MENU), _screen_width(0), _screen_height(0), - _is_single_player(false) {} + _is_single_player(false), + _show_debug_overlay(false) {_debug_overlay = std::make_unique();} GameState::~GameState(void) = default; @@ -77,6 +80,11 @@ void GameState::start_single_player_now(int screen_width, int screen_height) { } void GameState::handle_event(SDL_Event* event, int screen_width, int screen_height) { + if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_D && + (event->key.mod & SDL_KMOD_CTRL)) { + _show_debug_overlay = !_show_debug_overlay; + return; /* Consume the event. */ + } switch(_current_screen) { case Screen::MAIN_MENU: if(_main_menu) { @@ -94,7 +102,8 @@ void GameState::handle_event(SDL_Event* event, int screen_width, int screen_heig } } -void GameState::update(float dt) { +void GameState::update(float dt, int draw_calls, int shape_verts, int text_verts) { + _debug_overlay->update(dt, draw_calls, shape_verts, text_verts); switch(_current_screen) { case Screen::MAIN_MENU: { if(!_main_menu) break; @@ -221,4 +230,8 @@ void GameState::render(const RenderContext& context) { } break; } + + if(_show_debug_overlay) { + _debug_overlay->render(context.ui_renderer); + } } diff --git a/client/src/game_state.h b/client/src/game_state.h index ed4664a..bc3a0c9 100644 --- a/client/src/game_state.h +++ b/client/src/game_state.h @@ -4,6 +4,7 @@ #include "gfx/types.h" +class DebugOverlay; class ClientNetwork; class Desktop; class BootSequence; @@ -26,18 +27,20 @@ public: void init(int screen_width, int screen_height); void start_single_player_now(int screen_width, int screen_height); void handle_event(SDL_Event* event, int screen_width, int screen_height); - void update(float dt); + void update(float dt, int draw_calls, int shape_verts, int text_verts); void render(const RenderContext& context); private: - std::unique_ptr _network; - std::unique_ptr _desktop; - std::unique_ptr _boot_sequence; - std::unique_ptr _main_menu; - Screen _current_screen; - int _screen_width; - int _screen_height; - bool _is_single_player; + std::unique_ptr _network; + std::unique_ptr _desktop; + std::unique_ptr _boot_sequence; + std::unique_ptr _main_menu; + std::unique_ptr _debug_overlay; + bool _show_debug_overlay; + Screen _current_screen; + int _screen_width; + int _screen_height; + bool _is_single_player; void _init_desktop(void); void _run_server(void); diff --git a/client/src/gfx/shape_renderer.cpp b/client/src/gfx/shape_renderer.cpp index 5928455..3b5a4a4 100644 --- a/client/src/gfx/shape_renderer.cpp +++ b/client/src/gfx/shape_renderer.cpp @@ -1,6 +1,7 @@ #include #include "shape_renderer.h" +#include "debug/debug_stats.h" #include "math/math.h" ShapeRenderer::ShapeRenderer(unsigned int screen_width, unsigned int screen_height) { @@ -12,67 +13,70 @@ ShapeRenderer::ShapeRenderer(unsigned int screen_width, unsigned int screen_heig _shape_shader->use(); _shape_shader->set_mat4("projection", _projection); - /* Configure VAO/VBO. */ + /* Configure VAO/VBO for batch rendering. */ glGenVertexArrays(1, &_vao); glGenBuffers(1, &_vbo); glBindVertexArray(_vao); glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 2, NULL, GL_DYNAMIC_DRAW); + glBufferData(GL_ARRAY_BUFFER, sizeof(ShapeVertex) * MAX_SHAPE_VERTICES, nullptr, GL_DYNAMIC_DRAW); + + /* Position attribute. */ glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), 0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), (void*)0); + /* Colour attribute. */ + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(ShapeVertex), (void*)offsetof(ShapeVertex, r)); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); + + _vertices.reserve(MAX_SHAPE_VERTICES); } ShapeRenderer::~ShapeRenderer(void) { delete _shape_shader; } -void ShapeRenderer::draw_rect(int x, int y, int width, int height, const Color& color) { - _shape_shader->use(); - _shape_shader->set_vec3("objectColor", color.r, color.g, color.b); +void ShapeRenderer::begin(void) { + _vertices.clear(); +} +void ShapeRenderer::flush(void) { + if(_vertices.empty()) return; + + _shape_shader->use(); + glBindVertexArray(_vao); + + glBindBuffer(GL_ARRAY_BUFFER, _vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, _vertices.size() * sizeof(ShapeVertex), _vertices.data()); + + DebugStats::draw_calls++; + DebugStats::shape_vertices += _vertices.size(); + + glDrawArrays(GL_TRIANGLES, 0, _vertices.size()); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); +} + +void ShapeRenderer::draw_rect(int x, int y, int width, int height, const Color& color) { float x_f = (float)x; float y_f = (float)y; float w_f = (float)width; float h_f = (float)height; - /* This is ugly :) */ - float vertices[6][2] = { - { x_f, y_f + h_f }, - { x_f, y_f }, - { x_f + w_f, y_f }, + _vertices.push_back({x_f, y_f+h_f, color.r, color.g, color.b}); + _vertices.push_back({x_f, y_f, color.r, color.g, color.b}); + _vertices.push_back({x_f+w_f, y_f, color.r, color.g, color.b}); - { x_f, y_f + h_f }, - { x_f + w_f, y_f }, - {x_f + w_f, y_f + h_f } - }; - - glBindVertexArray(_vao); - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); - glDrawArrays(GL_TRIANGLES, 0, 6); - glBindVertexArray(0); + _vertices.push_back({x_f, y_f+h_f, color.r, color.g, color.b}); + _vertices.push_back({x_f+w_f, y_f, color.r, color.g, color.b}); + _vertices.push_back({x_f+w_f, y_f+h_f, color.r, color.g, color.b}); } void ShapeRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, const Color& color) { - _shape_shader->use(); - _shape_shader->set_vec3("objectColor", color.r, color.g, color.b); - - float vertices[3][2] = { - {(float)x1, (float)y1}, - {(float)x2, (float)y2}, - {(float)x3, (float)y3}, - }; - - glBindVertexArray(_vao); - glBindBuffer(GL_ARRAY_BUFFER, _vbo); - /* - * We're overwriting the buffer content here, this is fine for just one triangle, - * but don't do it for everything ;) - */ - glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); - glDrawArrays(GL_TRIANGLES, 0, 3); - glBindVertexArray(0); + _vertices.push_back({(float)x1, (float)y1, color.r, color.g, color.b}); + _vertices.push_back({(float)x2, (float)y2, color.r, color.g, color.b}); + _vertices.push_back({(float)x3, (float)y3, color.r, color.g, color.b}); } diff --git a/client/src/gfx/shape_renderer.h b/client/src/gfx/shape_renderer.h index 729bbb7..2428bcf 100644 --- a/client/src/gfx/shape_renderer.h +++ b/client/src/gfx/shape_renderer.h @@ -1,13 +1,26 @@ #pragma once +#include + #include "shader.h" #include "types.h" +struct ShapeVertex { + float x, y, r, g, b; +}; + +const int MAX_SHAPES_PER_BATCH = 10000; +const int VERTICES_PER_RECT = 6; +const int MAX_SHAPE_VERTICES = MAX_SHAPES_PER_BATCH * VERTICES_PER_RECT; + class ShapeRenderer { public: ShapeRenderer(unsigned int screen_width, unsigned int screen_height); ~ShapeRenderer(void); + void begin(void); + void flush(void); + void draw_rect(int x, int y, int width, int height, const Color& color); void draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, const Color& color); @@ -15,5 +28,6 @@ public: private: Shader* _shape_shader; unsigned int _vao, _vbo; + std::vector _vertices; float _projection[16]; }; diff --git a/client/src/gfx/txt_renderer.cpp b/client/src/gfx/txt_renderer.cpp index 1ded38d..c0591fb 100644 --- a/client/src/gfx/txt_renderer.cpp +++ b/client/src/gfx/txt_renderer.cpp @@ -5,6 +5,7 @@ #include #include "txt_renderer.h" +#include "debug/debug_stats.h" #include "math/math.h" TextRenderer::TextRenderer(unsigned int screen_width, unsigned int screen_height) { @@ -141,6 +142,9 @@ void TextRenderer::flush(void) { glBindBuffer(GL_ARRAY_BUFFER, _vbo); glBufferSubData(GL_ARRAY_BUFFER, 0, _vertices.size() * sizeof(TextVertex), _vertices.data()); + DebugStats::draw_calls++; + DebugStats::text_vertices += _vertices.size(); + glDrawArrays(GL_TRIANGLES, 0, _vertices.size()); glBindBuffer(GL_ARRAY_BUFFER, 0); diff --git a/client/src/main.cpp b/client/src/main.cpp index 44efaa7..d45237b 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -12,6 +12,7 @@ #include "ui/ui_renderer.h" #include "gfx/types.h" #include "ui/cursor_manager.h" +#include "debug/debug_stats.h" const int SCREEN_WIDTH = 1280; const int SCREEN_HEIGHT = 720; @@ -101,6 +102,9 @@ int main(int argc, char** argv) { bool running = true; while(running) { + /* Reset per-frame stats. */ + DebugStats::reset(); + /* Event handling. */ SDL_Event event; while(SDL_PollEvent(&event)) { @@ -116,8 +120,6 @@ int main(int argc, char** argv) { /* Clamp dt to avoid large jumps. */ if(dt > 0.1f) dt = 0.1f; - game_state->update(dt); - Uint32 current_time = SDL_GetTicks(); if(current_time - last_blink_time > 500) { /* Every 500ms. */ show_cursor = !show_cursor; @@ -135,6 +137,13 @@ int main(int argc, char** argv) { }; game_state->render(context); + /* Update game state after rendering so stats are for the frame just rendered. + * I know this is unusual, but given the text based nature of the game + * we won't see a negative impact, and this is better than a large refactor ;) + */ + game_state->update(dt, DebugStats::draw_calls, DebugStats::shape_vertices, + DebugStats::text_vertices); + /* It's really odd to call it SwapWindow now, rather than SwapBuffer. */ SDL_GL_SwapWindow(window); } diff --git a/client/src/ui/desktop.cpp b/client/src/ui/desktop.cpp index 86f66db..63c316c 100644 --- a/client/src/ui/desktop.cpp +++ b/client/src/ui/desktop.cpp @@ -60,9 +60,10 @@ Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) { Desktop::~Desktop(void) {} void Desktop::add_window(std::unique_ptr window) { + UIWindow* window_ptr = window.get(); _windows.push_back(std::move(window)); - /* Focus new window. */ - _set_focused_window(_windows.back().get()); + _taskbar->add_window(window_ptr); + _set_focused_window(window_ptr); } void Desktop::_set_focused_window(UIWindow* window) { @@ -92,31 +93,37 @@ void Desktop::_set_focused_window(UIWindow* window) { } void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height) { - /* Let focused window handle all events first. */ - if(_focused_window) { - _focused_window->handle_event(event, screen_width, screen_height, _taskbar->get_height()); - } if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) { int mouse_x = event->button.x; int mouse_y = event->button.y; + /* If click is on taskbar, ignore it on the down-event. Let the up-event handle it. */ + if(_taskbar->is_point_inside(mouse_x, mouse_y)) { + return; + } + /* When launcher open, check for outside click to close it. */ if(_launcher_is_open && !_launcher->is_point_inside(mouse_x, mouse_y, screen_height)) { _launcher_is_open = false; return; } - /* If click no on focused window, find which window should receive focus. */ + /* If click not on focused window, find which window should receive focus. */ bool click_on_window = false; for(int i = _windows.size()-1; i >= 0; --i) { if(_windows[i].get()->is_point_inside(mouse_x, mouse_y)) { - if(!_windows[i]->is_minimized()) { - _set_focused_window(_windows[i].get()); + /* Top most window. Focus it. */ + UIWindow* target = _windows[i].get(); + if(!target->is_minimized()) { + _set_focused_window(target); + /* Pass event down to newly focused window to handle dragging, etc. */ + target->handle_event(event, screen_width, screen_height, _taskbar->get_height()); } click_on_window = true; break; } } + /* Click wasn't on a window. Unfocus. */ if(!click_on_window) { _set_focused_window(nullptr); } @@ -139,7 +146,7 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height _launcher_is_open = false; } } else { - UIWindow* clicked_window = _taskbar->handle_event(event, screen_height, _windows); + UIWindow* clicked_window = _taskbar->handle_event(event, screen_height); if(clicked_window) { if(clicked_window == _focused_window && !clicked_window->is_minimized()) { clicked_window->minimize(); @@ -160,6 +167,11 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height } CursorManager::set_cursor(CursorType::ARROW); } + + /* Pass all other non-down-click events to focused window. */ + if(_focused_window && event->type != SDL_EVENT_MOUSE_BUTTON_DOWN) { + _focused_window->handle_event(event, screen_width, screen_height, _taskbar->get_height()); + } } void Desktop::update(float dt, int screen_width, int screen_height) { @@ -193,6 +205,7 @@ void Desktop::update(float dt, int screen_width, int screen_height) { _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [this](const std::unique_ptr& w) { if (w->should_close()) { + _taskbar->remove_window(w.get()); if (w.get() == _focused_window) { _focused_window = nullptr; } @@ -243,7 +256,7 @@ void Desktop::render(const RenderContext& context) { if(_launcher_is_open) { _launcher->render(context.ui_renderer); } - _taskbar->render(context.ui_renderer, _windows, _focused_window); + _taskbar->render(context.ui_renderer, _focused_window); context.ui_renderer->flush_text(); } diff --git a/client/src/ui/launcher.cpp b/client/src/ui/launcher.cpp index 6b2e62e..c0aae33 100644 --- a/client/src/ui/launcher.cpp +++ b/client/src/ui/launcher.cpp @@ -22,6 +22,9 @@ void Launcher::render(UIRenderer* ui_renderer) { const Color text_color = { 0.9f, 0.9f, 0.9f }; const Color hover_color = { 0.3f, 0.32f, 0.34f }; + ui_renderer->begin_shapes(); + ui_renderer->begin_text(); + /* Note: y-coord is TOP of launcher menu. */ ui_renderer->draw_rect(_x, _y, _width, _height, bg_color); @@ -41,6 +44,9 @@ void Launcher::render(UIRenderer* ui_renderer) { ui_renderer->render_text(app_name.c_str(), _x+10, item_y+20, text_color); item_y += item_height; } + + ui_renderer->flush_shapes(); + ui_renderer->flush_text(); } std::string Launcher::handle_event(SDL_Event* event, int screen_height) { diff --git a/client/src/ui/main_menu.cpp b/client/src/ui/main_menu.cpp index 3035be0..ec99566 100644 --- a/client/src/ui/main_menu.cpp +++ b/client/src/ui/main_menu.cpp @@ -101,6 +101,7 @@ void MainMenu::render(UIRenderer* ui_renderer) { ui_renderer->flush_text(); /* Pass 2: Buttons. */ + ui_renderer->begin_shapes(); ui_renderer->begin_text(); /* Button colours. */ @@ -124,6 +125,7 @@ void MainMenu::render(UIRenderer* ui_renderer) { ui_renderer->render_text(button.label.c_str(), text_x, button.rect.y + 32, text_color); } + ui_renderer->flush_shapes(); ui_renderer->flush_text(); } diff --git a/client/src/ui/menu_bar.cpp b/client/src/ui/menu_bar.cpp index 4c1b0ec..205fd28 100644 --- a/client/src/ui/menu_bar.cpp +++ b/client/src/ui/menu_bar.cpp @@ -73,8 +73,11 @@ void MenuBar::handle_event(SDL_Event* event, int window_x, int window_y) { } void MenuBar::render_bar(UIRenderer* ui_renderer, int x, int y, int width) { - const Color bg_color = {0.15f, 0.17f, 0.19f}; - const Color text_color = {0.9f, 0.9f, 0.9f}; + const Color bg_color = { 0.15f, 0.17f, 0.19f }; + const Color text_color = { 0.9f, 0.9f, 0.9f }; + + ui_renderer->begin_shapes(); + ui_renderer->begin_text(); ui_renderer->draw_rect(x, y, width, _height, bg_color); @@ -85,6 +88,9 @@ void MenuBar::render_bar(UIRenderer* ui_renderer, int x, int y, int width) { menu_x += menu_width; } + + ui_renderer->flush_shapes(); + ui_renderer->flush_text(); } void MenuBar::render_dropdown(UIRenderer* ui_renderer, int x, int y, int width ) { @@ -93,6 +99,9 @@ void MenuBar::render_dropdown(UIRenderer* ui_renderer, int x, int y, int width ) const Color bg_color = { 0.15f, 0.17f, 0.19f }; const Color text_color = { 0.9f, 0.9f, 0.9f }; + ui_renderer->begin_shapes(); + ui_renderer->begin_text(); + int menu_x = x + (_open_menu_index*60); int item_y = y + _height; int item_height = 30; @@ -102,4 +111,7 @@ void MenuBar::render_dropdown(UIRenderer* ui_renderer, int x, int y, int width ) ui_renderer->render_text(item.label.c_str(), menu_x+10, item_y+20, text_color); item_y += item_height; } + + ui_renderer->flush_shapes(); + ui_renderer->flush_text(); } diff --git a/client/src/ui/taskbar.cpp b/client/src/ui/taskbar.cpp index 15b7e8f..fc563cd 100644 --- a/client/src/ui/taskbar.cpp +++ b/client/src/ui/taskbar.cpp @@ -1,10 +1,11 @@ -#include "taskbar.h" +#include #include #include #include #include #include +#include "taskbar.h" #include "ui/ui_renderer.h" #include "ui/ui_window.h" @@ -17,15 +18,28 @@ Taskbar::Taskbar(int screen_width, int screen_height) { Taskbar::~Taskbar(void) {} -void Taskbar::render(UIRenderer* ui_renderer, - const std::vector>& windows, - UIWindow* focused_window) { +void Taskbar::add_window(UIWindow* window) { + _buttons.push_back({window, window->get_title()}); +} +void Taskbar::remove_window(UIWindow* window) { + _buttons.erase( + std::remove_if(_buttons.begin(), _buttons.end(), + [window](const TaskbarButton& btn) { + return btn.window == window; + }), + _buttons.end()); +} + +void Taskbar::render(UIRenderer* ui_renderer, UIWindow* focused_window) { const Color taskbar_color = { 0.1f, 0.12f, 0.14f }; const Color button_color = { 0.2f, 0.22f, 0.24f }; const Color button_focused_color = { 0.3f, 0.32f, 0.34f }; const Color button_text_color = { 0.9f, 0.9f, 0.9f }; + ui_renderer->begin_shapes(); + ui_renderer->begin_text(); + ui_renderer->draw_rect(0, _y_pos, _width, _height, taskbar_color); /* Draw start button. */ @@ -38,29 +52,31 @@ void Taskbar::render(UIRenderer* ui_renderer, int padding = 5; int x_offset = _start_button_width + padding; - for(const auto& window : windows) { - bool is_focused = (window.get() == focused_window); + for(const auto& button: _buttons) { + bool is_focused = (button.window == focused_window); ui_renderer->draw_rect(x_offset, _y_pos + padding, button_width, _height - (padding * 2), is_focused ? button_focused_color : button_color); /* TODO: Truncate text when too long. */ - ui_renderer->render_text(window->get_title().c_str(), x_offset + 10, + ui_renderer->render_text(button.title.c_str(), x_offset + 10, _y_pos + 20, button_text_color); x_offset += button_width + padding; } - /* Draw clock. */ - auto now = std::chrono::system_clock::now(); - auto in_time_t = std::chrono::system_clock::to_time_t(now); - std::stringstream ss; - ss << std::put_time(std::localtime(&in_time_t), "%H:%M"); - std::string time_str = ss.str(); + /* Draw clock. */ + auto now = std::chrono::system_clock::now(); + auto in_time_t = std::chrono::system_clock::to_time_t(now); + std::stringstream ss; + ss << std::put_time(std::localtime(&in_time_t), "%H:%M"); + std::string time_str = ss.str(); - ui_renderer->render_text(time_str.c_str(), _width-50, _y_pos+20, button_text_color); + ui_renderer->render_text(time_str.c_str(), _width-50, _y_pos+20, button_text_color); + + ui_renderer->flush_shapes(); + ui_renderer->flush_text(); } -UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height, - const std::vector>& windows) { +UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height) { if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) { int mouse_x = event->button.x; int mouse_y = event->button.y; @@ -68,10 +84,10 @@ UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height, int button_width = 150; int padding = 5; int x_offset = _start_button_width + padding; - for(const auto& window : windows) { + for(const auto& button : _buttons) { if(mouse_x >= x_offset && mouse_x <= x_offset + button_width && mouse_y >= _y_pos && mouse_y <= _y_pos + _height) { - return window.get(); /* Return clicked window. */ + return button.window; /* Return clicked window. */ } x_offset += button_width + padding; } @@ -92,3 +108,7 @@ bool Taskbar::is_start_button_clicked(SDL_Event* event, int screen_height) { int Taskbar::get_height(void) const { return _height; } + +bool Taskbar::is_point_inside(int x, int y) const { + return (y >= _y_pos && y<= _y_pos + _height); +} diff --git a/client/src/ui/taskbar.h b/client/src/ui/taskbar.h index 179cbf3..a1f8d9c 100644 --- a/client/src/ui/taskbar.h +++ b/client/src/ui/taskbar.h @@ -1,27 +1,33 @@ #pragma once #include +#include #include -#include class UIWindow; class UIRenderer; +struct TaskbarButton { + UIWindow* window; + std::string title; +}; + class Taskbar { public: Taskbar(int screen_width, int screen_height); ~Taskbar(void); - void render(UIRenderer* ui_renderer, - const std::vector>& windows, - UIWindow* focused_window); + void add_window(UIWindow* window); + void remove_window(UIWindow* window); - UIWindow* handle_event(SDL_Event* event, int screen_height, - const std::vector>& windows); + void render(UIRenderer* ui_renderer, UIWindow* focused_window); + UIWindow* handle_event(SDL_Event* event, int screen_height); bool is_start_button_clicked(SDL_Event* event, int screen_height); + bool is_point_inside(int x, int y) const; int get_height(void) const; private: + std::vector _buttons; int _width; int _height; int _y_pos; diff --git a/client/src/ui/ui_renderer.cpp b/client/src/ui/ui_renderer.cpp index d5d4a5b..a605f49 100644 --- a/client/src/ui/ui_renderer.cpp +++ b/client/src/ui/ui_renderer.cpp @@ -38,6 +38,16 @@ void UIRenderer::flush_text(void) { _txt_renderer->flush(); } +void UIRenderer::begin_shapes(void) { + if(!_shape_renderer) return; + _shape_renderer->begin(); +} + +void UIRenderer::flush_shapes(void) { + if(!_shape_renderer) return; + _shape_renderer->flush(); +} + TextRenderer* UIRenderer::get_text_renderer(void) { return _txt_renderer; } diff --git a/client/src/ui/ui_renderer.h b/client/src/ui/ui_renderer.h index 85bf8e5..a51860a 100644 --- a/client/src/ui/ui_renderer.h +++ b/client/src/ui/ui_renderer.h @@ -20,6 +20,9 @@ public: void begin_text(void); void flush_text(void); + void begin_shapes(void); + void flush_shapes(void); + /* Expose underlying text renderer for things like width calculation. */ TextRenderer* get_text_renderer(void); diff --git a/client/src/ui/ui_window.cpp b/client/src/ui/ui_window.cpp index 2cd60c6..d9fa69c 100644 --- a/client/src/ui/ui_window.cpp +++ b/client/src/ui/ui_window.cpp @@ -83,6 +83,7 @@ bool UIWindow::is_point_inside(int x, int y) { void UIWindow::render(const RenderContext& context) { int title_bar_height = 30; + context.ui_renderer->begin_shapes(); context.ui_renderer->begin_text(); /* Define colours. */ @@ -121,6 +122,8 @@ void UIWindow::render(const RenderContext& context) { context.ui_renderer->draw_triangle(corner_x, corner_y - 10, corner_x - 10, corner_y, corner_x, corner_y, resize_handle_color); + /* Flush the shapes and text for the window frame and title bar. */ + context.ui_renderer->flush_shapes(); context.ui_renderer->flush_text(); if(_content) {