214 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			214 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
#include "ui_window.h"
 | 
						|
#include <SDL3/SDL_events.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/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<IWindowContent> 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);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 |