#include #include #include #include #include #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/taskbar.h" #include #include #include "ui/ui_window.h" static const std::string& get_random_snippet(const std::vector& snippets) { if(snippets.empty()) { static const std::string err = "ERR"; return err; } return snippets[rand() % snippets.size()]; } Desktop::Desktop(int screen_width, int screen_height, ClientNetwork* network) { _taskbar = std::make_unique(screen_width, screen_height); _network = network; _focused_window = nullptr; _launcher_is_open = false; _launcher = std::make_unique(5, 5 + _taskbar->get_height(), 200); /* Load snippets for temp wallpaper. */ std::ifstream snippet_file("assets/menu_background_snippets.txt"); if(snippet_file.is_open()) { std::string line; while(std::getline(snippet_file, line)) { if(!line.empty()) { _snippets.push_back(line); } } } /* Init animated background. */ for(int i = 0; i < 100; ++i) { float y_pos = (float)(rand() % screen_height); _background_text.push_back( {get_random_snippet(_snippets), (float)(rand() % screen_width), y_pos, (float)(rand() % 30 + 10) / 100.0f, (int)y_pos }); } } /* Desktop owns UIWindow, make sure we delete them. */ Desktop::~Desktop(void) {} void Desktop::add_window(std::unique_ptr window) { _windows.push_back(std::move(window)); /* Focus new window. */ _set_focused_window(_windows.back().get()); } 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); } } void Desktop::handle_event(SDL_Event* event, int screen_width, int screen_height) { if(event->type == SDL_EVENT_MOUSE_BUTTON_DOWN) { 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)) { if(!_windows[i]->is_minimized()) { _set_focused_window(_windows[i].get()); } break; } } } else if(event->type == SDL_EVENT_MOUSE_BUTTON_UP) { 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(_network); auto term_window = std::make_unique("Terminal", 150, 150, 800, 500); term_window->set_content(std::move(term)); add_window(std::move(term_window)); _launcher_is_open = false; } else if(app_to_launch == "Editor") { auto editor = std::make_unique(); auto editor_window = std::make_unique("Editor", 200, 200, 600, 400); editor_window->set_content(std::move(editor)); add_window(std::move(editor_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) { 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) { _focused_window->handle_event(event, screen_width, screen_height, _taskbar->get_height()); IWindowContent* content = _focused_window->get_content(); if(content) { /* * Only terminal should receive direct text input this way. * Editor gets it through the generic handle_event call. */ content->handle_input(event); } } } void Desktop::update(int screen_width, int screen_height) { /* Remove closed windows. */ _update_wallpaper(screen_width, screen_height); _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [this](const std::unique_ptr& w) { if (w->should_close()) { if (w.get() == _focused_window) { _focused_window = nullptr; } return true; } return false; }), _windows.end()); } UIWindow* Desktop::get_focused_window(void) { return _focused_window; } 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.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) { 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); } } void Desktop::_update_wallpaper(int screen_width, int screen_height) { for(auto& line : _background_text) { line.y_precise -= line.speed; line.render_y = static_cast(std::round(line.y_precise)); if(line.render_y < 0) { line.y_precise = screen_height; line.x = (float)(rand() % screen_width); line.text = get_random_snippet(_snippets); } } }