[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.
This commit is contained in:
parent
d992cb54bf
commit
d37f632344
@ -1,8 +1,8 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
out vec4 FragColor;
|
out vec4 FragColor;
|
||||||
|
|
||||||
uniform vec3 objectColor;
|
in vec3 ourColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
FragColor = vec4(objectColor, 1.0);
|
FragColor = vec4(ourColor, 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
layout (location = 0) in vec2 aPos;
|
layout (location = 0) in vec2 aPos;
|
||||||
|
layout (location = 1) in vec3 aColor;
|
||||||
|
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
|
||||||
|
out vec3 ourColor;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
gl_Position = projection * vec4(aPos.x, aPos.y, 0.0, 1.0);
|
gl_Position = projection * vec4(aPos.x, aPos.y, 0.0, 1.0);
|
||||||
|
ourColor = aColor;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,8 +13,10 @@
|
|||||||
#include "ui/i_window_content.h"
|
#include "ui/i_window_content.h"
|
||||||
#include "ui/ui_window.h"
|
#include "ui/ui_window.h"
|
||||||
#include "ui/editor.h"
|
#include "ui/editor.h"
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
#include <ui/main_menu.h>
|
#include <ui/main_menu.h>
|
||||||
#include <ui/boot_sequence.h>
|
#include <ui/boot_sequence.h>
|
||||||
|
#include "debug/debug_overlay.h"
|
||||||
|
|
||||||
void GameState::_init_desktop(void) {
|
void GameState::_init_desktop(void) {
|
||||||
_desktop = std::make_unique<Desktop>(_screen_width, _screen_height, _network.get());
|
_desktop = std::make_unique<Desktop>(_screen_width, _screen_height, _network.get());
|
||||||
@ -46,7 +48,8 @@ GameState::GameState(void) :
|
|||||||
_current_screen(Screen::MAIN_MENU),
|
_current_screen(Screen::MAIN_MENU),
|
||||||
_screen_width(0),
|
_screen_width(0),
|
||||||
_screen_height(0),
|
_screen_height(0),
|
||||||
_is_single_player(false) {}
|
_is_single_player(false),
|
||||||
|
_show_debug_overlay(false) {_debug_overlay = std::make_unique<DebugOverlay>();}
|
||||||
|
|
||||||
GameState::~GameState(void) = default;
|
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) {
|
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) {
|
switch(_current_screen) {
|
||||||
case Screen::MAIN_MENU:
|
case Screen::MAIN_MENU:
|
||||||
if(_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) {
|
switch(_current_screen) {
|
||||||
case Screen::MAIN_MENU: {
|
case Screen::MAIN_MENU: {
|
||||||
if(!_main_menu) break;
|
if(!_main_menu) break;
|
||||||
@ -221,4 +230,8 @@ void GameState::render(const RenderContext& context) {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(_show_debug_overlay) {
|
||||||
|
_debug_overlay->render(context.ui_renderer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
|
|
||||||
|
class DebugOverlay;
|
||||||
class ClientNetwork;
|
class ClientNetwork;
|
||||||
class Desktop;
|
class Desktop;
|
||||||
class BootSequence;
|
class BootSequence;
|
||||||
@ -26,7 +27,7 @@ public:
|
|||||||
void init(int screen_width, int screen_height);
|
void init(int screen_width, int screen_height);
|
||||||
void start_single_player_now(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 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);
|
void render(const RenderContext& context);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -34,6 +35,8 @@ private:
|
|||||||
std::unique_ptr<Desktop> _desktop;
|
std::unique_ptr<Desktop> _desktop;
|
||||||
std::unique_ptr<BootSequence> _boot_sequence;
|
std::unique_ptr<BootSequence> _boot_sequence;
|
||||||
std::unique_ptr<MainMenu> _main_menu;
|
std::unique_ptr<MainMenu> _main_menu;
|
||||||
|
std::unique_ptr<DebugOverlay> _debug_overlay;
|
||||||
|
bool _show_debug_overlay;
|
||||||
Screen _current_screen;
|
Screen _current_screen;
|
||||||
int _screen_width;
|
int _screen_width;
|
||||||
int _screen_height;
|
int _screen_height;
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
|
||||||
#include "shape_renderer.h"
|
#include "shape_renderer.h"
|
||||||
|
#include "debug/debug_stats.h"
|
||||||
#include "math/math.h"
|
#include "math/math.h"
|
||||||
|
|
||||||
ShapeRenderer::ShapeRenderer(unsigned int screen_width, unsigned int screen_height) {
|
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->use();
|
||||||
_shape_shader->set_mat4("projection", _projection);
|
_shape_shader->set_mat4("projection", _projection);
|
||||||
|
|
||||||
/* Configure VAO/VBO. */
|
/* Configure VAO/VBO for batch rendering. */
|
||||||
glGenVertexArrays(1, &_vao);
|
glGenVertexArrays(1, &_vao);
|
||||||
glGenBuffers(1, &_vbo);
|
glGenBuffers(1, &_vbo);
|
||||||
glBindVertexArray(_vao);
|
glBindVertexArray(_vao);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
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);
|
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);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
|
|
||||||
|
_vertices.reserve(MAX_SHAPE_VERTICES);
|
||||||
}
|
}
|
||||||
|
|
||||||
ShapeRenderer::~ShapeRenderer(void) {
|
ShapeRenderer::~ShapeRenderer(void) {
|
||||||
delete _shape_shader;
|
delete _shape_shader;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeRenderer::draw_rect(int x, int y, int width, int height, const Color& color) {
|
void ShapeRenderer::begin(void) {
|
||||||
_shape_shader->use();
|
_vertices.clear();
|
||||||
_shape_shader->set_vec3("objectColor", color.r, color.g, color.b);
|
}
|
||||||
|
|
||||||
|
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 x_f = (float)x;
|
||||||
float y_f = (float)y;
|
float y_f = (float)y;
|
||||||
float w_f = (float)width;
|
float w_f = (float)width;
|
||||||
float h_f = (float)height;
|
float h_f = (float)height;
|
||||||
|
|
||||||
/* This is ugly :) */
|
_vertices.push_back({x_f, y_f+h_f, color.r, color.g, color.b});
|
||||||
float vertices[6][2] = {
|
_vertices.push_back({x_f, y_f, color.r, color.g, color.b});
|
||||||
{ x_f, y_f + h_f },
|
_vertices.push_back({x_f+w_f, y_f, color.r, color.g, color.b});
|
||||||
{ x_f, y_f },
|
|
||||||
{ x_f + w_f, y_f },
|
|
||||||
|
|
||||||
{ x_f, y_f + h_f },
|
_vertices.push_back({x_f, y_f+h_f, color.r, color.g, color.b});
|
||||||
{ x_f + w_f, y_f },
|
_vertices.push_back({x_f+w_f, y_f, color.r, color.g, color.b});
|
||||||
{x_f + w_f, y_f + h_f }
|
_vertices.push_back({x_f+w_f, y_f+h_f, color.r, color.g, color.b});
|
||||||
};
|
|
||||||
|
|
||||||
glBindVertexArray(_vao);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ShapeRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3,
|
void ShapeRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3,
|
||||||
int y3, const Color& color) {
|
int y3, const Color& color) {
|
||||||
_shape_shader->use();
|
_vertices.push_back({(float)x1, (float)y1, color.r, color.g, color.b});
|
||||||
_shape_shader->set_vec3("objectColor", 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});
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,26 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "shader.h"
|
#include "shader.h"
|
||||||
#include "types.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 {
|
class ShapeRenderer {
|
||||||
public:
|
public:
|
||||||
ShapeRenderer(unsigned int screen_width, unsigned int screen_height);
|
ShapeRenderer(unsigned int screen_width, unsigned int screen_height);
|
||||||
~ShapeRenderer(void);
|
~ShapeRenderer(void);
|
||||||
|
|
||||||
|
void begin(void);
|
||||||
|
void flush(void);
|
||||||
|
|
||||||
void draw_rect(int x, int y, int width, int height, const Color& color);
|
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,
|
void draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3,
|
||||||
const Color& color);
|
const Color& color);
|
||||||
@ -15,5 +28,6 @@ public:
|
|||||||
private:
|
private:
|
||||||
Shader* _shape_shader;
|
Shader* _shape_shader;
|
||||||
unsigned int _vao, _vbo;
|
unsigned int _vao, _vbo;
|
||||||
|
std::vector<ShapeVertex> _vertices;
|
||||||
float _projection[16];
|
float _projection[16];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include <freetype/freetype.h>
|
#include <freetype/freetype.h>
|
||||||
|
|
||||||
#include "txt_renderer.h"
|
#include "txt_renderer.h"
|
||||||
|
#include "debug/debug_stats.h"
|
||||||
#include "math/math.h"
|
#include "math/math.h"
|
||||||
|
|
||||||
TextRenderer::TextRenderer(unsigned int screen_width, unsigned int screen_height) {
|
TextRenderer::TextRenderer(unsigned int screen_width, unsigned int screen_height) {
|
||||||
@ -141,6 +142,9 @@ void TextRenderer::flush(void) {
|
|||||||
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
||||||
glBufferSubData(GL_ARRAY_BUFFER, 0, _vertices.size() * sizeof(TextVertex), _vertices.data());
|
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());
|
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
|
||||||
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
#include "ui/ui_renderer.h"
|
#include "ui/ui_renderer.h"
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
#include "ui/cursor_manager.h"
|
#include "ui/cursor_manager.h"
|
||||||
|
#include "debug/debug_stats.h"
|
||||||
|
|
||||||
const int SCREEN_WIDTH = 1280;
|
const int SCREEN_WIDTH = 1280;
|
||||||
const int SCREEN_HEIGHT = 720;
|
const int SCREEN_HEIGHT = 720;
|
||||||
@ -101,6 +102,9 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
bool running = true;
|
bool running = true;
|
||||||
while(running) {
|
while(running) {
|
||||||
|
/* Reset per-frame stats. */
|
||||||
|
DebugStats::reset();
|
||||||
|
|
||||||
/* Event handling. */
|
/* Event handling. */
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while(SDL_PollEvent(&event)) {
|
while(SDL_PollEvent(&event)) {
|
||||||
@ -116,8 +120,6 @@ int main(int argc, char** argv) {
|
|||||||
/* Clamp dt to avoid large jumps. */
|
/* Clamp dt to avoid large jumps. */
|
||||||
if(dt > 0.1f) dt = 0.1f;
|
if(dt > 0.1f) dt = 0.1f;
|
||||||
|
|
||||||
game_state->update(dt);
|
|
||||||
|
|
||||||
Uint32 current_time = SDL_GetTicks();
|
Uint32 current_time = SDL_GetTicks();
|
||||||
if(current_time - last_blink_time > 500) { /* Every 500ms. */
|
if(current_time - last_blink_time > 500) { /* Every 500ms. */
|
||||||
show_cursor = !show_cursor;
|
show_cursor = !show_cursor;
|
||||||
@ -135,6 +137,13 @@ int main(int argc, char** argv) {
|
|||||||
};
|
};
|
||||||
game_state->render(context);
|
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. */
|
/* It's really odd to call it SwapWindow now, rather than SwapBuffer. */
|
||||||
SDL_GL_SwapWindow(window);
|
SDL_GL_SwapWindow(window);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -60,9 +60,10 @@ Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) {
|
|||||||
Desktop::~Desktop(void) {}
|
Desktop::~Desktop(void) {}
|
||||||
|
|
||||||
void Desktop::add_window(std::unique_ptr<UIWindow> window) {
|
void Desktop::add_window(std::unique_ptr<UIWindow> window) {
|
||||||
|
UIWindow* window_ptr = window.get();
|
||||||
_windows.push_back(std::move(window));
|
_windows.push_back(std::move(window));
|
||||||
/* Focus new window. */
|
_taskbar->add_window(window_ptr);
|
||||||
_set_focused_window(_windows.back().get());
|
_set_focused_window(window_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Desktop::_set_focused_window(UIWindow* window) {
|
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) {
|
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) {
|
if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
|
||||||
int mouse_x = event->button.x;
|
int mouse_x = event->button.x;
|
||||||
int mouse_y = event->button.y;
|
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. */
|
/* When launcher open, check for outside click to close it. */
|
||||||
if(_launcher_is_open && !_launcher->is_point_inside(mouse_x, mouse_y, screen_height)) {
|
if(_launcher_is_open && !_launcher->is_point_inside(mouse_x, mouse_y, screen_height)) {
|
||||||
_launcher_is_open = false;
|
_launcher_is_open = false;
|
||||||
return;
|
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;
|
bool click_on_window = false;
|
||||||
for(int i = _windows.size()-1; i >= 0; --i) {
|
for(int i = _windows.size()-1; i >= 0; --i) {
|
||||||
if(_windows[i].get()->is_point_inside(mouse_x, mouse_y)) {
|
if(_windows[i].get()->is_point_inside(mouse_x, mouse_y)) {
|
||||||
if(!_windows[i]->is_minimized()) {
|
/* Top most window. Focus it. */
|
||||||
_set_focused_window(_windows[i].get());
|
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;
|
click_on_window = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* Click wasn't on a window. Unfocus. */
|
||||||
if(!click_on_window) {
|
if(!click_on_window) {
|
||||||
_set_focused_window(nullptr);
|
_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;
|
_launcher_is_open = false;
|
||||||
}
|
}
|
||||||
} else {
|
} 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) {
|
||||||
if(clicked_window == _focused_window && !clicked_window->is_minimized()) {
|
if(clicked_window == _focused_window && !clicked_window->is_minimized()) {
|
||||||
clicked_window->minimize();
|
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);
|
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) {
|
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(),
|
_windows.erase(std::remove_if(_windows.begin(), _windows.end(),
|
||||||
[this](const std::unique_ptr<UIWindow>& w) {
|
[this](const std::unique_ptr<UIWindow>& w) {
|
||||||
if (w->should_close()) {
|
if (w->should_close()) {
|
||||||
|
_taskbar->remove_window(w.get());
|
||||||
if (w.get() == _focused_window) {
|
if (w.get() == _focused_window) {
|
||||||
_focused_window = nullptr;
|
_focused_window = nullptr;
|
||||||
}
|
}
|
||||||
@ -243,7 +256,7 @@ void Desktop::render(const RenderContext& context) {
|
|||||||
if(_launcher_is_open) {
|
if(_launcher_is_open) {
|
||||||
_launcher->render(context.ui_renderer);
|
_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();
|
context.ui_renderer->flush_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,9 @@ void Launcher::render(UIRenderer* ui_renderer) {
|
|||||||
const Color text_color = { 0.9f, 0.9f, 0.9f };
|
const Color text_color = { 0.9f, 0.9f, 0.9f };
|
||||||
const Color hover_color = { 0.3f, 0.32f, 0.34f };
|
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. */
|
/* Note: y-coord is TOP of launcher menu. */
|
||||||
ui_renderer->draw_rect(_x, _y, _width, _height, bg_color);
|
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);
|
ui_renderer->render_text(app_name.c_str(), _x+10, item_y+20, text_color);
|
||||||
item_y += item_height;
|
item_y += item_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui_renderer->flush_shapes();
|
||||||
|
ui_renderer->flush_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Launcher::handle_event(SDL_Event* event, int screen_height) {
|
std::string Launcher::handle_event(SDL_Event* event, int screen_height) {
|
||||||
|
|||||||
@ -101,6 +101,7 @@ void MainMenu::render(UIRenderer* ui_renderer) {
|
|||||||
ui_renderer->flush_text();
|
ui_renderer->flush_text();
|
||||||
|
|
||||||
/* Pass 2: Buttons. */
|
/* Pass 2: Buttons. */
|
||||||
|
ui_renderer->begin_shapes();
|
||||||
ui_renderer->begin_text();
|
ui_renderer->begin_text();
|
||||||
|
|
||||||
/* Button colours. */
|
/* 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->render_text(button.label.c_str(), text_x, button.rect.y + 32, text_color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui_renderer->flush_shapes();
|
||||||
ui_renderer->flush_text();
|
ui_renderer->flush_text();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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) {
|
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 bg_color = { 0.15f, 0.17f, 0.19f };
|
||||||
const Color text_color = {0.9f, 0.9f, 0.9f};
|
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);
|
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;
|
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 ) {
|
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 bg_color = { 0.15f, 0.17f, 0.19f };
|
||||||
const Color text_color = { 0.9f, 0.9f, 0.9f };
|
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 menu_x = x + (_open_menu_index*60);
|
||||||
int item_y = y + _height;
|
int item_y = y + _height;
|
||||||
int item_height = 30;
|
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);
|
ui_renderer->render_text(item.label.c_str(), menu_x+10, item_y+20, text_color);
|
||||||
item_y += item_height;
|
item_y += item_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ui_renderer->flush_shapes();
|
||||||
|
ui_renderer->flush_text();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
#include "taskbar.h"
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
|
#include "taskbar.h"
|
||||||
#include "ui/ui_renderer.h"
|
#include "ui/ui_renderer.h"
|
||||||
#include "ui/ui_window.h"
|
#include "ui/ui_window.h"
|
||||||
|
|
||||||
@ -17,15 +18,28 @@ Taskbar::Taskbar(int screen_width, int screen_height) {
|
|||||||
|
|
||||||
Taskbar::~Taskbar(void) {}
|
Taskbar::~Taskbar(void) {}
|
||||||
|
|
||||||
void Taskbar::render(UIRenderer* ui_renderer,
|
void Taskbar::add_window(UIWindow* window) {
|
||||||
const std::vector<std::unique_ptr<UIWindow>>& windows,
|
_buttons.push_back({window, window->get_title()});
|
||||||
UIWindow* focused_window) {
|
}
|
||||||
|
|
||||||
|
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 taskbar_color = { 0.1f, 0.12f, 0.14f };
|
||||||
const Color button_color = { 0.2f, 0.22f, 0.24f };
|
const Color button_color = { 0.2f, 0.22f, 0.24f };
|
||||||
const Color button_focused_color = { 0.3f, 0.32f, 0.34f };
|
const Color button_focused_color = { 0.3f, 0.32f, 0.34f };
|
||||||
const Color button_text_color = { 0.9f, 0.9f, 0.9f };
|
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);
|
ui_renderer->draw_rect(0, _y_pos, _width, _height, taskbar_color);
|
||||||
|
|
||||||
/* Draw start button. */
|
/* Draw start button. */
|
||||||
@ -38,14 +52,14 @@ void Taskbar::render(UIRenderer* ui_renderer,
|
|||||||
int padding = 5;
|
int padding = 5;
|
||||||
int x_offset = _start_button_width + padding;
|
int x_offset = _start_button_width + padding;
|
||||||
|
|
||||||
for(const auto& window : windows) {
|
for(const auto& button: _buttons) {
|
||||||
bool is_focused = (window.get() == focused_window);
|
bool is_focused = (button.window == focused_window);
|
||||||
ui_renderer->draw_rect(x_offset, _y_pos + padding, button_width,
|
ui_renderer->draw_rect(x_offset, _y_pos + padding, button_width,
|
||||||
_height - (padding * 2),
|
_height - (padding * 2),
|
||||||
is_focused ? button_focused_color : button_color);
|
is_focused ? button_focused_color : button_color);
|
||||||
|
|
||||||
/* TODO: Truncate text when too long. */
|
/* 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);
|
_y_pos + 20, button_text_color);
|
||||||
x_offset += button_width + padding;
|
x_offset += button_width + padding;
|
||||||
}
|
}
|
||||||
@ -57,10 +71,12 @@ void Taskbar::render(UIRenderer* ui_renderer,
|
|||||||
std::string time_str = ss.str();
|
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,
|
UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height) {
|
||||||
const std::vector<std::unique_ptr<UIWindow>>& windows) {
|
|
||||||
if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||||
int mouse_x = event->button.x;
|
int mouse_x = event->button.x;
|
||||||
int mouse_y = event->button.y;
|
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 button_width = 150;
|
||||||
int padding = 5;
|
int padding = 5;
|
||||||
int x_offset = _start_button_width + padding;
|
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 &&
|
if(mouse_x >= x_offset && mouse_x <= x_offset + button_width &&
|
||||||
mouse_y >= _y_pos && mouse_y <= _y_pos + _height) {
|
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;
|
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 {
|
int Taskbar::get_height(void) const {
|
||||||
return _height;
|
return _height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Taskbar::is_point_inside(int x, int y) const {
|
||||||
|
return (y >= _y_pos && y<= _y_pos + _height);
|
||||||
|
}
|
||||||
|
|||||||
@ -1,27 +1,33 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL3/SDL_events.h>
|
#include <SDL3/SDL_events.h>
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
class UIWindow;
|
class UIWindow;
|
||||||
class UIRenderer;
|
class UIRenderer;
|
||||||
|
|
||||||
|
struct TaskbarButton {
|
||||||
|
UIWindow* window;
|
||||||
|
std::string title;
|
||||||
|
};
|
||||||
|
|
||||||
class Taskbar {
|
class Taskbar {
|
||||||
public:
|
public:
|
||||||
Taskbar(int screen_width, int screen_height);
|
Taskbar(int screen_width, int screen_height);
|
||||||
~Taskbar(void);
|
~Taskbar(void);
|
||||||
|
|
||||||
void render(UIRenderer* ui_renderer,
|
void add_window(UIWindow* window);
|
||||||
const std::vector<std::unique_ptr<UIWindow>>& windows,
|
void remove_window(UIWindow* window);
|
||||||
UIWindow* focused_window);
|
|
||||||
|
|
||||||
UIWindow* handle_event(SDL_Event* event, int screen_height,
|
void render(UIRenderer* ui_renderer, UIWindow* focused_window);
|
||||||
const std::vector<std::unique_ptr<UIWindow>>& windows);
|
UIWindow* handle_event(SDL_Event* event, int screen_height);
|
||||||
bool is_start_button_clicked(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;
|
int get_height(void) const;
|
||||||
private:
|
private:
|
||||||
|
std::vector<TaskbarButton> _buttons;
|
||||||
int _width;
|
int _width;
|
||||||
int _height;
|
int _height;
|
||||||
int _y_pos;
|
int _y_pos;
|
||||||
|
|||||||
@ -38,6 +38,16 @@ void UIRenderer::flush_text(void) {
|
|||||||
_txt_renderer->flush();
|
_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) {
|
TextRenderer* UIRenderer::get_text_renderer(void) {
|
||||||
return _txt_renderer;
|
return _txt_renderer;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,9 @@ public:
|
|||||||
void begin_text(void);
|
void begin_text(void);
|
||||||
void flush_text(void);
|
void flush_text(void);
|
||||||
|
|
||||||
|
void begin_shapes(void);
|
||||||
|
void flush_shapes(void);
|
||||||
|
|
||||||
/* Expose underlying text renderer for things like width calculation. */
|
/* Expose underlying text renderer for things like width calculation. */
|
||||||
TextRenderer* get_text_renderer(void);
|
TextRenderer* get_text_renderer(void);
|
||||||
|
|
||||||
|
|||||||
@ -83,6 +83,7 @@ bool UIWindow::is_point_inside(int x, int y) {
|
|||||||
void UIWindow::render(const RenderContext& context) {
|
void UIWindow::render(const RenderContext& context) {
|
||||||
int title_bar_height = 30;
|
int title_bar_height = 30;
|
||||||
|
|
||||||
|
context.ui_renderer->begin_shapes();
|
||||||
context.ui_renderer->begin_text();
|
context.ui_renderer->begin_text();
|
||||||
|
|
||||||
/* Define colours. */
|
/* 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,
|
context.ui_renderer->draw_triangle(corner_x, corner_y - 10, corner_x - 10,
|
||||||
corner_y, corner_x, corner_y, resize_handle_color);
|
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();
|
context.ui_renderer->flush_text();
|
||||||
|
|
||||||
if(_content) {
|
if(_content) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user