From e7607e3fc0ad59712930c2c10716cf6bd2aca74d Mon Sep 17 00:00:00 2001 From: Ritchie Cunningham Date: Sat, 4 Oct 2025 03:11:48 +0100 Subject: [PATCH] [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. --- client/src/game_state.cpp | 4 ++-- client/src/gfx/types.h | 6 ++---- client/src/main.cpp | 9 ++++++-- client/src/terminal.cpp | 19 +++++++++-------- client/src/terminal.h | 3 ++- client/src/ui/boot_sequence.cpp | 8 ++++---- client/src/ui/boot_sequence.h | 4 ++-- client/src/ui/desktop.cpp | 26 ++++++++++++------------ client/src/ui/desktop.h | 4 ++-- client/src/ui/editor.cpp | 13 ++++++------ client/src/ui/editor.h | 3 ++- client/src/ui/i_window_content.h | 3 ++- client/src/ui/launcher.cpp | 31 +++++++++++++++------------- client/src/ui/launcher.h | 7 ++++--- client/src/ui/main_menu.cpp | 23 ++++++++------------- client/src/ui/main_menu.h | 5 ++--- client/src/ui/menu_bar.cpp | 17 ++++++++-------- client/src/ui/menu_bar.h | 8 +++----- client/src/ui/taskbar.cpp | 28 +++++++++++-------------- client/src/ui/taskbar.h | 5 ++--- client/src/ui/text_view.cpp | 9 ++++---- client/src/ui/text_view.h | 3 ++- client/src/ui/ui_renderer.cpp | 33 ++++++++++++++++++++++++++++++ client/src/ui/ui_renderer.h | 27 ++++++++++++++++++++++++ client/src/ui/ui_window.cpp | 35 ++++++++++++++------------------ 25 files changed, 194 insertions(+), 139 deletions(-) create mode 100644 client/src/ui/ui_renderer.cpp create mode 100644 client/src/ui/ui_renderer.h diff --git a/client/src/game_state.cpp b/client/src/game_state.cpp index a0d9a2e..576e9d4 100644 --- a/client/src/game_state.cpp +++ b/client/src/game_state.cpp @@ -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: diff --git a/client/src/gfx/types.h b/client/src/gfx/types.h index 1752237..64ffafe 100644 --- a/client/src/gfx/types.h +++ b/client/src/gfx/types.h @@ -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; }; diff --git a/client/src/main.cpp b/client/src/main.cpp index 3edece2..a289d5a 100644 --- a/client/src/main.cpp +++ b/client/src/main.cpp @@ -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(); 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); diff --git a/client/src/terminal.cpp b/client/src/terminal.cpp index 008dee2..ebb922a 100644 --- a/client/src/terminal.cpp +++ b/client/src/terminal.cpp @@ -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); } diff --git a/client/src/terminal.h b/client/src/terminal.h index 04b2d83..735aa49 100644 --- a/client/src/terminal.h +++ b/client/src/terminal.h @@ -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); diff --git a/client/src/ui/boot_sequence.cpp b/client/src/ui/boot_sequence.cpp index 4a1f8bd..e0b458a 100644 --- a/client/src/ui/boot_sequence.cpp +++ b/client/src/ui/boot_sequence.cpp @@ -2,7 +2,7 @@ #include #include #include -#include "gfx/txt_renderer.h" +#include "ui/ui_renderer.h" #include "boot_sequence.h" #include @@ -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); } } diff --git a/client/src/ui/boot_sequence.h b/client/src/ui/boot_sequence.h index a8aeeb1..7e73dde 100644 --- a/client/src/ui/boot_sequence.h +++ b/client/src/ui/boot_sequence.h @@ -4,7 +4,7 @@ #include #include -#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 _messages; Uint32 _start_time; diff --git a/client/src/ui/desktop.cpp b/client/src/ui/desktop.cpp index 20bb760..58e9153 100644 --- a/client/src/ui/desktop.cpp +++ b/client/src/ui/desktop.cpp @@ -2,21 +2,19 @@ #include #include #include -#include #include +#include +#include +#include #include "gfx/types.h" #include "ui/editor.h" #include "desktop.h" -#include #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 -#include #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(5, 5 + _taskbar->get_height(), 200); + _launcher = std::make_unique(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); } } diff --git a/client/src/ui/desktop.h b/client/src/ui/desktop.h index 2ca2b40..e9e484f 100644 --- a/client/src/ui/desktop.h +++ b/client/src/ui/desktop.h @@ -5,11 +5,11 @@ #include #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> _windows; diff --git a/client/src/ui/editor.cpp b/client/src/ui/editor.cpp index 6ad1dec..714c3a4 100644 --- a/client/src/ui/editor.cpp +++ b/client/src/ui/editor.cpp @@ -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) { diff --git a/client/src/ui/editor.h b/client/src/ui/editor.h index 55247b4..1e32a2b 100644 --- a/client/src/ui/editor.h +++ b/client/src/ui/editor.h @@ -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); diff --git a/client/src/ui/i_window_content.h b/client/src/ui/i_window_content.h index 4fb02f6..1cb78d1 100644 --- a/client/src/ui/i_window_content.h +++ b/client/src/ui/i_window_content.h @@ -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; diff --git a/client/src/ui/launcher.cpp b/client/src/ui/launcher.cpp index 907dd4f..6b2e62e 100644 --- a/client/src/ui/launcher.cpp +++ b/client/src/ui/launcher.cpp @@ -1,8 +1,6 @@ #include "launcher.h" #include -#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; +} diff --git a/client/src/ui/launcher.h b/client/src/ui/launcher.h index 7eb0dcd..289c331 100644 --- a/client/src/ui/launcher.h +++ b/client/src/ui/launcher.h @@ -4,17 +4,18 @@ #include #include -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; diff --git a/client/src/ui/main_menu.cpp b/client/src/ui/main_menu.cpp index a02cae3..416c4bf 100644 --- a/client/src/ui/main_menu.cpp +++ b/client/src/ui/main_menu.cpp @@ -1,9 +1,7 @@ #include #include #include - -#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); } } diff --git a/client/src/ui/main_menu.h b/client/src/ui/main_menu.h index 014136c..487dd42 100644 --- a/client/src/ui/main_menu.h +++ b/client/src/ui/main_menu.h @@ -3,9 +3,8 @@ #include #include -#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); diff --git a/client/src/ui/menu_bar.cpp b/client/src/ui/menu_bar.cpp index 8db5ff6..fca7d64 100644 --- a/client/src/ui/menu_bar.cpp +++ b/client/src/ui/menu_bar.cpp @@ -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; diff --git a/client/src/ui/menu_bar.h b/client/src/ui/menu_bar.h index 30beaad..4cf24f6 100644 --- a/client/src/ui/menu_bar.h +++ b/client/src/ui/menu_bar.h @@ -5,8 +5,7 @@ #include #include -#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 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: diff --git a/client/src/ui/taskbar.cpp b/client/src/ui/taskbar.cpp index c834029..15b7e8f 100644 --- a/client/src/ui/taskbar.cpp +++ b/client/src/ui/taskbar.cpp @@ -5,21 +5,19 @@ #include #include -#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>& 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>& 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); } diff --git a/client/src/ui/taskbar.h b/client/src/ui/taskbar.h index ac3882d..179cbf3 100644 --- a/client/src/ui/taskbar.h +++ b/client/src/ui/taskbar.h @@ -5,15 +5,14 @@ #include 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>& windows, UIWindow* focused_window); diff --git a/client/src/ui/text_view.cpp b/client/src/ui/text_view.cpp index f786347..cf418aa 100644 --- a/client/src/ui/text_view.cpp +++ b/client/src/ui/text_view.cpp @@ -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; } } diff --git a/client/src/ui/text_view.h b/client/src/ui/text_view.h index b2f2856..a19d498 100644 --- a/client/src/ui/text_view.h +++ b/client/src/ui/text_view.h @@ -3,6 +3,7 @@ #include #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); diff --git a/client/src/ui/ui_renderer.cpp b/client/src/ui/ui_renderer.cpp new file mode 100644 index 0000000..405e872 --- /dev/null +++ b/client/src/ui/ui_renderer.cpp @@ -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; +} diff --git a/client/src/ui/ui_renderer.h b/client/src/ui/ui_renderer.h new file mode 100644 index 0000000..a638a4a --- /dev/null +++ b/client/src/ui/ui_renderer.h @@ -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; +}; diff --git a/client/src/ui/ui_window.cpp b/client/src/ui/ui_window.cpp index 3c87eae..311f3ee 100644 --- a/client/src/ui/ui_window.cpp +++ b/client/src/ui/ui_window.cpp @@ -1,10 +1,9 @@ #include "ui_window.h" #include +#include #include -#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); } }