#include "ui_window.h" #include #include #include "gfx/shape_renderer.h" #include "gfx/txt_renderer.h" #include "gfx/types.h" #include "ui/i_window_content.h" #include "ui/window_action.h" UIWindow::UIWindow(const char* title, int x, int y, int width, int height) : _title(title), _x(x), _y(y), _width(width), _height(height), _content(nullptr), _is_dragging(false), _is_hovered(false), _is_focused(false), _should_close(false), _state(WindowState::NORMAL), _is_resizing(false), _resize_margin(10) { /* Init title bar buttons. */ _title_bar_buttons.push_back({WindowButtonAction::CLOSE, {0.8f, 0.2f, 0.2f}}); _title_bar_buttons.push_back({WindowButtonAction::MAXIMIZE, {0.2f, 0.8f, 0.2f}}); _title_bar_buttons.push_back({WindowButtonAction::MINIMIZE, {0.8f, 0.8f, 0.2f}}); } 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::update(void) { if(_content) { if(_content->should_close()) { _should_close = true; } } } void UIWindow::set_content(std::unique_ptr content) { _content = std::move(content); } void UIWindow::set_focused(bool focused) { _is_focused = focused; } IWindowContent* UIWindow::get_content(void) { return _content.get(); } bool UIWindow::is_point_inside(int x, int y) { return (x >= _x && x <= _x + _width && y >= _y && y <= _y + _height); } void UIWindow::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height, bool show_cursor) { int title_bar_height = 30; /* Define colours. */ const Color frame_color = { 0.2f, 0.2f, 0.25f }; 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 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 = screen_height - _y - _height; /* Draw main window frame/background. */ shape_renderer->draw_rect(_x, y_gl, _width, _height, frame_color); /* Draw title bar. */ if(_is_focused) { shape_renderer->draw_rect(_x, y_gl+_height-title_bar_height, _width, title_bar_height, focused_title_bar_color); } else { shape_renderer->draw_rect(_x, y_gl+_height-title_bar_height, _width, title_bar_height, title_bar_color); } /* Draw title text. */ txt_renderer->render_text(_title.c_str(), _x+5, y_gl+_height-title_bar_height+8, 1.0f, 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) { shape_renderer->draw_rect(x_offset, y_gl + _height - title_bar_height + 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; shape_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(txt_renderer, _x, content_y_gl, _width, content_height_gl, show_cursor); } } void UIWindow::handle_event(SDL_Event* event, int screen_width, int screen_height, int taskbar_height) { int title_bar_height = 30; if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) { int mouse_x = event->button.x; int mouse_y = event->button.y; /* Check for title bar button clicks. */ int button_size = 20; int button_margin = 5; int x_offset = _x + _width - button_size - button_margin; for(const auto& button : _title_bar_buttons) { if(mouse_x >= x_offset && mouse_x <= x_offset + button_size && mouse_y >= _y + button_margin && mouse_y <= _y + button_margin + button_size) { switch(button.action) { case WindowButtonAction::CLOSE: _should_close = true; break; case WindowButtonAction::MAXIMIZE: if(_state == WindowState::MAXIMIZED) { _x = _pre_maximize_rect.x; _y = _pre_maximize_rect.y; _width = _pre_maximize_rect.w; _height = _pre_maximize_rect.h; _state = WindowState::NORMAL; } else { _pre_maximize_rect = { _x, _y, _width, _height }; _x = 0; _y = 0; _width = screen_width; _height = screen_height - taskbar_height; _state = WindowState::MAXIMIZED; } break; case WindowButtonAction::MINIMIZE: minimize(); break; } return; /* Button clicked, no need to process further. */ } x_offset -= (button_size + button_margin); } /* Check for resize handle click (bottom-right corner). */ if(is_mouse_over_resize_handle(mouse_x, mouse_y) && _state != WindowState::MAXIMIZED) { _is_resizing = true; } else if(mouse_x >= _x && mouse_x <= _x + _width && mouse_y >= _y && mouse_y <= _y + title_bar_height && _state != WindowState::MAXIMIZED) { /* Is click within title bar? */ _is_dragging = true; _drag_offset_x = mouse_x - _x; _drag_offset_y = mouse_y - _y; } } else if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) { _is_dragging = false; _is_resizing = false; } else if(event->type == SDL_EVENT_MOUSE_MOTION) { if(_is_dragging) { _x = event->motion.x - _drag_offset_x; _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. */ _is_hovered = is_point_inside(event->motion.x, event->motion.y); } else if(event->type == SDL_EVENT_MOUSE_WHEEL) { /* Only scroll window if mouse is hovering. */ if(_is_hovered && _content) { /* SDL's wheel motion is negative scroll up on the y. */ int content_height_gl = _height - title_bar_height; _content->scroll(-event->wheel.y, content_height_gl); } } }