feat: add word processor, spreadsheet, kernel and userspace memory improvements

This commit is contained in:
2026-03-05 19:18:11 +01:00
parent fcb6f8e247
commit ead7d296f7
28 changed files with 4146 additions and 56 deletions
+89 -17
View File
@@ -69,6 +69,7 @@ void gui::desktop_init(DesktopState* ds) {
ds->icon_procmgr = svg_load("0:/icons/system-monitor.svg", 20, 20, defColor);
ds->icon_mandelbrot = svg_load("0:/icons/applications-science.svg", 20, 20, defColor);
ds->icon_devexplorer = svg_load("0:/icons/hardware.svg", 20, 20, defColor);
ds->icon_spreadsheet = svg_load("0:/icons/spreadsheet.svg", 20, 20, defColor);
// Settings defaults
ds->settings.bg_gradient = true;
@@ -428,7 +429,7 @@ void gui::desktop_draw_panel(DesktopState* ds) {
static constexpr int MENU_W = 220;
static constexpr int MENU_ITEM_H = 36;
static constexpr int MENU_CAT_H = 28;
static constexpr int MENU_CAT_H = 32;
static constexpr int MENU_DIV_H = 10;
struct MenuRow {
@@ -437,30 +438,53 @@ struct MenuRow {
int app_id; // -1 for category headers / dividers
};
static constexpr int MENU_ROW_COUNT = 20;
static constexpr int MENU_ROW_COUNT = 22;
static const MenuRow menu_rows[MENU_ROW_COUNT] = {
{ true, "Applications", -1 },
{ true, "Applications", -1 }, // cat 0
{ false, "Terminal", 0 },
{ false, "Files", 1 },
{ false, "Text Editor", 4 },
{ false, "Word Processor", 15 },
{ false, "Spreadsheet", 16 },
{ false, "Calculator", 3 },
{ true, "Internet", -1 },
{ true, "Internet", -1 }, // cat 1
{ false, "Wikipedia", 9 },
{ false, "Weather", 13 },
{ true, "System", -1 },
{ true, "System", -1 }, // cat 2
{ false, "System Info", 2 },
{ false, "Kernel Log", 5 },
{ false, "Processes", 6 },
{ false, "Devices", 8 },
{ true, "Games", -1 },
{ true, "Games", -1 }, // cat 3
{ false, "Mandelbrot", 7 },
{ false, "DOOM", 10 },
{ true, "", -1 }, // divider
{ true, "", -1 }, // divider (always visible)
{ false, "Settings", 11 },
{ false, "Reboot", 12 },
{ false, "Shutdown", 14 },
};
// Collapsible category state (categories 0-3 are toggleable; divider category always expanded)
static constexpr int MENU_NUM_CATS = 5;
static bool menu_cat_expanded[MENU_NUM_CATS] = { false, false, false, false, true };
// Map a menu_rows index to its category index (0-4), or -1 for items before any category
static int menu_get_cat(int row_idx) {
int cat = -1;
for (int i = 0; i <= row_idx; i++)
if (menu_rows[i].is_category) cat++;
return cat;
}
// Check if a row should be visible given current expand/collapse state
static bool menu_row_visible(int row_idx) {
const MenuRow& row = menu_rows[row_idx];
if (row.is_category) return true; // category headers always visible
int cat = menu_get_cat(row_idx);
if (cat < 0 || cat >= MENU_NUM_CATS) return true;
return menu_cat_expanded[cat];
}
static int menu_row_height(const MenuRow& row) {
if (!row.is_category) return MENU_ITEM_H;
return row.label[0] ? MENU_CAT_H : MENU_DIV_H;
@@ -469,7 +493,8 @@ static int menu_row_height(const MenuRow& row) {
static int menu_total_height() {
int h = 10; // top + bottom padding
for (int i = 0; i < MENU_ROW_COUNT; i++)
h += menu_row_height(menu_rows[i]);
if (menu_row_visible(i))
h += menu_row_height(menu_rows[i]);
return h;
}
@@ -488,7 +513,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
draw_rect(fb, menu_x, menu_y, MENU_W, menu_h, colors::BORDER);
// Icon lookup by app_id
SvgIcon* icons[15] = {
SvgIcon* icons[17] = {
&ds->icon_terminal, // 0
&ds->icon_filemanager, // 1
&ds->icon_sysinfo, // 2
@@ -504,29 +529,64 @@ static void desktop_draw_app_menu(DesktopState* ds) {
&ds->icon_reboot, // 12
&ds->icon_weather, // 13
&ds->icon_shutdown, // 14
&ds->icon_texteditor, // 15
&ds->icon_spreadsheet, // 16
};
int mx = ds->mouse.x;
int my = ds->mouse.y;
int iy = menu_y + 5;
int cur_cat = -1;
for (int i = 0; i < MENU_ROW_COUNT; i++) {
const MenuRow& row = menu_rows[i];
if (row.is_category) cur_cat++;
if (!menu_row_visible(i)) continue;
int row_h = menu_row_height(row);
if (row.is_category) {
// Separator line above (except first category)
if (i > 0) {
for (int sx = menu_x + 8; sx < menu_x + MENU_W - 8; sx++)
fb.put_pixel(sx, iy + row_h / 2, colors::BORDER);
fb.put_pixel(sx, iy + 1, colors::BORDER);
}
// Category label (dimmed), skip for divider-only rows
// Category header with hover + expand/collapse indicator
if (row.label[0]) {
int tx = menu_x + 12;
Rect cat_rect = {menu_x + 4, iy, MENU_W - 8, row_h};
bool hovered = cat_rect.contains(mx, my);
if (hovered)
fill_rounded_rect(fb, cat_rect.x, cat_rect.y, cat_rect.w, cat_rect.h, 4,
Color::from_rgb(0xE8, 0xE8, 0xE8));
// Arrow indicator: > for collapsed, v for expanded
bool expanded = (cur_cat >= 0 && cur_cat < MENU_NUM_CATS) && menu_cat_expanded[cur_cat];
int ax = menu_x + 10;
int ay = iy + row_h / 2;
Color arrow_color = Color::from_rgb(0x88, 0x88, 0x88);
if (expanded) {
// Down arrow (v shape)
for (int d = 0; d < 4; d++) {
fb.put_pixel(ax + d, ay - 2 + d, arrow_color);
fb.put_pixel(ax + d + 1, ay - 2 + d, arrow_color);
fb.put_pixel(ax + 7 - d, ay - 2 + d, arrow_color);
fb.put_pixel(ax + 6 - d, ay - 2 + d, arrow_color);
}
} else {
// Right arrow (> shape)
for (int d = 0; d < 4; d++) {
fb.put_pixel(ax + d, ay - 3 + d, arrow_color);
fb.put_pixel(ax + d, ay - 3 + d + 1, arrow_color);
fb.put_pixel(ax + d, ay + 3 - d, arrow_color);
fb.put_pixel(ax + d, ay + 3 - d - 1, arrow_color);
}
}
int tx = menu_x + 24;
int ty = iy + (row_h - system_font_height()) / 2;
if (i > 0) ty += 2;
draw_text(fb, tx, ty, row.label, Color::from_rgb(0x88, 0x88, 0x88));
draw_text(fb, tx, ty, row.label, Color::from_rgb(0x66, 0x66, 0x66));
}
} else {
Rect item_rect = {menu_x + 4, iy, MENU_W - 8, row_h};
@@ -539,7 +599,7 @@ static void desktop_draw_app_menu(DesktopState* ds) {
// Icon
int icon_x = item_rect.x + 8;
int icon_y = item_rect.y + (row_h - 20) / 2;
if (row.app_id >= 0 && row.app_id < 15) {
if (row.app_id >= 0 && row.app_id < 17) {
SvgIcon* icon = icons[row.app_id];
if (icon && icon->pixels) {
fb.blit_alpha(icon_x, icon_y, icon->width, icon->height, icon->pixels);
@@ -1217,13 +1277,19 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
Rect menu_rect = {menu_x, menu_y, MENU_W, menu_h};
if (menu_rect.contains(mx, my)) {
// Walk rows to find which one was clicked
// Walk visible rows to find which one was clicked
int iy = menu_y + 5;
int cur_cat = -1;
for (int i = 0; i < MENU_ROW_COUNT; i++) {
const MenuRow& row = menu_rows[i];
if (row.is_category) cur_cat++;
if (!menu_row_visible(i)) continue;
int row_h = menu_row_height(row);
if (my >= iy && my < iy + row_h) {
if (!row.is_category) {
if (row.is_category && row.label[0] && cur_cat >= 0 && cur_cat < MENU_NUM_CATS - 1) {
// Toggle category expand/collapse
menu_cat_expanded[cur_cat] = !menu_cat_expanded[cur_cat];
} else if (!row.is_category) {
switch (row.app_id) {
case 0: open_terminal(ds); break;
case 1: open_filemanager(ds); break;
@@ -1240,6 +1306,8 @@ void gui::desktop_handle_mouse(DesktopState* ds) {
case 12: open_reboot_dialog(ds); break;
case 13: open_weather(ds); break;
case 14: open_shutdown_dialog(ds); break;
case 15: open_wordprocessor(ds); break;
case 16: open_spreadsheet(ds); break;
}
ds->app_menu_open = false;
}
@@ -1509,6 +1577,10 @@ void gui::desktop_handle_keyboard(DesktopState* ds, const Montauk::KeyEvent& key
open_texteditor(ds);
return;
}
if (key.ascii == 'w' || key.ascii == 'W') {
open_wordprocessor(ds);
return;
}
if (key.ascii == 'k' || key.ascii == 'K') {
open_klog(ds);
return;