[Add] Graphical application launcher.
This commit is contained in:
		
							parent
							
								
									996bf1c62c
								
							
						
					
					
						commit
						43907509eb
					
				@ -15,7 +15,7 @@
 | 
			
		||||
#include <ui/boot_sequence.h>
 | 
			
		||||
 | 
			
		||||
void GameState::_init_desktop(void) {
 | 
			
		||||
  _desktop = std::make_unique<Desktop>(_screen_width, _screen_height);
 | 
			
		||||
  _desktop = std::make_unique<Desktop>(_screen_width, _screen_height, _network.get());
 | 
			
		||||
 | 
			
		||||
  auto term = std::make_unique<Terminal>(_network.get());
 | 
			
		||||
  auto term_window = std::make_unique<UIWindow>("Terminal", 100, 100, 800, 500);
 | 
			
		||||
 | 
			
		||||
@ -6,6 +6,7 @@
 | 
			
		||||
 | 
			
		||||
#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"
 | 
			
		||||
@ -22,9 +23,12 @@ static const std::string& get_random_snippet(const std::vector<std::string>& sni
 | 
			
		||||
  return snippets[rand() % snippets.size()];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Desktop::Desktop(int screen_width, int screen_height) {
 | 
			
		||||
Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) {
 | 
			
		||||
  _taskbar = std::make_unique<Taskbar>(screen_width, screen_height);
 | 
			
		||||
  _network = network;
 | 
			
		||||
  _focused_window = nullptr;
 | 
			
		||||
  _launcher_is_open = false;
 | 
			
		||||
  _launcher = std::make_unique<Launcher>(5, 5 + _taskbar->get_height(), 200);
 | 
			
		||||
 | 
			
		||||
  /* Load snippets for temp wallpaper. */
 | 
			
		||||
  std::ifstream snippet_file("assets/menu_background_snippets.txt");
 | 
			
		||||
@ -50,11 +54,8 @@ Desktop::~Desktop(void) {}
 | 
			
		||||
 | 
			
		||||
void Desktop::add_window(std::unique_ptr<UIWindow> window) {
 | 
			
		||||
  _windows.push_back(std::move(window));
 | 
			
		||||
  /* I'm sick of reaching for my mouse to focus the terminal.. Stop that! */
 | 
			
		||||
  if(_windows.size() == 1) {
 | 
			
		||||
    _focused_window = _windows.back().get();
 | 
			
		||||
    _focused_window->set_focused(true);
 | 
			
		||||
  }
 | 
			
		||||
  /* Focus new window. */
 | 
			
		||||
  _set_focused_window(_windows.back().get());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::_set_focused_window(UIWindow* window) {
 | 
			
		||||
@ -69,17 +70,6 @@ void Desktop::_set_focused_window(UIWindow* window) {
 | 
			
		||||
  _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));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -88,6 +78,12 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height
 | 
			
		||||
    int mouse_x = event->button.x;
 | 
			
		||||
    int mouse_y = event->button.y;
 | 
			
		||||
 | 
			
		||||
    /* When launcher open, check for outside click to close it. */
 | 
			
		||||
    if(_launcher_is_open && !_launcher->is_point_inside(mouse_x, mouse_y, screen_height)) {
 | 
			
		||||
      _launcher_is_open = false;
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /* Find the top-most window that was clicked. */
 | 
			
		||||
    for(int i = _windows.size()-1; i >= 0; --i) {
 | 
			
		||||
      if(_windows[i].get()->is_point_inside(mouse_x, mouse_y)) {
 | 
			
		||||
@ -98,14 +94,27 @@ void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  } 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);
 | 
			
		||||
    if(_taskbar->is_start_button_clicked(event, screen_height)) {
 | 
			
		||||
      _launcher_is_open = !_launcher_is_open;
 | 
			
		||||
    } else if(_launcher_is_open) {
 | 
			
		||||
      std::string app_to_launch = _launcher->handle_event(event, screen_height);
 | 
			
		||||
      if(app_to_launch == "Terminal") {
 | 
			
		||||
        auto term = std::make_unique<Terminal>(_network);
 | 
			
		||||
        auto term_window = std::make_unique<UIWindow>("Terminal", 150, 150, 800, 500);
 | 
			
		||||
        term_window->set_content(std::move(term));
 | 
			
		||||
        add_window(std::move(term_window));
 | 
			
		||||
        _launcher_is_open = false;
 | 
			
		||||
      }
 | 
			
		||||
    } else {
 | 
			
		||||
      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) {
 | 
			
		||||
@ -154,12 +163,20 @@ UIWindow* Desktop::get_focused_window(void) {
 | 
			
		||||
void Desktop::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
                     int screen_height, bool show_cursor) {
 | 
			
		||||
  _render_wallpaper(txt_renderer);
 | 
			
		||||
  if(_launcher_is_open) {
 | 
			
		||||
    _launcher->render(shape_renderer, txt_renderer, screen_height);
 | 
			
		||||
  }
 | 
			
		||||
  _taskbar->render(shape_renderer, txt_renderer, _windows, _focused_window);
 | 
			
		||||
  /* Render non-focused windows first. */
 | 
			
		||||
  for(const auto& win : _windows) {
 | 
			
		||||
    if(!win->is_minimized()) {
 | 
			
		||||
    if(win.get() != _focused_window && !win->is_minimized()) {
 | 
			
		||||
      win.get()->render(shape_renderer, txt_renderer, screen_height, show_cursor);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  /* Render focused window last so it's on top. */
 | 
			
		||||
  if(_focused_window && !_focused_window->is_minimized()) {
 | 
			
		||||
    _focused_window->render(shape_renderer, txt_renderer, screen_height, show_cursor);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Desktop::_render_wallpaper(TextRenderer* txt_renderer) {
 | 
			
		||||
 | 
			
		||||
@ -4,13 +4,15 @@
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <SDL3/SDL.h>
 | 
			
		||||
 | 
			
		||||
#include "client_network.h"
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "ui/ui_window.h"
 | 
			
		||||
#include "ui/taskbar.h"
 | 
			
		||||
#include "ui/launcher.h"
 | 
			
		||||
 | 
			
		||||
/* Animated background stuff. */
 | 
			
		||||
struct ScollingText {
 | 
			
		||||
struct ScrollingText {
 | 
			
		||||
  std::string text;
 | 
			
		||||
  float x;
 | 
			
		||||
  float y;
 | 
			
		||||
@ -19,7 +21,7 @@ struct ScollingText {
 | 
			
		||||
 | 
			
		||||
class Desktop {
 | 
			
		||||
public:
 | 
			
		||||
  Desktop(int screen_width, int screen_height);
 | 
			
		||||
  Desktop(int screen_width, int screen_height, ClientNetwork* network);
 | 
			
		||||
  ~Desktop(void);
 | 
			
		||||
 | 
			
		||||
  void add_window(std::unique_ptr<UIWindow> window);
 | 
			
		||||
@ -35,8 +37,11 @@ private:
 | 
			
		||||
  void _render_wallpaper(TextRenderer* txt_renderer);
 | 
			
		||||
 | 
			
		||||
  std::vector<std::unique_ptr<UIWindow>> _windows;
 | 
			
		||||
  std::unique_ptr<Taskbar>  _taskbar;
 | 
			
		||||
  UIWindow*                 _focused_window;
 | 
			
		||||
  std::vector<ScollingText> _background_text;
 | 
			
		||||
  std::vector<std::string>  _snippets;
 | 
			
		||||
  std::unique_ptr<Taskbar>   _taskbar;
 | 
			
		||||
  std::unique_ptr<Launcher>  _launcher;
 | 
			
		||||
  UIWindow*                  _focused_window;
 | 
			
		||||
  ClientNetwork*             _network;
 | 
			
		||||
  std::vector<ScrollingText> _background_text;
 | 
			
		||||
  std::vector<std::string>   _snippets;
 | 
			
		||||
  bool                       _launcher_is_open;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								client/src/ui/launcher.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								client/src/ui/launcher.cpp
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
#include "launcher.h"
 | 
			
		||||
#include <SDL3/SDL_mouse.h>
 | 
			
		||||
#include "gfx/shape_renderer.h"
 | 
			
		||||
#include "gfx/txt_renderer.h"
 | 
			
		||||
#include "gfx/types.h"
 | 
			
		||||
 | 
			
		||||
Launcher::Launcher(int x, int y, int width) : _x(x), _y(y), _width(width) {
 | 
			
		||||
  /* TODO: Hardcode the launcher apps for now. */
 | 
			
		||||
  _apps.push_back("Terminal");
 | 
			
		||||
 | 
			
		||||
  int item_height = 30;
 | 
			
		||||
  _height = _apps.size() * item_height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Launcher::render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer,
 | 
			
		||||
                      int screen_height) {
 | 
			
		||||
  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);
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    txt_renderer->render_text(app_name.c_str(), _x + 10, item_y + 8, 1.0f, text_color);
 | 
			
		||||
    item_y += item_height;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 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) {
 | 
			
		||||
        return app_name; /* Return name of clicked app. */
 | 
			
		||||
      }
 | 
			
		||||
      item_y += item_height;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return ""; /* Nothing clicked. */
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								client/src/ui/launcher.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								client/src/ui/launcher.h
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <SDL3/SDL_events.h>
 | 
			
		||||
 | 
			
		||||
class ShapeRenderer;
 | 
			
		||||
class TextRenderer;
 | 
			
		||||
 | 
			
		||||
class Launcher {
 | 
			
		||||
public:
 | 
			
		||||
  Launcher(int x, int y, int width);
 | 
			
		||||
  ~Launcher(void);
 | 
			
		||||
 | 
			
		||||
  void render(ShapeRenderer* shape_renderer, TextRenderer* txt_renderer, int screen_height);
 | 
			
		||||
  std::string handle_event(SDL_Event* event, int screen_height);
 | 
			
		||||
  bool is_point_inside(int x, int y, int screen_height) const;
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
  int _x, _y, _width, _height;
 | 
			
		||||
  std::vector<std::string> _apps;
 | 
			
		||||
};
 | 
			
		||||
@ -13,7 +13,8 @@
 | 
			
		||||
Taskbar::Taskbar(int screen_width, int screen_height) {
 | 
			
		||||
  _width  = screen_width;
 | 
			
		||||
  _height = 32;
 | 
			
		||||
  _y_pos  = 0; /* Taksbar at bottom because boring? */
 | 
			
		||||
  _y_pos  = 0; /* Taskbar at bottom because boring? */
 | 
			
		||||
  _start_button_width = 60;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Taskbar::~Taskbar(void) {}
 | 
			
		||||
@ -27,24 +28,30 @@ 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);
 | 
			
		||||
 | 
			
		||||
  /* Draw start button. */
 | 
			
		||||
  shape_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);
 | 
			
		||||
 | 
			
		||||
  /* Draw app buttons. */
 | 
			
		||||
  int button_width  = 150;
 | 
			
		||||
  int padding       = 5;
 | 
			
		||||
  int x_offset      = padding;
 | 
			
		||||
  int x_offset      = _start_button_width + padding;
 | 
			
		||||
 | 
			
		||||
  for(const auto& window : windows) {
 | 
			
		||||
    bool is_focused = (window.get() == focused_window);
 | 
			
		||||
    shape_renderer->draw_rect(x_offset, _y_pos + padding, button_width, 
 | 
			
		||||
    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, 
 | 
			
		||||
    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;
 | 
			
		||||
 | 
			
		||||
  }
 | 
			
		||||
    /* Draw clock. */
 | 
			
		||||
    auto now = std::chrono::system_clock::now();
 | 
			
		||||
    auto in_time_t = std::chrono::system_clock::to_time_t(now);
 | 
			
		||||
@ -52,9 +59,8 @@ 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, 
 | 
			
		||||
    txt_renderer->render_text(time_str.c_str(), _width-50, _y_pos+11, 1.0f,
 | 
			
		||||
                              button_text_color);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height,
 | 
			
		||||
@ -63,21 +69,30 @@ UIWindow* Taskbar::handle_event(SDL_Event* event, int screen_height,
 | 
			
		||||
    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;
 | 
			
		||||
    int button_width  = 150;
 | 
			
		||||
    int padding       = 5;
 | 
			
		||||
    int x_offset      = _start_button_width + padding;
 | 
			
		||||
    for(const auto& window : windows) {
 | 
			
		||||
      if(mouse_x >= x_offset && mouse_x <= x_offset + button_width &&
 | 
			
		||||
         mouse_y >= _y_pos && mouse_y <= _y_pos + _height) {
 | 
			
		||||
        return window.get(); /* Return clicked window. */
 | 
			
		||||
      }
 | 
			
		||||
      x_offset += button_width + padding;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return nullptr; /* No window button was clicked. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
    return (mouse_x >= 0 && mouse_x <= _start_button_width && mouse_y >= _y_pos &&
 | 
			
		||||
            mouse_y <= _y_pos + _height);
 | 
			
		||||
  }
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Taskbar::get_height(void) const {
 | 
			
		||||
  return _height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -18,11 +18,13 @@ public:
 | 
			
		||||
              UIWindow* focused_window);
 | 
			
		||||
 | 
			
		||||
  UIWindow* handle_event(SDL_Event* event, int screen_height,
 | 
			
		||||
                    const std::vector<std::unique_ptr<UIWindow>>& windows);
 | 
			
		||||
                                    const std::vector<std::unique_ptr<UIWindow>>& windows);
 | 
			
		||||
  bool is_start_button_clicked(SDL_Event* event, int screen_height);
 | 
			
		||||
 | 
			
		||||
  int get_height(void) const;
 | 
			
		||||
private:
 | 
			
		||||
  int _width;
 | 
			
		||||
  int _height;
 | 
			
		||||
  int _y_pos;
 | 
			
		||||
  int _start_button_width;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user