[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,18 +27,20 @@ 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:
 | 
				
			||||||
  std::unique_ptr<ClientNetwork> _network;
 | 
					  std::unique_ptr<ClientNetwork>  _network;
 | 
				
			||||||
  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;
 | 
				
			||||||
  Screen                         _current_screen;
 | 
					  std::unique_ptr<DebugOverlay>   _debug_overlay;
 | 
				
			||||||
  int                            _screen_width;
 | 
					  bool                            _show_debug_overlay;
 | 
				
			||||||
  int                            _screen_height;
 | 
					  Screen                          _current_screen;
 | 
				
			||||||
  bool                           _is_single_player;
 | 
					  int                             _screen_width;
 | 
				
			||||||
 | 
					  int                             _screen_height;
 | 
				
			||||||
 | 
					  bool                            _is_single_player;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void _init_desktop(void);
 | 
					  void _init_desktop(void);
 | 
				
			||||||
  void _run_server(void);
 | 
					  void _run_server(void);
 | 
				
			||||||
 | 
				
			|||||||
@ -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,29 +52,31 @@ 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;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    /* Draw clock. */
 | 
					  /* Draw clock. */
 | 
				
			||||||
    auto now = std::chrono::system_clock::now();
 | 
					  auto now = std::chrono::system_clock::now();
 | 
				
			||||||
    auto in_time_t = std::chrono::system_clock::to_time_t(now);
 | 
					  auto in_time_t = std::chrono::system_clock::to_time_t(now);
 | 
				
			||||||
    std::stringstream ss;
 | 
					  std::stringstream ss;
 | 
				
			||||||
    ss << std::put_time(std::localtime(&in_time_t), "%H:%M");
 | 
					  ss << std::put_time(std::localtime(&in_time_t), "%H:%M");
 | 
				
			||||||
    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