#include #include "shell.h" #include "GLOBALS.h" #include #include "system_manager.h" #include "helpers.h" void Shell::init(TFT_Handler* th, DISPLAY_STATE* ds, SystemManager* system_manager) { tft = th; display_state = ds; _system_manager = system_manager; EventManager::getInstance().subscribe(this); draw_background(COL_TEAL); draw_task_bar(COL_GREY); // Create desktop items with their associated program classes DesktopItem di0 = { .id = 0, .place_x = 0 * 50 + 1, .place_y = 10, .icon_size_x = 45, .icon_size_y = 50, .name = "Counter", .program_class = "CounterProgram" }; items.push_back(di0); DesktopItem di1 = { .id = 1, .place_x = 1 * 50 + 1, .place_y = 10, .icon_size_x = 45, .icon_size_y = 50, .name = "Text Editor", .program_class = "TextEditorProgram" }; items.push_back(di1); DesktopItem di2 = { .id = 2, .place_x = 2 * 50 + 1, .place_y = 10, .icon_size_x = 45, .icon_size_y = 50, .name = "Calculator", .program_class = "CalculatorProgram" }; items.push_back(di2); DesktopItem di3 = { .id = 3, .place_x = 3 * 50 + 1, .place_y = 10, .icon_size_x = 45, .icon_size_y = 50, .name = "Game", .program_class = "GameProgram" }; items.push_back(di3); DesktopItem di4 = { .id = 4, .place_x = 4 * 50 + 1, .place_y = 10, .icon_size_x = 45, .icon_size_y = 50, .name = "Settings", .program_class = "SettingsProgram" }; items.push_back(di4); for (const auto& item : items) { draw_desktop_Item(item); } } void Shell::on_click_event(CLICK_EVENT event) { if (event.event != CLICK_EVENTS::LEFT_CLICK) return; // Always check windows first int window_id = handle_window_click(event); if (window_id >= 0) { display_state->update_display.store(true); return; } int icon_id = handle_desktop_click(event); if (icon_id != -1) { display_state->update_display.store(true); return; } int tast_icon_id = handle_taskbar_click(event); if (tast_icon_id != -1) { display_state->update_display.store(true); return; } bool ckicked_start_button = handle_start_button_click(event); if (ckicked_start_button) { display_state->update_display.store(true); return; } } // ======= Window ======= void Shell::create_window(Window* window) { int total_pos_x = window->x + window->width; int total_pos_y = window->y + window->height; // Clamp window within the screen size AND taskbar if (total_pos_x > MAX_SCREEN_WIDTH) window->x = MAX_SCREEN_WIDTH - window->width; if (total_pos_y > MAX_SCREEN_HEIGHT) window->y = MAX_SCREEN_HEIGHT - window->height - 29; windows.push_back(window); TaskBarItem item; item.id = window->id; item.focused = window->focused; item.name = window->title; item.width = 95; item.height = 18; item.offset_x = 100; item.place_y = 298; // Set place_x and place_y for ALL items if (task_bar_items.size() == 0) { item.place_x = 60; } else { // For subsequent items, use the previous item's position + offset const TaskBarItem& prev = task_bar_items.back(); item.place_x = prev.place_x + prev.offset_x; } task_bar_items.push_back(item); display_state->update_display.store(true); } void Shell::close_window(int window_id) { for (auto it = windows.begin(); it != windows.end(); ++it) { if ((*it)->id == window_id) { windows.erase(it); _system_manager->close_program((*it)->id); display_state->update_display.store(true); break; } } for (auto it = task_bar_items.begin(); it != task_bar_items.end(); ++it) { if (it->id == window_id) { task_bar_items.erase(it); display_state->update_display.store(true); break; } } Helpers::recalculate_taskbar(task_bar_items); } void Shell::minimize_window(int window_id) { for (auto it = windows.begin(); it != windows.end(); ++it) { for (int i = 0; i < task_bar_items.size(); ++i) { if ((*it)->id == window_id && (*it)->id == task_bar_items[i].id) { (*it)->minimized = true; task_bar_items[i].focused = false; display_state->update_display.store(true); break; } } } } void Shell::draw_all() { tft->start_draw(); draw_background(0x0410); draw_task_bar(0xbdf7); for (const auto& item : items) { draw_desktop_Item(item); } for (const auto& win : windows) { if (!win->minimized) draw_window(*win); } tft->end_draw(); } void Shell::draw_window(const Window& window) { if (window.minimized) return; // Outer shadow tft->draw_rect(window.x, window.y, window.width + 2, window.height + 2, 1, 0x0000); // Inner shadow tft->draw_rect(window.x, window.y, window.width + 1, window.height + 1, 1, 0x8410); // Highlight tft->draw_rect(window.x - 1, window.y - 1, window.width + 1, window.height + 1, 1, 0xffff); // Window tft->draw_box(window.x, window.y, window.width, window.height, window.background_color); // Decorations tft->draw_box(window.x + 3, window.y + 3, window.width - 6, 30, window.foreground_color); for (auto it = window.window_decorations.begin(); it != window.window_decorations.end(); ++it) { if (it->action == WindowAction::CLOSE) { // Close button tft->draw_box(window.x + it->x_offset, window.y + it->y_offset, it->height, it->width, COL_RED); } if (it->action == WindowAction::MINIMIZE) { // Minimize button tft->draw_box(window.x + it->x_offset, window.y + it->y_offset, it->height, it->width, COL_LIGHT_GREY); } } // Window title tft->draw_text(window.x + 6 + 65, window.y + 10, window.title, COL_WHITE, 2); // Window content tft->draw_box(window.x + 3, window.y + 35, window.width - 6, window.height - 38, COL_WHITE); for (WindowContentText text : window.window_content_text) { tft->draw_text(text.x, text.y, text.text, COL_BLACK, text.size); } if (window.hasSprite) tft->push_sprite(window.sprite); } // ======= Desktop ======= void Shell::draw_background(int color) { tft->fill_screen(color); } void Shell::draw_desktop_Item(const DesktopItem& desktop_item) { tft->draw_box(desktop_item.place_x, desktop_item.place_y, desktop_item.icon_size_x, desktop_item.icon_size_y, 0x4648); } void Shell::draw_task_bar(int color) { // bar tft->draw_box(0, 293, 480, 40, color); tft->draw_line(0, 294, 480, 294, 0xffff); // Outer shadow tft->draw_box(5, 297, 53, 21, 0x0000); // Inner shadow tft->draw_box(6, 298, 51, 19, 0x8410); // Highlight tft->draw_box(5, 297, 51, 19, 0xffff); // Button tft->draw_box(6, 298, 50, 18, color); for (TaskBarItem item : task_bar_items) { if (item.focused) { tft->draw_box(item.place_x - 2, item.place_y - 2, item.width + 3, item.height + 3, COL_BLACK); tft->draw_box(item.place_x - 1, item.place_y - 1, item.width + 2, item.height + 2, COL_DARK_GREY); tft->draw_box(item.place_x, item.place_y, item.width + 2, item.height + 2, COL_WHITE); tft->draw_box(item.place_x, item.place_y, item.width, item.height, COL_GREY); tft->draw_text(item.place_x + 2, item.place_y + 2, item.name, COL_BLACK, 1); } else { tft->draw_box(item.place_x - 1, item.place_y - 1, item.width + 1, item.height + 1, COL_WHITE); tft->draw_box(item.place_x, item.place_y, item.width + 2, item.height + 2, COL_BLACK); tft->draw_rect(item.place_x, item.place_y, item.width + 1, item.height + 1, 1, COL_DARK_GREY); tft->draw_box(item.place_x, item.place_y, item.width, item.height, COL_GREY); tft->draw_text(item.place_x + 2, item.place_y + 2, item.name, COL_BLACK, 1); } } } void Shell::draw_start_menu() { } int Shell::handle_window_click(CLICK_EVENT event) { // Iterate BACKWARDS - last window is on top for (int i = windows.size() - 1; i >= 0; i--) { Window* window = windows[i]; if (window->minimized) continue; // Check if click is within window bounds if (event.x >= window->x && event.x <= (window->x + window->width) && event.y >= window->y && event.y <= (window->y + window->height)) { for (auto it = window->window_decorations.begin(); it != window->window_decorations.end(); ++it) { int x = window->x + it->x_offset; int y = window->y + it->y_offset; // Check if click is within either of the buttons if (event.x >= x && event.x <= (x + it->width) && event.y >= y && event.y <= (y + it->height) && it->action == WindowAction::CLOSE && !window->minimized) { close_window(window->id); return window->id; } if (event.x >= x && event.x <= (x + it->width) && event.y >= y && event.y <= (y + it->height) && it->action == WindowAction::MINIMIZE) { minimize_window(window->id); return window->id; } } // If the window is already focused, return if (window->focused) return -1; // Unfocus all windows for (auto& win : windows) { win->focused = false; } // Focus this window window->focused = true; // Move window to end (top of z-order) auto it = windows.begin() + i; std::rotate(it, it + 1, windows.end()); return window->id; } } return -1; } int Shell::handle_desktop_click(CLICK_EVENT event) { for (const auto& item : items) { if (event.x >= item.place_x && event.x <= (item.place_x + item.icon_size_x) && event.y >= item.place_y && event.y <= (item.place_y + item.icon_size_y)) { // Check if program is already running for (const auto& prog : _system_manager->_programs) { if (prog->get_name() == item.name) { return -1; // Already running } } // Create window Window* win = new Window(&tft->tft); WindowDecoration close_btn = { .x_offset = 6, .y_offset = 5, .width = 25, .height = 25, .action = WindowAction::CLOSE, }; WindowDecoration minimize_btn = { .x_offset = 36, .y_offset = 5, .width = 25, .height = 25, .action = WindowAction::MINIMIZE, }; win->window_decorations.push_back(close_btn); win->window_decorations.push_back(minimize_btn); win->x = 10; win->y = 0; win->width = 350; win->height = 250; win->background_color = 0xbdf7; win->foreground_color = COL_DARK_BLUE; win->focused = true; win->minimized = false; win->title = item.name; win->window_content_text = {}; // Spawn program using the program_class bool result = _system_manager->spawn_program(win, item.program_class); if (!result) { delete win; return -1; } create_window(win); return item.id; } } return -1; } int Shell::handle_taskbar_click(CLICK_EVENT event) { for (int i = 0; i < task_bar_items.size(); ++i) { if (event.x >= task_bar_items[i].place_x && event.x <= (task_bar_items[i].place_x + task_bar_items[i].width) && event.y >= task_bar_items[i].place_y && event.y <= (task_bar_items[i].place_y + task_bar_items[i].height)) { for (int j = 0; j < windows.size(); ++j) { if (windows[j]->id == task_bar_items[i].id) { for (int k = 0; k < windows.size(); ++k) windows[k]->focused = false; windows[j]->focused = true; windows[j]->minimized = false; } } for (int l = 0; l < task_bar_items.size(); ++l) task_bar_items[l].focused = false; task_bar_items[i].focused = true; return task_bar_items[i].id; } } return -1; } bool Shell::handle_start_button_click(CLICK_EVENT event) { if (event.x >= 6 && event.x <= (6 + 50) && event.y >= 298 && event.y <= (298 + 18)) { Serial.println("Taskbar button clicked"); // TODO: Handle icon click (open window, etc.) return true; } return false; }