#include #include #include #include #include #include #include #include #include "gfx/types.h" #include "game_state.h" #include "desktop.h" #include "terminal.h" #include "ui/i_window_content.h" #include "ui/launcher.h" #include "ui/taskbar.h" #include "ui/ui_window.h" #include "ui/editor.h" #include "ui/window_action.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, GameState* game_state, uint32_t initial_session_id) { _taskbar = std::make_unique(screen_width, screen_height); _initial_session_id = initial_session_id; _game_state= game_state; _focused_window = nullptr; _window_awaiting_session_id = nullptr; _launcher_is_open = false; _launcher = std::make_unique(5, 0, 200); /* Tmp y-coord. */ int launcher_y = screen_height - _taskbar->get_height() - _launcher->get_height(); _launcher->set_y(launcher_y); /* 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) { UIWindow* window_ptr = window.get(); _windows.push_back(std::move(window)); _taskbar->add_window(window_ptr); _set_focused_window(window_ptr); } void Desktop::_set_focused_window(UIWindow* window) { if(window == _focused_window) { return; } /* Unfocus old window. */ if(_focused_window) { _focused_window->set_focused(false); } /* Set new focused window. */ _focused_window = window; if(_focused_window) { _focused_window->set_focused(true); /* Move newly focused window to end of vector so it's rendered on top. */ auto it = std::find_if(_windows.begin(), _windows.end(), [window](const std::unique_ptr& p) { return p.get() == window; }); if(it != _windows.end()) { std::rotate(it, it+1, _windows.end()); } } } 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; /* If click is on taskbar, ignore it on the down-event. Let the up-event handle it. */ if(_taskbar->is_point_inside(mouse_x, mouse_y)) { return; } /* 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; } /* If click not on focused window, find which window should receive focus. */ bool click_on_window = false; for(int i = _windows.size()-1; i >= 0; --i) { if(_windows[i].get()->is_point_inside(mouse_x, mouse_y)) { /* Top most window. Focus it. */ UIWindow* target = _windows[i].get(); if(!target->is_minimized()) { _set_focused_window(target); /* Pass event down to newly focused window to handle dragging, etc. */ target->handle_event(event, screen_width, screen_height, _taskbar->get_height()); } click_on_window = true; break; } } /* Click wasn't on a window. Unfocus. */ if(!click_on_window) { _set_focused_window(nullptr); } } 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(_game_state); auto term_window = std::make_unique("Terminal", 150, 150, 800, 500); _window_awaiting_session_id = term_window.get(); term_window->set_content(std::move(term)); add_window(std::move(term_window)); _launcher_is_open = false; _game_state->send_create_session_request(); } 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_session_id(_initial_session_id); 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); 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) { /* Update cursor if hovering over any resize handle. */ for(int i = _windows.size() - 1; i >= 0; --i) { if(_windows[i]->is_mouse_over_resize_handle(event->motion.x, event->motion.y)) { return CursorManager::set_cursor(CursorType::RESIZE_NWSE); } } CursorManager::set_cursor(CursorType::ARROW); } /* Pass all other non-down-click events to focused window. */ if(_focused_window && event->type != SDL_EVENT_MOUSE_BUTTON_DOWN) { _focused_window->handle_event(event, screen_width, screen_height, _taskbar->get_height()); } } void Desktop::update(float dt, int screen_width, int screen_height) { /* Remove closed windows. */ if(_focused_window) { IWindowContent* content = _focused_window->get_content(); if(content) { WindowAction action = content->get_pending_action(); uint32_t session_id = _focused_window->get_session_id(); switch(action.type) { case ActionType::WRITE_FILE: { if(session_id != 0) { _game_state->send_file_write_request(session_id, action.payload1, action.payload2); } break; } case ActionType::READ_FILE: { if(session_id != 0) { _game_state->send_file_read_request(session_id, action.payload1); } break; } default: break; } } } for(auto& window : _windows) { if(window) { window->update(); } } _update_wallpaper(dt, screen_width, screen_height); _windows.erase(std::remove_if(_windows.begin(), _windows.end(), [this](const std::unique_ptr& w) { if (w->should_close()) { /* Also remove from session map. */ uint32_t session_to_remove = 0; for(auto const& [sid, win] : _session_windows) { if(win == w.get()) { session_to_remove = sid; break; } } if(session_to_remove != 0) _session_windows.erase(session_to_remove); _taskbar->remove_window(w.get()); if (w.get() == _focused_window) { _focused_window = nullptr; } return true; } return false; }), _windows.end()); } void Desktop::register_session(uint32_t session_id, UIWindow* window) { _session_windows[session_id] = window; if(_window_awaiting_session_id == window) { _window_awaiting_session_id = nullptr; } } UIWindow* Desktop::get_window_by_session_id(uint32_t session_id) { if(_session_windows.count(session_id)) { return _session_windows.at(session_id); } return nullptr; } UIWindow* Desktop::get_focused_window(void) { return _focused_window; } UIWindow* Desktop::get_window_awaiting_session_id(void) { return _window_awaiting_session_id; } void Desktop::render(const RenderContext& context) { /* Pass 1: Background. */ context.ui_renderer->begin_text(); _render_wallpaper(context.ui_renderer); context.ui_renderer->flush_text(); /* Pass 2: Windows Render in order, last window is top-most. */ for(size_t i = 0; i < _windows.size(); ++i) { auto& win_i = _windows[i]; if(win_i->is_minimized()) continue; bool is_occluded = false; Rect rect_i = win_i->get_rect(); /* Check against all windows on top of this one. */ for(size_t j = i+1; j < _windows.size(); ++j) { auto& win_j = _windows[j]; if(win_j->is_minimized()) continue; Rect rect_j = win_j->get_rect(); if(rect_i.x >= rect_j.x && (rect_i.x + rect_i.w) <= (rect_j.x + rect_j.w) && rect_i.y >= rect_j.y && (rect_i.y + rect_i.h) <= (rect_j.y + rect_j.h)) { is_occluded = true; break; } } if(!is_occluded) { win_i->render(context); } } /* Pass 3: Top-level static UI (taskbar, Launcher, etc.). */ context.ui_renderer->begin_text(); if(_launcher_is_open) { _launcher->render(context.ui_renderer); } _taskbar->render(context.ui_renderer, _focused_window); context.ui_renderer->flush_text(); } void Desktop::_render_wallpaper(UIRenderer* ui_renderer) { const Color wallpaper_color = { 0.0f, 0.15f, 0.08f }; for(auto& line : _background_text) { ui_renderer->render_text(line.text.c_str(), std::round(line.x), line.render_y, wallpaper_color); } } void Desktop::_update_wallpaper(float dt, int screen_width, int screen_height) { for(auto& line : _background_text) { line.y_precise -= line.speed * dt * 100.0f; 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); } } }