[Add] Taskbar and window management work
- Adds a taskbar that displays and manages open application windows - close, minimise and restore functionality - resizeable windows - Refactored desktop event handling to manage window focus and render order
This commit is contained in:
parent
6876cbff95
commit
ea25cb6cc7
@ -15,7 +15,7 @@
|
|||||||
#include <ui/boot_sequence.h>
|
#include <ui/boot_sequence.h>
|
||||||
|
|
||||||
void GameState::_init_desktop(void) {
|
void GameState::_init_desktop(void) {
|
||||||
_desktop = std::make_unique<Desktop>();
|
_desktop = std::make_unique<Desktop>(_screen_width, _screen_height);
|
||||||
|
|
||||||
auto term = std::make_unique<Terminal>(_network.get());
|
auto term = std::make_unique<Terminal>(_network.get());
|
||||||
auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
|
auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
|
||||||
@ -40,11 +40,16 @@ void GameState::_run_server(void) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GameState::GameState(void) : _current_screen(Screen::MAIN_MENU) {}
|
GameState::GameState(void) :
|
||||||
|
_current_screen(Screen::MAIN_MENU),
|
||||||
|
_screen_width(0),
|
||||||
|
_screen_height(0) {}
|
||||||
|
|
||||||
GameState::~GameState(void) = default;
|
GameState::~GameState(void) = default;
|
||||||
|
|
||||||
void GameState::init(int screen_width, int screen_height) {
|
void GameState::init(int screen_width, int screen_height) {
|
||||||
|
_screen_width = screen_width;
|
||||||
|
_screen_height = screen_height;
|
||||||
/* Create and connect the network client. */
|
/* Create and connect the network client. */
|
||||||
_network = std::make_unique<ClientNetwork>();
|
_network = std::make_unique<ClientNetwork>();
|
||||||
|
|
||||||
@ -77,7 +82,7 @@ void GameState::handle_event(SDL_Event* event) {
|
|||||||
break;
|
break;
|
||||||
case Screen::DESKTOP:
|
case Screen::DESKTOP:
|
||||||
if(_desktop) {
|
if(_desktop) {
|
||||||
_desktop->handle_event(event);
|
_desktop->handle_event(event, _screen_height);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,6 +34,8 @@ private:
|
|||||||
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;
|
Screen _current_screen;
|
||||||
|
int _screen_width;
|
||||||
|
int _screen_height;
|
||||||
|
|
||||||
void _init_desktop(void);
|
void _init_desktop(void);
|
||||||
void _run_server(void);
|
void _run_server(void);
|
||||||
|
|||||||
@ -54,3 +54,25 @@ void ShapeRenderer::draw_rect(int x, int y, int width, int height, const Color&
|
|||||||
glDrawArrays(GL_TRIANGLES, 0, 6);
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||||
glBindVertexArray(0);
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShapeRenderer::draw_triangle(int x1, int y1, int x2, int y2, int x3,
|
||||||
|
int y3, const Color& color) {
|
||||||
|
_shape_shader->use();
|
||||||
|
_shape_shader->set_vec3("objectColor", color.r, color.g, color.b);
|
||||||
|
|
||||||
|
float vertices[3][2] = {
|
||||||
|
{(float)x1, (float)y1},
|
||||||
|
{(float)x2, (float)y2},
|
||||||
|
{(float)x3, (float)y3},
|
||||||
|
};
|
||||||
|
|
||||||
|
glBindVertexArray(_vao);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
|
||||||
|
/*
|
||||||
|
* We're overwriting the buffer content here, this is fine for just one triangle,
|
||||||
|
* but don't do it for everything ;)
|
||||||
|
*/
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 3);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|||||||
@ -9,6 +9,8 @@ public:
|
|||||||
~ShapeRenderer(void);
|
~ShapeRenderer(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,
|
||||||
|
const Color& color);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Shader* _shape_shader;
|
Shader* _shape_shader;
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
#include "gfx/shape_renderer.h"
|
#include "gfx/shape_renderer.h"
|
||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
#include "game_state.h"
|
#include "game_state.h"
|
||||||
|
#include "ui/cursor_manager.h"
|
||||||
|
|
||||||
const int SCREEN_WIDTH = 1280;
|
const int SCREEN_WIDTH = 1280;
|
||||||
const int SCREEN_HEIGHT = 720;
|
const int SCREEN_HEIGHT = 720;
|
||||||
@ -66,6 +67,9 @@ int main(int argc, char** argv) {
|
|||||||
/* Listen for text input. */
|
/* Listen for text input. */
|
||||||
SDL_StartTextInput(window);
|
SDL_StartTextInput(window);
|
||||||
|
|
||||||
|
/* Init cursor manager. */
|
||||||
|
CursorManager::init();
|
||||||
|
|
||||||
/* Init text renderer. */
|
/* Init text renderer. */
|
||||||
TextRenderer* txt_render_instance = new TextRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
|
TextRenderer* txt_render_instance = new TextRenderer(SCREEN_WIDTH, SCREEN_HEIGHT);
|
||||||
txt_render_instance->load_font("assets/fonts/hack/Hack-Regular.ttf", 14);
|
txt_render_instance->load_font("assets/fonts/hack/Hack-Regular.ttf", 14);
|
||||||
@ -116,6 +120,7 @@ int main(int argc, char** argv) {
|
|||||||
delete shape_renderer_instance;
|
delete shape_renderer_instance;
|
||||||
delete txt_render_instance;
|
delete txt_render_instance;
|
||||||
SDL_GL_DestroyContext(context);
|
SDL_GL_DestroyContext(context);
|
||||||
|
CursorManager::quit();
|
||||||
SDL_DestroyWindow(window);
|
SDL_DestroyWindow(window);
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
|
|
||||||
|
|||||||
39
client/src/ui/cursor_manager.cpp
Normal file
39
client/src/ui/cursor_manager.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "cursor_manager.h"
|
||||||
|
#include <SDL3/SDL_error.h>
|
||||||
|
#include <SDL3/SDL_mouse.h>
|
||||||
|
|
||||||
|
SDL_Cursor* CursorManager::_arrow_cursor = nullptr;
|
||||||
|
SDL_Cursor* CursorManager::_resize_cursor = nullptr;
|
||||||
|
|
||||||
|
void CursorManager::init(void) {
|
||||||
|
_arrow_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_DEFAULT);
|
||||||
|
_resize_cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_NWSE_RESIZE);
|
||||||
|
|
||||||
|
if(!_arrow_cursor || !_resize_cursor) {
|
||||||
|
printf("Failed to create system cursors! SDL_Error: %s\n", SDL_GetError());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CursorManager::set_cursor(CursorType type) {
|
||||||
|
SDL_Cursor* cursor_to_set = _arrow_cursor;
|
||||||
|
switch(type) {
|
||||||
|
case CursorType::ARROW:
|
||||||
|
cursor_to_set = _arrow_cursor;
|
||||||
|
break;
|
||||||
|
case CursorType::RESIZE_NWSE:
|
||||||
|
cursor_to_set = _resize_cursor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(SDL_GetCursor() != cursor_to_set) {
|
||||||
|
SDL_SetCursor(cursor_to_set);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CursorManager::quit(void) {
|
||||||
|
SDL_DestroyCursor(_arrow_cursor);
|
||||||
|
SDL_DestroyCursor(_resize_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
19
client/src/ui/cursor_manager.h
Normal file
19
client/src/ui/cursor_manager.h
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL_mouse.h>
|
||||||
|
|
||||||
|
enum class CursorType {
|
||||||
|
ARROW,
|
||||||
|
RESIZE_NWSE /* Diagonal resize arrow. */
|
||||||
|
};
|
||||||
|
|
||||||
|
class CursorManager {
|
||||||
|
public:
|
||||||
|
static void init(void);
|
||||||
|
static void set_cursor(CursorType type);
|
||||||
|
static void quit(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static SDL_Cursor* _arrow_cursor;
|
||||||
|
static SDL_Cursor* _resize_cursor;
|
||||||
|
};
|
||||||
@ -1,4 +1,5 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "desktop.h"
|
#include "desktop.h"
|
||||||
@ -6,9 +7,13 @@
|
|||||||
#include "gfx/shape_renderer.h"
|
#include "gfx/shape_renderer.h"
|
||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
#include "ui/taskbar.h"
|
||||||
|
#include <SDL3/SDL_video.h>
|
||||||
|
#include <ui/cursor_manager.h>
|
||||||
#include "ui/ui_window.h"
|
#include "ui/ui_window.h"
|
||||||
|
|
||||||
Desktop::Desktop(void) {
|
Desktop::Desktop(int screen_width, int screen_height) {
|
||||||
|
_taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
|
||||||
_focused_window = nullptr;
|
_focused_window = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -24,7 +29,33 @@ void Desktop::add_window(std::unique_ptr<UIWindow> window) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Desktop::handle_event(SDL_Event* event) {
|
void Desktop::_set_focused_window(UIWindow* window) {
|
||||||
|
if(window == _focused_window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_focused_window) {
|
||||||
|
_focused_window->set_focused(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_focused_window = window;
|
||||||
|
if(_focused_window) {
|
||||||
|
_focused_window->set_focused(true);
|
||||||
|
|
||||||
|
/* Find window in vector and move it to the end. */
|
||||||
|
auto it = std::find_if(_windows.begin(), _windows.end(),
|
||||||
|
[window](const std::unique_ptr<UIWindow>& w)
|
||||||
|
{ return w.get() == window; });
|
||||||
|
|
||||||
|
if(it != _windows.end()) {
|
||||||
|
auto window_to_move = std::move(*it);
|
||||||
|
_windows.erase(it);
|
||||||
|
_windows.push_back(std::move(window_to_move));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Desktop::handle_event(SDL_Event* event, int screen_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;
|
||||||
@ -32,22 +63,35 @@ void Desktop::handle_event(SDL_Event* event) {
|
|||||||
/* Find the top-most window that was clicked. */
|
/* Find the top-most window that was clicked. */
|
||||||
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 not focused, focus it. */
|
if(!_windows[i]->is_minimized()) {
|
||||||
if(_windows[i].get() != _focused_window) {
|
_set_focused_window(_windows[i].get());
|
||||||
if(_focused_window) {
|
|
||||||
_focused_window->set_focused(false);
|
|
||||||
}
|
|
||||||
_focused_window = _windows[i].get();
|
|
||||||
_focused_window->set_focused(true);
|
|
||||||
|
|
||||||
/* Move window to the front. */
|
|
||||||
auto window_to_move = std::move(_windows[i]);
|
|
||||||
_windows.erase(_windows.begin() + i);
|
|
||||||
_windows.push_back(std::move(window_to_move));
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||||
|
UIWindow* clicked_window = _taskbar->handle_event(event, screen_height, _windows);
|
||||||
|
if(clicked_window) {
|
||||||
|
if(clicked_window == _focused_window && !clicked_window->is_minimized()) {
|
||||||
|
clicked_window->minimize();
|
||||||
|
_set_focused_window(nullptr);
|
||||||
|
} else {
|
||||||
|
clicked_window->restore();
|
||||||
|
_set_focused_window(clicked_window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if(event->type == SDL_EVENT_MOUSE_MOTION) {
|
||||||
|
bool on_resize_handle = false;
|
||||||
|
/* Iterate backwards since top-most windows are at the end. */
|
||||||
|
for(int i = _windows.size() - 1; i >= 0; --i) {
|
||||||
|
if(_windows[i]->is_mouse_over_resize_handle(event->motion.x,
|
||||||
|
event->motion.y)) {
|
||||||
|
on_resize_handle = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CursorManager::set_cursor(on_resize_handle ? CursorType::RESIZE_NWSE
|
||||||
|
: CursorType::ARROW);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(_focused_window) {
|
if(_focused_window) {
|
||||||
@ -61,12 +105,10 @@ void Desktop::handle_event(SDL_Event* event) {
|
|||||||
|
|
||||||
void Desktop::update(void) {
|
void Desktop::update(void) {
|
||||||
/* Remove closed windows. */
|
/* Remove closed windows. */
|
||||||
_windows.erase(std::remove_if(_windows.begin(), _windows.end(),
|
_windows.erase(
|
||||||
[](const std::unique_ptr<UIWindow>& window) {
|
std::remove_if(_windows.begin(), _windows.end(),
|
||||||
Terminal* term = window->get_content();
|
[](const std::unique_ptr<UIWindow>& w) { return w->should_close(); }),
|
||||||
return term && term->close();
|
_windows.end());
|
||||||
}),
|
|
||||||
_windows.end());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UIWindow* Desktop::get_focused_window(void) {
|
UIWindow* Desktop::get_focused_window(void) {
|
||||||
@ -75,7 +117,11 @@ UIWindow* Desktop::get_focused_window(void) {
|
|||||||
|
|
||||||
void Desktop::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
void Desktop::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
||||||
int screen_height, bool show_cursor) {
|
int screen_height, bool show_cursor) {
|
||||||
|
/* TODO: Render wallpaper here. */
|
||||||
|
_taskbar->render(shape_renderer, txt_renderer, _windows, _focused_window);
|
||||||
for(const auto& win : _windows) {
|
for(const auto& win : _windows) {
|
||||||
win.get()->render(shape_renderer, txt_renderer, screen_height, show_cursor);
|
if(!win->is_minimized()) {
|
||||||
|
win.get()->render(shape_renderer, txt_renderer, screen_height, show_cursor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,14 +7,15 @@
|
|||||||
#include "gfx/shape_renderer.h"
|
#include "gfx/shape_renderer.h"
|
||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
#include "ui/ui_window.h"
|
#include "ui/ui_window.h"
|
||||||
|
#include "ui/taskbar.h"
|
||||||
|
|
||||||
class Desktop {
|
class Desktop {
|
||||||
public:
|
public:
|
||||||
Desktop(void);
|
Desktop(int screen_width, int screen_height);
|
||||||
~Desktop(void);
|
~Desktop(void);
|
||||||
|
|
||||||
void add_window(std::unique_ptr<UIWindow> window);
|
void add_window(std::unique_ptr<UIWindow> window);
|
||||||
void handle_event(SDL_Event* event);
|
void handle_event(SDL_Event* event, int screen_height);
|
||||||
void update(void);
|
void update(void);
|
||||||
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
|
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
|
||||||
bool show_cursor);
|
bool show_cursor);
|
||||||
@ -22,6 +23,8 @@ public:
|
|||||||
UIWindow* get_focused_window(void);
|
UIWindow* get_focused_window(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void _set_focused_window(UIWindow* window);
|
||||||
std::vector<std::unique_ptr<UIWindow>> _windows;
|
std::vector<std::unique_ptr<UIWindow>> _windows;
|
||||||
|
std::unique_ptr<Taskbar> _taskbar;
|
||||||
UIWindow* _focused_window;
|
UIWindow* _focused_window;
|
||||||
};
|
};
|
||||||
|
|||||||
68
client/src/ui/taskbar.cpp
Normal file
68
client/src/ui/taskbar.cpp
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#include "taskbar.h"
|
||||||
|
#include <memory>
|
||||||
|
#include "gfx/shape_renderer.h"
|
||||||
|
#include "gfx/txt_renderer.h"
|
||||||
|
#include "gfx/types.h"
|
||||||
|
#include "ui/ui_window.h"
|
||||||
|
|
||||||
|
Taskbar::Taskbar(int screen_width, int screen_height) {
|
||||||
|
_width = screen_width;
|
||||||
|
_height = 32;
|
||||||
|
_y_pos = 0; /* Taksbar at bottom because boring? */
|
||||||
|
}
|
||||||
|
|
||||||
|
Taskbar::~Taskbar(void) {}
|
||||||
|
|
||||||
|
void Taskbar::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
||||||
|
const std::vector<std::unique_ptr<UIWindow>>& windows,
|
||||||
|
UIWindow* focused_window) {
|
||||||
|
|
||||||
|
const Color taskbar_color = { 0.1f, 0.12f, 0.14f };
|
||||||
|
const Color button_color = { 0.2f, 0.22f, 0.24f };
|
||||||
|
const Color button_focused_color = { 0.3f, 0.32f, 0.34f };
|
||||||
|
const Color button_text_color = { 0.9f, 0.9f, 0.9f };
|
||||||
|
|
||||||
|
|
||||||
|
shape_renderer->draw_rect(0, _y_pos, _width, _height, taskbar_color);
|
||||||
|
|
||||||
|
int button_width = 150;
|
||||||
|
int padding = 5;
|
||||||
|
int x_offset = padding;
|
||||||
|
|
||||||
|
for(const auto& window : windows) {
|
||||||
|
bool is_focused = (window.get() == focused_window);
|
||||||
|
shape_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);
|
||||||
|
x_offset += button_width + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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. */
|
||||||
|
|
||||||
|
if(mouse_y >= _y_pos && mouse_y <= _y_pos + _height) {
|
||||||
|
int button_width = 150;
|
||||||
|
int padding = 5;
|
||||||
|
int x_offset = padding;
|
||||||
|
for(auto& window : windows) {
|
||||||
|
if(mouse_x >= x_offset && mouse_x <= x_offset + button_width) {
|
||||||
|
return window.get(); /* Return clicked window. */
|
||||||
|
}
|
||||||
|
x_offset += button_width + padding;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr; /* No window button was clicked. */
|
||||||
|
}
|
||||||
|
|
||||||
|
int Taskbar::get_height(void) const {
|
||||||
|
return _height;
|
||||||
|
}
|
||||||
28
client/src/ui/taskbar.h
Normal file
28
client/src/ui/taskbar.h
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL_events.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
class UIWindow;
|
||||||
|
class ShapeRenderer;
|
||||||
|
class TextRenderer;
|
||||||
|
|
||||||
|
class Taskbar {
|
||||||
|
public:
|
||||||
|
Taskbar(int screen_width, int screen_height);
|
||||||
|
~Taskbar(void);
|
||||||
|
|
||||||
|
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
||||||
|
const std::vector<std::unique_ptr<UIWindow>>& windows,
|
||||||
|
UIWindow* focused_window);
|
||||||
|
|
||||||
|
UIWindow* handle_event(SDL_Event* event, int screen_height,
|
||||||
|
const std::vector<std::unique_ptr<UIWindow>>& windows);
|
||||||
|
|
||||||
|
int get_height(void) const;
|
||||||
|
private:
|
||||||
|
int _width;
|
||||||
|
int _height;
|
||||||
|
int _y_pos;
|
||||||
|
};
|
||||||
@ -5,20 +5,49 @@
|
|||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
#include "gfx/types.h"
|
#include "gfx/types.h"
|
||||||
|
|
||||||
UIWindow::UIWindow(const char* title, int x, int y, int width, int height) {
|
UIWindow::UIWindow(const char* title, int x, int y, int width, int height) :
|
||||||
_title = title;
|
_title(title),
|
||||||
_x = x;
|
_x(x),
|
||||||
_y = y;
|
_y(y),
|
||||||
_width = width;
|
_width(width),
|
||||||
_height = height;
|
_height(height),
|
||||||
_content = nullptr;
|
_content(nullptr),
|
||||||
_is_dragging = false;
|
_is_dragging(false),
|
||||||
_is_hovered = false;
|
_is_hovered(false),
|
||||||
_is_focused = false; /* Not focused by default? */
|
_is_focused(false),
|
||||||
}
|
_should_close(false),
|
||||||
|
_state(WindowState::NORMAL),
|
||||||
|
_is_resizing(false),
|
||||||
|
_resize_margin(10) {}
|
||||||
|
|
||||||
UIWindow::~UIWindow(void) {}
|
UIWindow::~UIWindow(void) {}
|
||||||
|
|
||||||
|
void UIWindow::minimize(void) {
|
||||||
|
_state = WindowState::MINIMIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UIWindow::restore(void) {
|
||||||
|
_state = WindowState::NORMAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UIWindow::is_minimized(void) const {
|
||||||
|
return _state == WindowState::MINIMIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& UIWindow::get_title(void) const {
|
||||||
|
return _title;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UIWindow::is_mouse_over_resize_handle(int mouse_x, int mouse_y) const {
|
||||||
|
return (mouse_x >= _x + _width - _resize_margin && mouse_x <= _x + _width &&
|
||||||
|
mouse_y >= _y + _height - _resize_margin &&
|
||||||
|
mouse_y <= _y + _height);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UIWindow::should_close(void) const {
|
||||||
|
return _should_close;
|
||||||
|
}
|
||||||
|
|
||||||
void UIWindow::set_content(std::unique_ptr<Terminal> term) {
|
void UIWindow::set_content(std::unique_ptr<Terminal> term) {
|
||||||
_content = std::move(term);
|
_content = std::move(term);
|
||||||
}
|
}
|
||||||
@ -45,6 +74,9 @@ void UIWindow::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
|||||||
const Color title_bar_color = { 0.15f, 0.15f, 0.2f };
|
const Color title_bar_color = { 0.15f, 0.15f, 0.2f };
|
||||||
const Color focused_title_bar_color = { 0.3f, 0.3f, 0.4f };
|
const Color focused_title_bar_color = { 0.3f, 0.3f, 0.4f };
|
||||||
const Color title_text_color = { 0.9f, 0.9f, 0.9f };
|
const Color title_text_color = { 0.9f, 0.9f, 0.9f };
|
||||||
|
const Color close_button_color = { 0.8f, 0.2f, 0.2f };
|
||||||
|
const Color minimize_button_color = { 0.8f, 0.8f, 0.2f };
|
||||||
|
const Color resize_handle_color = { 0.9f, 0.9f, 0.9f };
|
||||||
|
|
||||||
/* Convert top-left coords to bottom-left for OpenGL. */
|
/* Convert top-left coords to bottom-left for OpenGL. */
|
||||||
int y_gl = screen_height - _y - _height;
|
int y_gl = screen_height - _y - _height;
|
||||||
@ -65,6 +97,21 @@ void UIWindow::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
|
|||||||
txt_renderer->render_text(_title.c_str(), _x+5, y_gl+_height-title_bar_height+8,
|
txt_renderer->render_text(_title.c_str(), _x+5, y_gl+_height-title_bar_height+8,
|
||||||
1.0f, title_text_color);
|
1.0f, title_text_color);
|
||||||
|
|
||||||
|
/* Draw close button. */
|
||||||
|
shape_renderer->draw_rect(_x + _width - 25, y_gl + _height - title_bar_height + 5,
|
||||||
|
20, 20, close_button_color);
|
||||||
|
|
||||||
|
/* Draw minimize button. */
|
||||||
|
shape_renderer->draw_rect(_x + _width - 50,
|
||||||
|
y_gl + _height - title_bar_height + 5, 20, 20,
|
||||||
|
minimize_button_color);
|
||||||
|
|
||||||
|
/* Draw Resize handle. */
|
||||||
|
int corner_x = _x + _width;
|
||||||
|
int corner_y = y_gl;
|
||||||
|
shape_renderer->draw_triangle(corner_x, corner_y + 10, corner_x - 10,
|
||||||
|
corner_y, corner_x, corner_y, resize_handle_color);
|
||||||
|
|
||||||
if(_content) {
|
if(_content) {
|
||||||
int content_y_gl = y_gl;
|
int content_y_gl = y_gl;
|
||||||
int content_height_gl = _height - title_bar_height;
|
int content_height_gl = _height - title_bar_height;
|
||||||
@ -79,19 +126,46 @@ void UIWindow::handle_event(SDL_Event* event) {
|
|||||||
int mouse_x = event->button.x;
|
int mouse_x = event->button.x;
|
||||||
int mouse_y = event->button.y;
|
int mouse_y = event->button.y;
|
||||||
|
|
||||||
/* Is click within title bar? */
|
/* Check for close button click. */
|
||||||
if(mouse_x >= _x && mouse_x <= _x + _width &&
|
int close_button_x = _x + _width - 25;
|
||||||
|
int close_button_y = _y + 5;
|
||||||
|
if(mouse_x >= close_button_x && mouse_x <= close_button_x + 20 &&
|
||||||
|
mouse_y >= close_button_y && mouse_y <= close_button_y + 20) {
|
||||||
|
_should_close = true;
|
||||||
|
return; /* Stop processing this event. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for minimize button click. */
|
||||||
|
int minimize_button_x = _x + _width - 50;
|
||||||
|
int minimize_button_y = _y + 5;
|
||||||
|
if(mouse_x >= minimize_button_x && mouse_x <= minimize_button_x + 20 &&
|
||||||
|
mouse_y >= minimize_button_y && mouse_y <= minimize_button_y + 20) {
|
||||||
|
minimize();
|
||||||
|
return; /* Stop processing this event. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for resize handle click (bottom-right corner). */
|
||||||
|
if(is_mouse_over_resize_handle(mouse_x, mouse_y)) {
|
||||||
|
_is_resizing = true;
|
||||||
|
} else if(mouse_x >= _x && mouse_x <= _x + _width &&
|
||||||
mouse_y >= _y && mouse_y <= _y + title_bar_height) {
|
mouse_y >= _y && mouse_y <= _y + title_bar_height) {
|
||||||
|
/* Is click within title bar? */
|
||||||
_is_dragging = true;
|
_is_dragging = true;
|
||||||
_drag_offset_x = mouse_x - _x;
|
_drag_offset_x = mouse_x - _x;
|
||||||
_drag_offset_y = mouse_y - _y;
|
_drag_offset_y = mouse_y - _y;
|
||||||
}
|
}
|
||||||
} else if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
} else if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||||
_is_dragging = false;
|
_is_dragging = false;
|
||||||
|
_is_resizing = false;
|
||||||
} else if(event->type == SDL_EVENT_MOUSE_MOTION) {
|
} else if(event->type == SDL_EVENT_MOUSE_MOTION) {
|
||||||
if(_is_dragging) {
|
if(_is_dragging) {
|
||||||
_x = event->motion.x - _drag_offset_x;
|
_x = event->motion.x - _drag_offset_x;
|
||||||
_y = event->motion.y - _drag_offset_y;
|
_y = event->motion.y - _drag_offset_y;
|
||||||
|
} else if(_is_resizing) {
|
||||||
|
int new_width = event->motion.x - _x;
|
||||||
|
int new_height = event->motion.y - _y;
|
||||||
|
_width = (new_width > 100) ? new_width : 100; /* Min width. */
|
||||||
|
_height = (new_height > 80) ? new_height : 80; /* Min height. */
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if mouse hovered over window. */
|
/* Check if mouse hovered over window. */
|
||||||
|
|||||||
@ -7,6 +7,12 @@
|
|||||||
#include "gfx/txt_renderer.h"
|
#include "gfx/txt_renderer.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
|
|
||||||
|
enum class WindowState {
|
||||||
|
NORMAL,
|
||||||
|
MINIMIZED,
|
||||||
|
MAXIMIZED
|
||||||
|
};
|
||||||
|
|
||||||
class UIWindow {
|
class UIWindow {
|
||||||
public:
|
public:
|
||||||
UIWindow(const char* title, int x, int y, int width, int height);
|
UIWindow(const char* title, int x, int y, int width, int height);
|
||||||
@ -15,19 +21,32 @@ public:
|
|||||||
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
|
void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height,
|
||||||
bool show_cursor);
|
bool show_cursor);
|
||||||
void handle_event(SDL_Event* event);
|
void handle_event(SDL_Event* event);
|
||||||
|
void minimize(void);
|
||||||
|
void restore(void);
|
||||||
|
bool is_minimized(void) const;
|
||||||
|
bool should_close(void) const;
|
||||||
void set_focused(bool focused);
|
void set_focused(bool focused);
|
||||||
bool is_point_inside(int x, int y);
|
bool is_point_inside(int x, int y);
|
||||||
void set_content(std::unique_ptr<Terminal> term);
|
void set_content(std::unique_ptr<Terminal> term);
|
||||||
Terminal* get_content(void);
|
Terminal* get_content(void);
|
||||||
|
bool is_mouse_over_resize_handle(int mouse_x, int mouse_y) const;
|
||||||
|
const std::string& get_title(void) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class Taskbar; /* Allow taskbar to access private members. */
|
||||||
int _x, _y, _width, _height;
|
int _x, _y, _width, _height;
|
||||||
|
Rect _pre_maximize_rect;
|
||||||
std::string _title;
|
std::string _title;
|
||||||
std::unique_ptr<Terminal> _content;
|
std::unique_ptr<Terminal> _content;
|
||||||
bool _is_focused; /* Managed by desktop. */
|
bool _is_focused; /* Managed by desktop. */
|
||||||
bool _is_hovered; /* Send scroll events even if not focused. */
|
bool _is_hovered; /* Send scroll events even if not focused. */
|
||||||
|
bool _should_close;
|
||||||
|
|
||||||
|
WindowState _state;
|
||||||
|
|
||||||
bool _is_dragging;
|
bool _is_dragging;
|
||||||
int _drag_offset_x, _drag_offset_y;
|
int _drag_offset_x, _drag_offset_y;
|
||||||
};
|
|
||||||
|
|
||||||
|
bool _is_resizing;
|
||||||
|
int _resize_margin;
|
||||||
|
};
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user