[Refactor] Created UIRenderer for consistant coord system.
The UI codebase was suffering with complexity due to the need to manually convert between two different coordinate systems: - Top-down "screen coordinates" used by SDL for input and windowing. - Bottom-up "GL coordinates" used by low-level renderers. This was making layout calculations diffucult and is bug prone. With this commit, I'm introducing a 'UIRenderer' abstraction layer that wraps the low-level 'ShapeRenderer' and 'TextRenderer'. This is responsible for centralising all coordinate system conversations. All UI components has been refactored to use the 'UIRenderer' so the entire UI code opeates exclusively in a single, top-down screen coordinate system as one would expect.
This commit is contained in:
		
							parent
							
								
									601bc86bdc
								
							
						
					
					
						commit
						e7607e3fc0
					
				@ -197,12 +197,12 @@ void GameState::render(const RenderContext& context) {
 | 
			
		||||
  switch(_current_screen) {
 | 
			
		||||
  case Screen::MAIN_MENU:
 | 
			
		||||
    if(_main_menu) {
 | 
			
		||||
      _main_menu->render(context.shape_renderer, context.txt_renderer);
 | 
			
		||||
      _main_menu->render(context.ui_renderer);
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  case Screen::BOOTING:
 | 
			
		||||
    if(_boot_sequence) {
 | 
			
		||||
      _boot_sequence->render(context.txt_renderer, context.screen_height);
 | 
			
		||||
      _boot_sequence->render(context.ui_renderer);
 | 
			
		||||
    }
 | 
			
		||||
    break;
 | 
			
		||||
  case Screen::DESKTOP:
 | 
			
		||||
 | 
			
		||||
@ -1,7 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
class ShapeRenderer;
 | 
			
		||||
class TextRenderer;
 | 
			
		||||
class UIRenderer;
 | 
			
		||||
 | 
			
		||||
struct Color {
 | 
			
		||||
  float r, g, b;
 | 
			
		||||
@ -12,8 +11,7 @@ struct Rect {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct RenderContext {
 | 
			
		||||
  ShapeRenderer* shape_renderer;
 | 
			
		||||
  TextRenderer* txt_renderer;
 | 
			
		||||
  UIRenderer* ui_renderer;
 | 
			
		||||
  int screen_height;
 | 
			
		||||
  bool show_cursor;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "game_state.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/cursor_manager.h"
 | 
			
		||||
 | 
			
		||||
@ -78,6 +79,10 @@ int main(int argc, char** argv) {
 | 
			
		||||
  /* Init shape renderer. */
 | 
			
		||||
  ShapeRenderer* shape_renderer_instance = new ShapeRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
			
		||||
 | 
			
		||||
  /* Init UI renderer. */
 | 
			
		||||
  UIRenderer* ui_renderer_instance = new UIRenderer(shape_renderer_instance,
 | 
			
		||||
                                                    txt_render_instance, SCREEN_HEIGHT);
 | 
			
		||||
 | 
			
		||||
  auto game_state = std::make_unique<GameState>();
 | 
			
		||||
  if(argc > 1 && std::string(argv[1]) == "-sp") {
 | 
			
		||||
    game_state->start_single_player_now(SCREEN_WIDTH, SCREEN_HEIGHT);
 | 
			
		||||
@ -111,8 +116,7 @@ int main(int argc, char** argv) {
 | 
			
		||||
    glClear(GL_COLOR_BUFFER_BIT);
 | 
			
		||||
 | 
			
		||||
    RenderContext context = {
 | 
			
		||||
      shape_renderer_instance,
 | 
			
		||||
      txt_render_instance,
 | 
			
		||||
      ui_renderer_instance,
 | 
			
		||||
      SCREEN_HEIGHT,
 | 
			
		||||
      show_cursor
 | 
			
		||||
    };
 | 
			
		||||
@ -124,6 +128,7 @@ int main(int argc, char** argv) {
 | 
			
		||||
 | 
			
		||||
  /* Cleanup. */
 | 
			
		||||
  game_state.reset();
 | 
			
		||||
  delete ui_renderer_instance;
 | 
			
		||||
  delete shape_renderer_instance;
 | 
			
		||||
  delete txt_render_instance;
 | 
			
		||||
  SDL_GL_DestroyContext(context);
 | 
			
		||||
 | 
			
		||||
@ -111,8 +111,8 @@ void Terminal::scroll(int amount, int win_content_height) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Terminal::render(const RenderContext& context, int x, int y, int width, int height) {
 | 
			
		||||
 | 
			
		||||
void Terminal::render(const RenderContext& context, int x, int y_screen, int y_gl,
 | 
			
		||||
                      int width, int height) {
 | 
			
		||||
  const Color white = { 1.0f, 1.0f, 1.0f };
 | 
			
		||||
  const Color green = { 0.2f, 1.0f, 0.2f };
 | 
			
		||||
  float line_height = 20.0f;
 | 
			
		||||
@ -120,27 +120,28 @@ void Terminal::render(const RenderContext& context, int x, int y, int width, int
 | 
			
		||||
 | 
			
		||||
  /* Enable scissor test to clip rendering to the window content area. */
 | 
			
		||||
  glEnable(GL_SCISSOR_TEST);
 | 
			
		||||
  glScissor(x, y, width, height);
 | 
			
		||||
  glScissor(x, y_gl, width, height);
 | 
			
		||||
 | 
			
		||||
  /* Draw History. */
 | 
			
		||||
  for(size_t i = _scroll_offset; i < _history.size(); ++i) {
 | 
			
		||||
    float y_pos = (y+height) - padding - line_height - ((i - _scroll_offset) * line_height);
 | 
			
		||||
    context.txt_renderer->render_text(_history[i].c_str(), x+padding, y_pos, 1.0f, white);
 | 
			
		||||
    float line_y_pos = y_screen + padding + ((i - _scroll_offset) * line_height);
 | 
			
		||||
    context.ui_renderer->render_text(_history[i].c_str(), x+padding, line_y_pos+18, white);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Draw current input line. */
 | 
			
		||||
  float prompt_y_pos = (y+height) - padding - line_height
 | 
			
		||||
    - ((_history.size()-_scroll_offset)*line_height);
 | 
			
		||||
  float prompt_line_y = (y_screen + padding) + ((_history.size() - _scroll_offset) * line_height);
 | 
			
		||||
  float prompt_baseline_y = prompt_line_y + 18;
 | 
			
		||||
 | 
			
		||||
  /* Render prompt string. */
 | 
			
		||||
  std::string prompt_str = _prompt + "> ";
 | 
			
		||||
  context.txt_renderer->render_text(prompt_str.c_str(), x+padding, prompt_y_pos, 1.0f, green);
 | 
			
		||||
  context.ui_renderer->render_text(prompt_str.c_str(), x+padding, prompt_baseline_y, green);
 | 
			
		||||
 | 
			
		||||
  /* Render text view for the input right after prompt. */
 | 
			
		||||
  float input_x_pos = x + padding + (prompt_str.length() * 8.5f); /* Estimate width */
 | 
			
		||||
  float input_width = width - (input_x_pos-x);
 | 
			
		||||
  _input_view->render(context.txt_renderer, input_x_pos, prompt_y_pos, input_width, 
 | 
			
		||||
  _input_view->render(context.ui_renderer, input_x_pos, prompt_line_y, input_width, 
 | 
			
		||||
                      line_height, context.show_cursor);
 | 
			
		||||
 | 
			
		||||
  /* Disable scissor test. */
 | 
			
		||||
  glDisable(GL_SCISSOR_TEST);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,7 +18,8 @@ public:
 | 
			
		||||
 | 
			
		||||
  void update(void) override;
 | 
			
		||||
  void handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) override;
 | 
			
		||||
  void render(const RenderContext& context, int x, int y, int width, int height) override;
 | 
			
		||||
  void render(const RenderContext& context, int x, int y_screen, int y_gl,
 | 
			
		||||
              int width, int height) override;
 | 
			
		||||
  void scroll(int amount, int content_height) override;
 | 
			
		||||
  void add_history(const std::string& line);
 | 
			
		||||
  void set_prompt(const std::string& prompt);
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <cstdio>
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
#include "boot_sequence.h"
 | 
			
		||||
#include <SDL3/SDL_timer.h>
 | 
			
		||||
@ -35,7 +35,7 @@ bool BootSequence::is_finished(void) {
 | 
			
		||||
  return (SDL_GetTicks() - _start_time) >= _total_duration_ms;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void BootSequence::render(TextRenderer* txt_renderer, int screen_height) {
 | 
			
		||||
void BootSequence::render(UIRenderer* ui_renderer) {
 | 
			
		||||
  const Color text_color  = { 0.9f, 0.9f, 0.9f }; /* grey/white */
 | 
			
		||||
  const float line_height = 18.0f;
 | 
			
		||||
  const float padding     = 15.0f;
 | 
			
		||||
@ -48,7 +48,7 @@ void BootSequence::render(TextRenderer* txt_renderer, int screen_height) {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  for(int i = 0; i < lines_to_show; ++i) {
 | 
			
		||||
    float y_pos = (screen_height - padding) - (i * line_height);
 | 
			
		||||
    txt_renderer->render_text(_messages[i].c_str(), padding, y_pos, 1.0f, text_color);
 | 
			
		||||
    float y_pos = padding + (i*line_height);
 | 
			
		||||
    ui_renderer->render_text(_messages[i].c_str(), padding, y_pos, text_color);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,7 +4,7 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
class BootSequence {
 | 
			
		||||
public:
 | 
			
		||||
@ -12,7 +12,7 @@ public:
 | 
			
		||||
  ~BootSequence(void);
 | 
			
		||||
 | 
			
		||||
  bool is_finished(void);
 | 
			
		||||
  void render(TextRenderer* txt_renderer, int screen_height);
 | 
			
		||||
  void render(UIRenderer* ui_renderer);
 | 
			
		||||
private:
 | 
			
		||||
  std::vector<std::string> _messages;
 | 
			
		||||
  Uint32 _start_time;
 | 
			
		||||
 | 
			
		||||
@ -2,21 +2,19 @@
 | 
			
		||||
#include <ctime>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <cmath>
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <ui/cursor_manager.h>
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
#include <SDL3/SDL_video.h>
 | 
			
		||||
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/editor.h"
 | 
			
		||||
#include "desktop.h"
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "terminal.h"
 | 
			
		||||
#include "ui/i_window_content.h"
 | 
			
		||||
#include "ui/launcher.h"
 | 
			
		||||
#include "ui/taskbar.h"
 | 
			
		||||
#include <SDL3/SDL_video.h>
 | 
			
		||||
#include <ui/cursor_manager.h>
 | 
			
		||||
#include "ui/ui_window.h"
 | 
			
		||||
#include "ui/window_action.h"
 | 
			
		||||
 | 
			
		||||
@ -33,7 +31,9 @@ Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) {
 | 
			
		||||
  _network = network;
 | 
			
		||||
  _focused_window = nullptr;
 | 
			
		||||
  _launcher_is_open = false;
 | 
			
		||||
  _launcher = std::make_unique<Launcher>(5, 5 + _taskbar->get_height(), 200);
 | 
			
		||||
  _launcher = std::make_unique<Launcher>(5, 0, 200); /* Tmp y-coord. */
 | 
			
		||||
  int launcher_y = screen_height - _taskbar->get_height() - _launcher->get_height();
 | 
			
		||||
  _launcher->set_y(launcher_y);
 | 
			
		||||
 | 
			
		||||
  /* Load snippets for temp wallpaper. */
 | 
			
		||||
  std::ifstream snippet_file("assets/menu_background_snippets.txt");
 | 
			
		||||
@ -196,11 +196,11 @@ UIWindow* Desktop::get_focused_window(void) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::render(const RenderContext& context) {
 | 
			
		||||
  _render_wallpaper(context.txt_renderer);
 | 
			
		||||
  _render_wallpaper(context.ui_renderer);
 | 
			
		||||
  if(_launcher_is_open) {
 | 
			
		||||
    _launcher->render(context.shape_renderer, context.txt_renderer, context.screen_height);
 | 
			
		||||
    _launcher->render(context.ui_renderer);
 | 
			
		||||
  }
 | 
			
		||||
  _taskbar->render(context.shape_renderer, context.txt_renderer, _windows, _focused_window);
 | 
			
		||||
  _taskbar->render(context.ui_renderer, _windows, _focused_window);
 | 
			
		||||
  /* Render non-focused windows first. */
 | 
			
		||||
  for(const auto& win : _windows) {
 | 
			
		||||
    if(win.get() != _focused_window && !win->is_minimized()) {
 | 
			
		||||
@ -213,11 +213,11 @@ void Desktop::render(const RenderContext& context) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::_render_wallpaper(TextRenderer* txt_renderer) {
 | 
			
		||||
void Desktop::_render_wallpaper(UIRenderer* ui_renderer) {
 | 
			
		||||
  const Color wallpaper_color = { 0.0f, 0.15f, 0.08f };
 | 
			
		||||
  for(auto& line : _background_text) {
 | 
			
		||||
    txt_renderer->render_text(line.text.c_str(), std::round(line.x),
 | 
			
		||||
                              line.render_y, 1.0f, wallpaper_color);
 | 
			
		||||
    ui_renderer->render_text(line.text.c_str(), std::round(line.x),
 | 
			
		||||
                              line.render_y, wallpaper_color);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -5,11 +5,11 @@
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/ui_window.h"
 | 
			
		||||
#include "ui/taskbar.h"
 | 
			
		||||
#include "ui/launcher.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
/* Animated background stuff. */
 | 
			
		||||
struct ScrollingText {
 | 
			
		||||
@ -34,7 +34,7 @@ public:
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _set_focused_window(UIWindow* window);
 | 
			
		||||
  void _render_wallpaper(TextRenderer* txt_renderer);
 | 
			
		||||
  void _render_wallpaper(UIRenderer* ui_renderer);
 | 
			
		||||
  void _update_wallpaper(int screen_width, int screen_height);
 | 
			
		||||
 | 
			
		||||
  std::vector<std::unique_ptr<UIWindow>> _windows;
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@ void Editor::update(void) {
 | 
			
		||||
void Editor::handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
 | 
			
		||||
  if(!event) return;
 | 
			
		||||
 | 
			
		||||
  _menu_bar->handle_event(event, window_x, window_y, window_gl_y);
 | 
			
		||||
  _menu_bar->handle_event(event, window_x, window_y);
 | 
			
		||||
 | 
			
		||||
  /* We don't care about the return val here. RET is just newline. */
 | 
			
		||||
  if(event->type == SDL_EVENT_KEY_DOWN && event->key.key == SDLK_S &&
 | 
			
		||||
@ -46,13 +46,14 @@ void Editor::handle_input(SDL_Event* event, int window_x, int window_y, int wind
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Editor::render(const RenderContext& context, int x, int y, int width, int height) {
 | 
			
		||||
void Editor::render(const RenderContext& context, int x, int y_screen, int y_gl,
 | 
			
		||||
                    int width, int height) {
 | 
			
		||||
  int menu_bar_height = _menu_bar->get_height();
 | 
			
		||||
  int menu_bar_y_gl = y + height - menu_bar_height;
 | 
			
		||||
  int content_y = y_screen + menu_bar_height;
 | 
			
		||||
  int content_height = height - menu_bar_height;
 | 
			
		||||
 | 
			
		||||
  _view->render(context.txt_renderer, x, y, width, height - menu_bar_height,
 | 
			
		||||
                context.show_cursor);
 | 
			
		||||
  _menu_bar->render(context.shape_renderer, context.txt_renderer, x, menu_bar_y_gl, width);
 | 
			
		||||
  _view->render(context.ui_renderer, x, content_y, width, content_height, context.show_cursor);
 | 
			
		||||
  _menu_bar->render(context.ui_renderer, x, y_screen, width);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Editor::scroll(int amount, int content_height) {
 | 
			
		||||
 | 
			
		||||
@ -17,7 +17,8 @@ public:
 | 
			
		||||
 | 
			
		||||
  void update(void) override;
 | 
			
		||||
  void handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) override;
 | 
			
		||||
  void render(const RenderContext& context, int x, int y, int width, int height) override;
 | 
			
		||||
  void render(const RenderContext& context, int x, int y_screen, int y_gl,
 | 
			
		||||
              int width, int height) override;
 | 
			
		||||
  void scroll(int amount, int content_height) override;
 | 
			
		||||
  bool should_close(void) override;
 | 
			
		||||
  void set_buffer_content(const std::string& content);
 | 
			
		||||
 | 
			
		||||
@ -10,7 +10,8 @@ public:
 | 
			
		||||
  virtual ~IWindowContent(void) = default;
 | 
			
		||||
  virtual void update(void) = 0;
 | 
			
		||||
  virtual void handle_input(SDL_Event* event, int window_x, int window_y, int window_gl_y) = 0;
 | 
			
		||||
  virtual void render(const RenderContext& context, int x, int y, int width, int height) = 0;
 | 
			
		||||
  virtual void render(const RenderContext& context, int x, int y_screen, int y_gl,
 | 
			
		||||
                      int width, int height) = 0;
 | 
			
		||||
  virtual void scroll(int amount, int content_height) = 0;
 | 
			
		||||
  virtual bool should_close(void) = 0;
 | 
			
		||||
  virtual WindowAction get_pending_action() = 0;
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,6 @@
 | 
			
		||||
#include "launcher.h"
 | 
			
		||||
#include <SDL3/SDL_mouse.h>
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
Launcher::Launcher(int x, int y, int width) : _x(x), _y(y), _width(width) {
 | 
			
		||||
  /* TODO: Hardcode the launcher apps for now. */
 | 
			
		||||
@ -16,34 +14,31 @@ Launcher::Launcher(int x, int y, int width) : _x(x), _y(y), _width(width) {
 | 
			
		||||
Launcher::~Launcher(void) {}
 | 
			
		||||
 | 
			
		||||
bool Launcher::is_point_inside(int x, int y, int screen_height) const {
 | 
			
		||||
  int ui_y = screen_height - y; /* convert mouse y to GL/UI coords. */
 | 
			
		||||
  return (x >= _x && x <= _x + _width && ui_y >= _y && ui_y <= _y + _height);
 | 
			
		||||
  return (x >= _x && x <= _x + _width && y>= _y && y <= _y + _height);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Launcher::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
                      int screen_height) {
 | 
			
		||||
void Launcher::render(UIRenderer* ui_renderer) {
 | 
			
		||||
  const Color bg_color    = { 0.15f, 0.17f, 0.19f };
 | 
			
		||||
  const Color text_color  = { 0.9f,  0.9f,  0.9f  };
 | 
			
		||||
  const Color hover_color = { 0.3f,  0.32f, 0.34f };
 | 
			
		||||
 | 
			
		||||
  /* Note: y-coord is TOP of launcher menu. */
 | 
			
		||||
  shape_renderer->draw_rect(_x, _y, _width, _height, bg_color);
 | 
			
		||||
  ui_renderer->draw_rect(_x, _y, _width, _height, bg_color);
 | 
			
		||||
 | 
			
		||||
  int item_height = 30;
 | 
			
		||||
  int item_y = _y;
 | 
			
		||||
 | 
			
		||||
  float mouse_x, mouse_y;
 | 
			
		||||
  SDL_GetMouseState(&mouse_x, &mouse_y);
 | 
			
		||||
  int ui_mouse_y = screen_height - mouse_y;
 | 
			
		||||
 | 
			
		||||
  for(const auto& app_name : _apps) {
 | 
			
		||||
    /* Check for hover. */
 | 
			
		||||
    if(mouse_x >= _x && mouse_x <= _x + _width &&
 | 
			
		||||
       ui_mouse_y >= item_y && ui_mouse_y <= item_y + item_height) {
 | 
			
		||||
      shape_renderer->draw_rect(_x, item_y, _width, item_height, hover_color);
 | 
			
		||||
       mouse_y >= item_y && mouse_y <= item_y + item_height) {
 | 
			
		||||
      ui_renderer->draw_rect(_x, item_y, _width, item_height, hover_color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    txt_renderer->render_text(app_name.c_str(), _x + 10, item_y + 8, 1.0f, text_color);
 | 
			
		||||
    ui_renderer->render_text(app_name.c_str(), _x+10, item_y+20, text_color);
 | 
			
		||||
    item_y += item_height;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -51,13 +46,13 @@ void Launcher::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
std::string Launcher::handle_event(SDL_Event* event, int screen_height) {
 | 
			
		||||
  if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
 | 
			
		||||
    int mouse_x = event->button.x;
 | 
			
		||||
    int ui_mouse_y = screen_height - event->button.y;
 | 
			
		||||
    int mouse_y = event->button.y;
 | 
			
		||||
 | 
			
		||||
    int item_height = 30;
 | 
			
		||||
    int item_y = _y;
 | 
			
		||||
    for(const auto& app_name : _apps) {
 | 
			
		||||
      if(mouse_x >= _x && mouse_x <= _x + _width &&
 | 
			
		||||
         ui_mouse_y >= item_y && ui_mouse_y <= item_y + item_height) {
 | 
			
		||||
         mouse_y >= item_y && mouse_y <= item_y + item_height) {
 | 
			
		||||
        return app_name; /* Return name of clicked app. */
 | 
			
		||||
      }
 | 
			
		||||
      item_y += item_height;
 | 
			
		||||
@ -65,3 +60,11 @@ std::string Launcher::handle_event(SDL_Event* event, int screen_height) {
 | 
			
		||||
  }
 | 
			
		||||
  return ""; /* Nothing clicked. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Launcher::set_y(int y) {
 | 
			
		||||
  _y = y;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Launcher::get_height(void) const {
 | 
			
		||||
  return _height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -4,17 +4,18 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
 | 
			
		||||
class ShapeRenderer;
 | 
			
		||||
class TextRenderer;
 | 
			
		||||
class UIRenderer;
 | 
			
		||||
 | 
			
		||||
class Launcher {
 | 
			
		||||
public:
 | 
			
		||||
  Launcher(int x, int y, int width);
 | 
			
		||||
  ~Launcher(void);
 | 
			
		||||
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height);
 | 
			
		||||
  void render(UIRenderer* ui_renderer);
 | 
			
		||||
  std::string handle_event(SDL_Event* event, int screen_height);
 | 
			
		||||
  bool is_point_inside(int x, int y, int screen_height) const;
 | 
			
		||||
  void set_y(int y);
 | 
			
		||||
  int get_height(void) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  int _x, _y, _width, _height;
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,7 @@
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
#include "main_menu.h"
 | 
			
		||||
 | 
			
		||||
@ -73,12 +71,10 @@ void MainMenu::handle_event(SDL_Event* event) {
 | 
			
		||||
    int mouse_x = event->motion.x;
 | 
			
		||||
    int mouse_y = event->motion.y;
 | 
			
		||||
    for(auto& button : _buttons) {
 | 
			
		||||
      /* Invert mouse_y for UI coordinate system. */
 | 
			
		||||
      int inverted_y = _screen_height - mouse_y;
 | 
			
		||||
      button.is_hovered = (mouse_x >= button.rect.x
 | 
			
		||||
        && mouse_x <= button.rect.x + button.rect.w
 | 
			
		||||
        && inverted_y >= button.rect.y
 | 
			
		||||
        && inverted_y <= button.rect.y + button.rect.h);
 | 
			
		||||
        && mouse_y >= button.rect.y
 | 
			
		||||
        && mouse_y <= button.rect.y + button.rect.h);
 | 
			
		||||
    }
 | 
			
		||||
  } else if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
 | 
			
		||||
    for(const auto& button : _buttons) {
 | 
			
		||||
@ -95,8 +91,8 @@ Screen MainMenu::update(void) {
 | 
			
		||||
  return _next_screen;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MainMenu::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer) {
 | 
			
		||||
  _render_background(txt_renderer);
 | 
			
		||||
void MainMenu::render(UIRenderer* ui_renderer) {
 | 
			
		||||
  _render_background(ui_renderer->get_text_renderer());
 | 
			
		||||
 | 
			
		||||
  /* Button colours. */
 | 
			
		||||
  const Color button_color        = { 0.1f, 0.15f, 0.2f };
 | 
			
		||||
@ -106,18 +102,17 @@ void MainMenu::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer)
 | 
			
		||||
  for(const auto& button : _buttons) {
 | 
			
		||||
    /* Draw button background. */
 | 
			
		||||
    if(button.is_hovered) {
 | 
			
		||||
      shape_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w,
 | 
			
		||||
      ui_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w,
 | 
			
		||||
                                button.rect.h, button_hover_color);
 | 
			
		||||
    } else {
 | 
			
		||||
      shape_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w,
 | 
			
		||||
      ui_renderer->draw_rect(button.rect.x, button.rect.y, button.rect.w,
 | 
			
		||||
                                button.rect.h, button_color);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Draw button text centered. */
 | 
			
		||||
    float text_width = txt_renderer->get_text_width(button.label.c_str(), 1.0f);
 | 
			
		||||
    float text_width = ui_renderer->get_text_renderer()->get_text_width(button.label.c_str(), 1.0f);
 | 
			
		||||
    float text_x = button.rect.x + (button.rect.w - text_width) / 2.0f;
 | 
			
		||||
    txt_renderer->render_text(button.label.c_str(), text_x,
 | 
			
		||||
                              button.rect.y + 18, 1.0f, text_color);
 | 
			
		||||
    ui_renderer->render_text(button.label.c_str(), text_x, button.rect.y + 32, text_color);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -3,9 +3,8 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
#include "game_state.h"
 | 
			
		||||
 | 
			
		||||
union SDL_Event;
 | 
			
		||||
@ -24,7 +23,7 @@ public:
 | 
			
		||||
 | 
			
		||||
  void handle_event(SDL_Event* event);
 | 
			
		||||
  Screen update(void);
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer);
 | 
			
		||||
  void render(UIRenderer* ui_renderer);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  void _update_background(void);
 | 
			
		||||
 | 
			
		||||
@ -26,7 +26,7 @@ int MenuBar::get_height(void) const {
 | 
			
		||||
  return _height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBar::handle_event(SDL_Event* event, int window_x, int window_y, int window_gl_y) {
 | 
			
		||||
void MenuBar::handle_event(SDL_Event* event, int window_x, int window_y) {
 | 
			
		||||
  if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
 | 
			
		||||
    int mouse_x = event->button.x;
 | 
			
		||||
    int mouse_y = event->button.y;
 | 
			
		||||
@ -72,27 +72,26 @@ void MenuBar::handle_event(SDL_Event* event, int window_x, int window_y, int win
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void MenuBar::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
                     int x, int y_gl, int width) {
 | 
			
		||||
void MenuBar::render(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 hover_color = {0.3f, 0.32f, 0.34f};
 | 
			
		||||
 | 
			
		||||
  shape_renderer->draw_rect(x, y_gl, width, _height, bg_color);
 | 
			
		||||
  ui_renderer->draw_rect(x, y, width, _height, bg_color);
 | 
			
		||||
 | 
			
		||||
  int menu_x = x;
 | 
			
		||||
  for(size_t i = 0; i < _menus.size(); ++i) {
 | 
			
		||||
    int menu_width = 60;
 | 
			
		||||
    txt_renderer->render_text(_menus[i].label.c_str(), menu_x+10, y_gl+8, 1.0f, text_color);
 | 
			
		||||
    ui_renderer->render_text(_menus[i].label.c_str(), menu_x+10, y+20, text_color);
 | 
			
		||||
 | 
			
		||||
    if(_open_menu_index == (int)i) {
 | 
			
		||||
      int item_gl_y = y_gl - 30; /* Draw items below the bar. */
 | 
			
		||||
      int item_y = y + _height; /* Draw items below the bar. */
 | 
			
		||||
      int item_height = 30;
 | 
			
		||||
      int dropdown_width = 150;
 | 
			
		||||
      for(const auto& item : _menus[i].items) {
 | 
			
		||||
        shape_renderer->draw_rect(menu_x, item_gl_y, dropdown_width, item_height, bg_color);
 | 
			
		||||
        txt_renderer->render_text(item.label.c_str(), menu_x+10, item_gl_y+8, 1.0f, text_color);
 | 
			
		||||
        item_gl_y -= item_height;
 | 
			
		||||
        ui_renderer->draw_rect(menu_x, item_y, dropdown_width, item_height, bg_color);
 | 
			
		||||
        ui_renderer->render_text(item.label.c_str(), menu_x+10, item_y+20, text_color);
 | 
			
		||||
        item_y += item_height;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    menu_x += menu_width;
 | 
			
		||||
 | 
			
		||||
@ -5,8 +5,7 @@
 | 
			
		||||
#include <functional>
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
struct MenuItem {
 | 
			
		||||
  std::string label;
 | 
			
		||||
@ -28,9 +27,8 @@ public:
 | 
			
		||||
  void add_menu_item(const std::string& menu_label, const std::string& item_label,
 | 
			
		||||
                     std::function<void()> action);
 | 
			
		||||
 | 
			
		||||
  void handle_event(SDL_Event* event, int window_x, int window_y, int window_gl_y);
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int x,
 | 
			
		||||
              int y_gl, int width);
 | 
			
		||||
  void handle_event(SDL_Event* event, int window_x, int window_y);
 | 
			
		||||
  void render(UIRenderer* ui_renderer, int x, int y, int width);
 | 
			
		||||
 | 
			
		||||
  int get_height(void) const;
 | 
			
		||||
private:
 | 
			
		||||
 | 
			
		||||
@ -5,21 +5,19 @@
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
#include "ui/ui_window.h"
 | 
			
		||||
 | 
			
		||||
Taskbar::Taskbar(int screen_width, int screen_height) {
 | 
			
		||||
  _width  = screen_width;
 | 
			
		||||
  _height = 32;
 | 
			
		||||
  _y_pos  = 0; /* Taskbar at bottom because boring? */
 | 
			
		||||
  _y_pos  = screen_height - _height; /* Taskbar at bottom because boring? */
 | 
			
		||||
  _start_button_width = 60;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Taskbar::~Taskbar(void) {}
 | 
			
		||||
 | 
			
		||||
void Taskbar::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
void Taskbar::render(UIRenderer* ui_renderer,
 | 
			
		||||
                     const std::vector<std::unique_ptr<UIWindow>>& windows,
 | 
			
		||||
                     UIWindow* focused_window) {
 | 
			
		||||
 | 
			
		||||
@ -28,13 +26,12 @@ void Taskbar::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
  const Color button_focused_color  = { 0.3f, 0.32f, 0.34f };
 | 
			
		||||
  const Color button_text_color     = { 0.9f, 0.9f,  0.9f  };
 | 
			
		||||
 | 
			
		||||
  shape_renderer->draw_rect(0, _y_pos, _width, _height, taskbar_color);
 | 
			
		||||
  ui_renderer->draw_rect(0, _y_pos, _width, _height, taskbar_color);
 | 
			
		||||
 | 
			
		||||
  /* Draw start button. */
 | 
			
		||||
  shape_renderer->draw_rect(5, _y_pos + 5, _start_button_width - 10,
 | 
			
		||||
  ui_renderer->draw_rect(5, _y_pos + 5, _start_button_width - 10,
 | 
			
		||||
                            _height - 10, button_color);
 | 
			
		||||
  txt_renderer->render_text("[B]", 20, _y_pos + 11, 1.0f,
 | 
			
		||||
                            button_text_color);
 | 
			
		||||
  ui_renderer->render_text("[B]", 20, _y_pos + 20, button_text_color);
 | 
			
		||||
 | 
			
		||||
  /* Draw app buttons. */
 | 
			
		||||
  int button_width  = 150;
 | 
			
		||||
@ -43,13 +40,13 @@ void Taskbar::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
 | 
			
		||||
  for(const auto& window : windows) {
 | 
			
		||||
    bool is_focused = (window.get() == focused_window);
 | 
			
		||||
    shape_renderer->draw_rect(x_offset, _y_pos + padding, button_width,
 | 
			
		||||
    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. */
 | 
			
		||||
    txt_renderer->render_text(window->get_title().c_str(), x_offset + 10,
 | 
			
		||||
                              _y_pos + 11, 1.0f, button_text_color);
 | 
			
		||||
    ui_renderer->render_text(window->get_title().c_str(), x_offset + 10,
 | 
			
		||||
                              _y_pos + 20, button_text_color);
 | 
			
		||||
    x_offset += button_width + padding;
 | 
			
		||||
  }
 | 
			
		||||
    /* Draw clock. */
 | 
			
		||||
@ -59,15 +56,14 @@ void Taskbar::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
    ss << std::put_time(std::localtime(&in_time_t), "%H:%M");
 | 
			
		||||
    std::string time_str = ss.str();
 | 
			
		||||
 | 
			
		||||
    txt_renderer->render_text(time_str.c_str(), _width-50, _y_pos+11, 1.0f,
 | 
			
		||||
                              button_text_color);
 | 
			
		||||
    ui_renderer->render_text(time_str.c_str(), _width-50, _y_pos+20, button_text_color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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) {
 | 
			
		||||
    int mouse_x = event->button.x;
 | 
			
		||||
    int mouse_y = screen_height - event->button.y; /* Convert to UI coords. */
 | 
			
		||||
    int mouse_y = event->button.y;
 | 
			
		||||
 | 
			
		||||
    int button_width  = 150;
 | 
			
		||||
    int padding       = 5;
 | 
			
		||||
@ -86,7 +82,7 @@ UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height,
 | 
			
		||||
bool Taskbar::is_start_button_clicked(SDL_Event* event, int screen_height) {
 | 
			
		||||
  if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
 | 
			
		||||
    int mouse_x = event->button.x;
 | 
			
		||||
    int mouse_y = screen_height - event->button.y;
 | 
			
		||||
    int mouse_y = event->button.y;
 | 
			
		||||
    return (mouse_x >= 0 && mouse_x <= _start_button_width && mouse_y >= _y_pos &&
 | 
			
		||||
            mouse_y <= _y_pos + _height);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -5,15 +5,14 @@
 | 
			
		||||
#include <memory>
 | 
			
		||||
 | 
			
		||||
class UIWindow;
 | 
			
		||||
class ShapeRenderer;
 | 
			
		||||
class TextRenderer;
 | 
			
		||||
class UIRenderer;
 | 
			
		||||
 | 
			
		||||
class Taskbar {
 | 
			
		||||
public:
 | 
			
		||||
  Taskbar(int screen_width, int screen_height);
 | 
			
		||||
  ~Taskbar(void);
 | 
			
		||||
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
  void render(UIRenderer* ui_renderer,
 | 
			
		||||
              const std::vector<std::unique_ptr<UIWindow>>& windows,
 | 
			
		||||
              UIWindow* focused_window);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -70,7 +70,7 @@ void TextView::scroll(int amount, int content_height) {
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TextView::render(TextRenderer* renderer, int x, int y, int width, int height,
 | 
			
		||||
void TextView::render(UIRenderer* ui_renderer, int x, int y, int width, int height,
 | 
			
		||||
                      bool show_cursor) {
 | 
			
		||||
  if(!_buffer) return;
 | 
			
		||||
 | 
			
		||||
@ -79,7 +79,7 @@ void TextView::render(TextRenderer* renderer, int x, int y, int width, int heigh
 | 
			
		||||
  float padding = 5.0f;
 | 
			
		||||
 | 
			
		||||
  Point cursor_pos = _buffer->get_cursor_pos();
 | 
			
		||||
  float current_y = y + height - line_height; /* Start at top. */
 | 
			
		||||
  float current_y = y;
 | 
			
		||||
 | 
			
		||||
  for(size_t i = _scroll_offset; i < _buffer->get_line_count(); ++i) {
 | 
			
		||||
    std::string line = _buffer->get_line(i);
 | 
			
		||||
@ -96,7 +96,8 @@ void TextView::render(TextRenderer* renderer, int x, int y, int width, int heigh
 | 
			
		||||
        line.insert(cursor_pos.col, 1, '_');
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    renderer->render_text(line.c_str(), x, current_y, 1.0f, text_color);
 | 
			
		||||
    current_y -= line_height;
 | 
			
		||||
    /* Add 18 to get baseline from top of the line. */
 | 
			
		||||
    ui_renderer->render_text(line.c_str(), x+padding, current_y + 18, text_color);
 | 
			
		||||
    current_y += line_height;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3,6 +3,7 @@
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
 | 
			
		||||
#include "ui/text_buffer.h"
 | 
			
		||||
#include "ui/ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
class TextRenderer;
 | 
			
		||||
 | 
			
		||||
@ -12,7 +13,7 @@ public:
 | 
			
		||||
  ~TextView(void);
 | 
			
		||||
 | 
			
		||||
  bool handle_event(SDL_Event* event);
 | 
			
		||||
  void render(TextRenderer* renderer, int x, int y, int width, int height,
 | 
			
		||||
  void render(UIRenderer* ui_renderer, int x, int y, int width, int height,
 | 
			
		||||
              bool show_cursor);
 | 
			
		||||
  void scroll(int amount, int content_height);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										33
									
								
								client/src/ui/ui_renderer.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								client/src/ui/ui_renderer.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
			
		||||
#include "ui_renderer.h"
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
 | 
			
		||||
UIRenderer::UIRenderer(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height)
 | 
			
		||||
    : _shape_renderer(shape_renderer), _txt_renderer(txt_renderer), _screen_height(screen_height) {}
 | 
			
		||||
 | 
			
		||||
void UIRenderer::draw_rect(int x, int y, int width, int height, const Color& color) {
 | 
			
		||||
  if(!_shape_renderer) return;
 | 
			
		||||
  /* Convert top-left screen coord to bottom-left GL coord. */
 | 
			
		||||
  int y_gl = _screen_height - y - height;
 | 
			
		||||
  _shape_renderer->draw_rect(x, y_gl, width, height, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UIRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3, int y3, const Color& color) {
 | 
			
		||||
  if(!_shape_renderer) return;
 | 
			
		||||
  /* Convert top-left screen coord to bottom-left GL coord. */
 | 
			
		||||
  int y1_gl = _screen_height - y1;
 | 
			
		||||
  int y2_gl = _screen_height - y2;
 | 
			
		||||
  int y3_gl = _screen_height - y3;
 | 
			
		||||
  _shape_renderer->draw_triangle(x1, y1_gl, x2, y2_gl, x3, y3_gl, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UIRenderer::render_text(const char* text, int x, int y, const Color& color) {
 | 
			
		||||
  if(!_txt_renderer) return;
 | 
			
		||||
  /* Convert the screen-space baseline y-coord to GL-space baseline y-coord. */
 | 
			
		||||
  int y_gl = _screen_height - y;
 | 
			
		||||
  _txt_renderer->render_text(text, x, y_gl, 1.0f, color);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TextRenderer* UIRenderer::get_text_renderer(void) {
 | 
			
		||||
  return _txt_renderer;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										27
									
								
								client/src/ui/ui_renderer.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								client/src/ui/ui_renderer.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * I'm so damn sick of working between two rendering systems!
 | 
			
		||||
 * Here! Have a wrapper around the low-level renderers to provide a f.cking
 | 
			
		||||
 * consistant top-left origin for all UI components.
 | 
			
		||||
 */
 | 
			
		||||
class UIRenderer {
 | 
			
		||||
public:
 | 
			
		||||
  UIRenderer(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
  void render_text(const char* text, int x, int y, const Color& color);
 | 
			
		||||
 | 
			
		||||
  /* Expose underlying text renderer for things like width calculation. */
 | 
			
		||||
  TextRenderer* get_text_renderer(void);
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  ShapeRenderer*  _shape_renderer;
 | 
			
		||||
  TextRenderer*   _txt_renderer;
 | 
			
		||||
  int             _screen_height;
 | 
			
		||||
};
 | 
			
		||||
@ -1,10 +1,9 @@
 | 
			
		||||
#include "ui_window.h"
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
#include <SDL3/SDL_hidapi.h>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
#include "ui/i_window_content.h"
 | 
			
		||||
#include "ui_renderer.h"
 | 
			
		||||
 | 
			
		||||
UIWindow::UIWindow(const char* title, int x, int y, int width, int height) :
 | 
			
		||||
    _title(title),
 | 
			
		||||
@ -87,45 +86,41 @@ void UIWindow::render(const RenderContext& context) {
 | 
			
		||||
  const Color title_text_color        = { 0.9f,  0.9f,  0.9f  };
 | 
			
		||||
  const Color resize_handle_color     = { 0.9f,  0.9f,  0.9f  };
 | 
			
		||||
 | 
			
		||||
  /* Convert top-left coords to bottom-left for OpenGL. */
 | 
			
		||||
  int y_gl = context.screen_height - _y - _height;
 | 
			
		||||
 | 
			
		||||
  /* Draw main window frame/background. */
 | 
			
		||||
  context.shape_renderer->draw_rect(_x, y_gl, _width, _height, frame_color);
 | 
			
		||||
  context.ui_renderer->draw_rect(_x, _y, _width, _height, frame_color);
 | 
			
		||||
 | 
			
		||||
  /* Draw title bar. */
 | 
			
		||||
  if(_is_focused) {
 | 
			
		||||
    context.shape_renderer->draw_rect(_x, y_gl+_height-title_bar_height, _width, title_bar_height,
 | 
			
		||||
                              focused_title_bar_color);
 | 
			
		||||
    context.ui_renderer->draw_rect(_x, _y, _width, title_bar_height, focused_title_bar_color);
 | 
			
		||||
  } else {
 | 
			
		||||
    context.shape_renderer->draw_rect(_x, y_gl+_height-title_bar_height, _width, title_bar_height,
 | 
			
		||||
                              title_bar_color);
 | 
			
		||||
    context.ui_renderer->draw_rect(_x, _y, _width, title_bar_height, title_bar_color);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Draw title text. */
 | 
			
		||||
  context.txt_renderer->render_text(_title.c_str(), _x+5, y_gl+_height-title_bar_height+8,
 | 
			
		||||
                            1.0f, title_text_color);
 | 
			
		||||
  context.ui_renderer->render_text(_title.c_str(), _x+5, _y+20, title_text_color);
 | 
			
		||||
 | 
			
		||||
  /* Draw title bar buttons. */
 | 
			
		||||
  int button_size = 20;
 | 
			
		||||
  int button_margin = 5;
 | 
			
		||||
  int x_offset = _x + _width - button_size - button_margin;
 | 
			
		||||
  for(const auto& button : _title_bar_buttons) {
 | 
			
		||||
    context.shape_renderer->draw_rect(x_offset, y_gl + _height - title_bar_height + button_margin,
 | 
			
		||||
                              button_size, button_size, button.color);
 | 
			
		||||
    context.ui_renderer->draw_rect(x_offset, _y+button_margin, button_size,
 | 
			
		||||
                                   button_size, button.color);
 | 
			
		||||
    x_offset -= (button_size + button_margin);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Draw Resize handle. */
 | 
			
		||||
  int corner_x = _x + _width;
 | 
			
		||||
  int corner_y = y_gl;
 | 
			
		||||
  context.shape_renderer->draw_triangle(corner_x, corner_y + 10, corner_x - 10,
 | 
			
		||||
  int corner_y = _y + _height;
 | 
			
		||||
  context.ui_renderer->draw_triangle(corner_x, corner_y - 10, corner_x - 10,
 | 
			
		||||
                                corner_y, corner_x, corner_y, resize_handle_color);
 | 
			
		||||
 | 
			
		||||
  if(_content) {
 | 
			
		||||
    int content_y_gl = y_gl;
 | 
			
		||||
    int content_height_gl = _height - title_bar_height;
 | 
			
		||||
    _content->render(context, _x, content_y_gl, _width, content_height_gl);
 | 
			
		||||
    int content_screen_y = _y + title_bar_height;
 | 
			
		||||
    int content_height = _height - title_bar_height;
 | 
			
		||||
    /* Got to pass GL y-coord for scissor box to work correctly. */
 | 
			
		||||
    int content_gl_y = context.screen_height - content_screen_y - content_height;
 | 
			
		||||
    _content->render(context, _x, content_screen_y, content_gl_y, _width, content_height);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user