#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; di0.id = 0; di0.place_x = 0 * 50 + 1; di0.place_y = 10; di0.icon_size_x = 45; di0.icon_size_y = 50; di0.name = "Counter"; di0.program_class = "CounterProgram"; items.push_back(di0); DesktopItem di1; di1.id = 4; di1.place_x = 4 * 50 + 1; di1.place_y = 10; di1.icon_size_x = 45; di1.icon_size_y = 50; di1.name = "Text Editor"; di1.program_class = "TextEditorProgram"; items.push_back(di1); DesktopItem di2; di2.id = 2; di2.place_x = 2 * 50 + 1; di2.place_y = 10; di2.icon_size_x = 45; di2.icon_size_y = 50; di2.name = "Calculator"; di2.program_class = "CalculatorProgram"; items.push_back(di2); DesktopItem di3; di3.id = 3; di3.place_x = 3 * 50 + 1; di3.place_y = 10; di3.icon_size_x = 45; di3.icon_size_y = 50; di3.name = "Game"; di3.program_class = "Chatty"; items.push_back(di3); DesktopItem di4; di4.id = 1; di4.place_x = 1 * 50 + 1; di4.place_y = 10; di4.icon_size_x = 45; di4.icon_size_y = 50; di4.name = "Settings"; di4.program_class = "SettingsProgram"; items.push_back(di4); DesktopItem di5; di5.id = 5; di5.place_x = 5 * 50 + 1; di5.place_y = 10; di5.icon_size_x = 45; di5.icon_size_y = 50; di5.name = "Internet Tester"; di5.program_class = "InternetTester"; items.push_back(di5); 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 short window_id = handle_window_click(event); if (window_id >= 0) { display_state->update_display.store(true); return; } short icon_id = handle_desktop_click(event); if (icon_id != -1) { display_state->update_display.store(true); return; } short 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) { short total_pos_x = window->x + window->width; short 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); for (short i = 0; i < task_bar_items.size(); ++i) { task_bar_items[i].focused = false; } TaskBarItem item; item.id = window->id; item.focused = true; 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(short window_id) { for (auto it = windows.begin(); it != windows.end(); ++it) { if ((*it)->id == window_id) { Window* window_to_delete = *it; windows.erase(it); _system_manager->close_program(window_to_delete->id); delete window_to_delete; 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(short window_id) { for (auto it = windows.begin(); it != windows.end(); ++it) { for (short 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() { draw_background(0x0410); draw_task_bar(0xbdf7); for (const auto& item : items) { draw_desktop_Item(item); } for (const auto& win : windows) { if (win->minimized) continue; draw_window(*win); } } 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); } for (ContentButton button : window.content_buttons) { tft->draw_button(button.x, button.y, button.width, button.height, COL_GREY, button.pressed, button.action); } } // ======= Desktop ======= void Shell::draw_background(short 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(short 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() { } short Shell::handle_window_click(CLICK_EVENT event) { // Iterate BACKWARDS - last window is on top for (short 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) { short x = window->x + it->x_offset; short 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; } } // 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; } short 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(); 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; } short Shell::handle_taskbar_click(CLICK_EVENT event) { for (short 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 (short j = 0; j < windows.size(); ++j) { if (windows[j]->id != task_bar_items[i].id) continue; for (short k = 0; k < windows.size(); ++k) windows[k]->focused = false; windows[j]->focused = true; windows[j]->minimized = false; auto it = windows.begin() + j; std::rotate(it, it + 1, windows.end()); } for (short 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; }