PocketPutr/Desktop_Test/shell.cpp
2026-01-06 19:50:28 +01:00

421 lines
12 KiB
C++

#include <string>
#include "shell.h"
#include "GLOBALS.h"
#include <algorithm>
#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(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);
}
}
// ======= Desktop =======
void Shell::draw_background(int color) {
tft->fill_screen(color);
}
void Shell::draw_desktop_Item(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);
Serial.println(task_bar_items.size());
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();
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;
}