From 326416636dfcc69b9c46ad5ce4b9c70bbde7ce8d Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 7 Aug 2026 11:41:47 +0200 Subject: [PATCH] feat: desktop - use MTK buttons for power dialogs --- kernel/src/Api/BuildNo.hpp | 2 +- programs/src/desktop/dialogs.cpp | 331 ++++++++++++------------------- 2 files changed, 129 insertions(+), 204 deletions(-) diff --git a/kernel/src/Api/BuildNo.hpp b/kernel/src/Api/BuildNo.hpp index 5edc9c2..f3e9e0e 100644 --- a/kernel/src/Api/BuildNo.hpp +++ b/kernel/src/Api/BuildNo.hpp @@ -12,4 +12,4 @@ #pragma once -#define MONTAUK_BUILD_NUMBER 87 +#define MONTAUK_BUILD_NUMBER 88 diff --git a/programs/src/desktop/dialogs.cpp b/programs/src/desktop/dialogs.cpp index 25f288d..933018b 100644 --- a/programs/src/desktop/dialogs.cpp +++ b/programs/src/desktop/dialogs.cpp @@ -5,6 +5,94 @@ */ #include "desktop_internal.hpp" +#include + +// ============================================================================ +// Shared Layout +// ============================================================================ +// +// All three power dialogs are the same shape: one centered question with a +// confirm/Cancel pair pinned to the bottom. Draw and hit-test both derive the +// button rects from these helpers, so neither has to cache a layout. + +static constexpr int POWER_DIALOG_W = 300; +static constexpr int POWER_DIALOG_H = 150; +static constexpr int POWER_BTN_W = 100; +static constexpr int POWER_BTN_GAP = 20; +static constexpr int POWER_BTN_MARGIN = 20; + +static Rect power_confirm_rect(int cw, int ch, const gui::mtk::Theme& theme) { + int total = POWER_BTN_W * 2 + POWER_BTN_GAP; + return {(cw - total) / 2, ch - theme.control_h - POWER_BTN_MARGIN, + POWER_BTN_W, theme.control_h}; +} + +static Rect power_cancel_rect(int cw, int ch, const gui::mtk::Theme& theme) { + Rect r = power_confirm_rect(cw, ch, theme); + r.x += POWER_BTN_W + POWER_BTN_GAP; + return r; +} + +static gui::mtk::Theme power_dialog_theme(const DesktopState* ds) { + return gui::mtk::make_theme(ds->settings.accent_color); +} + +static void power_dialog_draw(Window* win, + const gui::mtk::Theme& theme, + const char* message, + const char* confirm_label, + gui::mtk::ButtonVariant confirm_variant, + bool hover_confirm, + bool hover_cancel) { + Canvas c(win); + c.fill(theme.window_bg); + + int tw = text_width(message); + c.text((c.w - tw) / 2, 30, message, theme.text); + + gui::mtk::draw_modal_actions( + c, + power_confirm_rect(c.w, c.h, theme), confirm_label, confirm_variant, + gui::mtk::widget_state(false, hover_confirm), + power_cancel_rect(c.w, c.h, theme), "Cancel", + gui::mtk::widget_state(false, hover_cancel), + theme); +} + +// Updates the hover flags from a mouse event and reports where the pointer is. +static void power_dialog_hover(Window* win, + const MouseEvent& ev, + const gui::mtk::Theme& theme, + bool* hover_confirm, + bool* hover_cancel) { + gui::mtk::DesktopHost host(win); + int mx = 0; + int my = 0; + if (!host.map_mouse(ev, &mx, &my)) return; + + int cw = win->content_w; + int ch = win->content_h; + *hover_confirm = power_confirm_rect(cw, ch, theme).contains(mx, my); + *hover_cancel = power_cancel_rect(cw, ch, theme).contains(mx, my); +} + +// The dialogs own themselves through win->app_data, so closing means finding +// the window that carries this state. +static void power_dialog_close(DesktopState* ds, void* state) { + for (int i = 0; i < ds->window_count; i++) { + if (ds->windows[i].app_data == state) { + desktop_close_window(ds, i); + return; + } + } +} + +static void power_dialog_on_close(Window* win) { + if (win->app_data) { + montauk::mfree(win->app_data); + win->app_data = nullptr; + } +} // ============================================================================ // Reboot Dialog @@ -12,72 +100,29 @@ struct RebootDialogState { DesktopState* ds; - // Button layout (pixel-buffer-relative coordinates) - int btn_w, btn_h, btn_y, reboot_x, cancel_x; bool hover_reboot, hover_cancel; }; static void reboot_dialog_on_draw(Window* win, Framebuffer& fb) { + (void)fb; RebootDialogState* rs = (RebootDialogState*)win->app_data; if (!rs) return; - Canvas c(win); - c.fill(colors::WINDOW_BG); - - // "Reboot the system?" centered - const char* msg = "Reboot the system?"; - int tw = text_width(msg); - c.text((c.w - tw) / 2, 30, msg, colors::TEXT_COLOR); - - // Compute button layout - int btn_w = 100; - int btn_h = 32; - int btn_y = c.h - btn_h - 20; - int gap = 20; - int total_w = btn_w * 2 + gap; - int bx = (c.w - total_w) / 2; - rs->btn_w = btn_w; - rs->btn_h = btn_h; - rs->btn_y = btn_y; - rs->reboot_x = bx; - rs->cancel_x = bx + btn_w + gap; - - // Draw Reboot button - Color reboot_bg = rs->hover_reboot - ? Color::from_rgb(0xDD, 0x44, 0x44) - : Color::from_rgb(0xCC, 0x33, 0x33); - c.button(rs->reboot_x, btn_y, btn_w, btn_h, "Reboot", reboot_bg, colors::WHITE, 4); - - // Draw Cancel button - Color cancel_bg = rs->hover_cancel - ? Color::from_rgb(0x99, 0x99, 0x99) - : Color::from_rgb(0x88, 0x88, 0x88); - c.button(rs->cancel_x, btn_y, btn_w, btn_h, "Cancel", cancel_bg, colors::WHITE, 4); + power_dialog_draw(win, power_dialog_theme(rs->ds), "Reboot the system?", + "Reboot", gui::mtk::BUTTON_DANGER, + rs->hover_reboot, rs->hover_cancel); } static void reboot_dialog_on_mouse(Window* win, MouseEvent& ev) { RebootDialogState* rs = (RebootDialogState*)win->app_data; if (!rs) return; - Rect cr = win->content_rect(); - int lx = ev.x - cr.x; - int ly = ev.y - cr.y; - - Rect rb = {rs->reboot_x, rs->btn_y, rs->btn_w, rs->btn_h}; - Rect cb = {rs->cancel_x, rs->btn_y, rs->btn_w, rs->btn_h}; - rs->hover_reboot = rb.contains(lx, ly); - rs->hover_cancel = cb.contains(lx, ly); + power_dialog_hover(win, ev, power_dialog_theme(rs->ds), + &rs->hover_reboot, &rs->hover_cancel); if (ev.left_pressed()) { if (rs->hover_reboot) desktop_request_power(montauk::abi::POWER_REQ_REBOOT); - if (rs->hover_cancel) { - for (int i = 0; i < rs->ds->window_count; i++) { - if (rs->ds->windows[i].app_data == rs) { - desktop_close_window(rs->ds, i); - return; - } - } - } + if (rs->hover_cancel) power_dialog_close(rs->ds, rs); } } @@ -89,26 +134,14 @@ static void reboot_dialog_on_key(Window* win, const montauk::abi::KeyEvent& key) desktop_request_power(montauk::abi::POWER_REQ_REBOOT); } if (key.scancode == 0x01) { // Escape - for (int i = 0; i < rs->ds->window_count; i++) { - if (rs->ds->windows[i].app_data == rs) { - desktop_close_window(rs->ds, i); - return; - } - } - } -} - -static void reboot_dialog_on_close(Window* win) { - if (win->app_data) { - montauk::mfree(win->app_data); - win->app_data = nullptr; + power_dialog_close(rs->ds, rs); } } void open_reboot_dialog(DesktopState* ds) { - int wx = (ds->screen_w - 300) / 2; - int wy = (ds->screen_h - 150) / 2; - int idx = desktop_create_window(ds, "Reboot", wx, wy, 300, 150); + int wx = (ds->screen_w - POWER_DIALOG_W) / 2; + int wy = (ds->screen_h - POWER_DIALOG_H) / 2; + int idx = desktop_create_window(ds, "Reboot", wx, wy, POWER_DIALOG_W, POWER_DIALOG_H); if (idx < 0) return; Window* win = &ds->windows[idx]; @@ -120,7 +153,7 @@ void open_reboot_dialog(DesktopState* ds) { win->on_draw = reboot_dialog_on_draw; win->on_mouse = reboot_dialog_on_mouse; win->on_key = reboot_dialog_on_key; - win->on_close = reboot_dialog_on_close; + win->on_close = power_dialog_on_close; } // ============================================================================ @@ -129,67 +162,29 @@ void open_reboot_dialog(DesktopState* ds) { struct ShutdownDialogState { DesktopState* ds; - int btn_w, btn_h, btn_y, shutdown_x, cancel_x; bool hover_shutdown, hover_cancel; }; static void shutdown_dialog_on_draw(Window* win, Framebuffer& fb) { + (void)fb; ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data; if (!ss) return; - Canvas c(win); - c.fill(colors::WINDOW_BG); - - const char* msg = "Shut down the system?"; - int tw = text_width(msg); - c.text((c.w - tw) / 2, 30, msg, colors::TEXT_COLOR); - - int btn_w = 100; - int btn_h = 32; - int btn_y = c.h - btn_h - 20; - int gap = 20; - int total_w = btn_w * 2 + gap; - int bx = (c.w - total_w) / 2; - ss->btn_w = btn_w; - ss->btn_h = btn_h; - ss->btn_y = btn_y; - ss->shutdown_x = bx; - ss->cancel_x = bx + btn_w + gap; - - Color shutdown_bg = ss->hover_shutdown - ? Color::from_rgb(0xDD, 0x44, 0x44) - : Color::from_rgb(0xCC, 0x33, 0x33); - c.button(ss->shutdown_x, btn_y, btn_w, btn_h, "Shut Down", shutdown_bg, colors::WHITE, 4); - - Color cancel_bg = ss->hover_cancel - ? Color::from_rgb(0x99, 0x99, 0x99) - : Color::from_rgb(0x88, 0x88, 0x88); - c.button(ss->cancel_x, btn_y, btn_w, btn_h, "Cancel", cancel_bg, colors::WHITE, 4); + power_dialog_draw(win, power_dialog_theme(ss->ds), "Shut down the system?", + "Shut Down", gui::mtk::BUTTON_DANGER, + ss->hover_shutdown, ss->hover_cancel); } static void shutdown_dialog_on_mouse(Window* win, MouseEvent& ev) { ShutdownDialogState* ss = (ShutdownDialogState*)win->app_data; if (!ss) return; - Rect cr = win->content_rect(); - int lx = ev.x - cr.x; - int ly = ev.y - cr.y; - - Rect sb = {ss->shutdown_x, ss->btn_y, ss->btn_w, ss->btn_h}; - Rect cb = {ss->cancel_x, ss->btn_y, ss->btn_w, ss->btn_h}; - ss->hover_shutdown = sb.contains(lx, ly); - ss->hover_cancel = cb.contains(lx, ly); + power_dialog_hover(win, ev, power_dialog_theme(ss->ds), + &ss->hover_shutdown, &ss->hover_cancel); if (ev.left_pressed()) { if (ss->hover_shutdown) desktop_request_power(montauk::abi::POWER_REQ_SHUTDOWN); - if (ss->hover_cancel) { - for (int i = 0; i < ss->ds->window_count; i++) { - if (ss->ds->windows[i].app_data == ss) { - desktop_close_window(ss->ds, i); - return; - } - } - } + if (ss->hover_cancel) power_dialog_close(ss->ds, ss); } } @@ -201,26 +196,14 @@ static void shutdown_dialog_on_key(Window* win, const montauk::abi::KeyEvent& ke desktop_request_power(montauk::abi::POWER_REQ_SHUTDOWN); } if (key.scancode == 0x01) { // Escape - for (int i = 0; i < ss->ds->window_count; i++) { - if (ss->ds->windows[i].app_data == ss) { - desktop_close_window(ss->ds, i); - return; - } - } - } -} - -static void shutdown_dialog_on_close(Window* win) { - if (win->app_data) { - montauk::mfree(win->app_data); - win->app_data = nullptr; + power_dialog_close(ss->ds, ss); } } void open_shutdown_dialog(DesktopState* ds) { - int wx = (ds->screen_w - 300) / 2; - int wy = (ds->screen_h - 150) / 2; - int idx = desktop_create_window(ds, "Shut Down", wx, wy, 300, 150); + int wx = (ds->screen_w - POWER_DIALOG_W) / 2; + int wy = (ds->screen_h - POWER_DIALOG_H) / 2; + int idx = desktop_create_window(ds, "Shut Down", wx, wy, POWER_DIALOG_W, POWER_DIALOG_H); if (idx < 0) return; Window* win = &ds->windows[idx]; @@ -232,7 +215,7 @@ void open_shutdown_dialog(DesktopState* ds) { win->on_draw = shutdown_dialog_on_draw; win->on_mouse = shutdown_dialog_on_mouse; win->on_key = shutdown_dialog_on_key; - win->on_close = shutdown_dialog_on_close; + win->on_close = power_dialog_on_close; } // ============================================================================ @@ -241,76 +224,34 @@ void open_shutdown_dialog(DesktopState* ds) { struct SleepDialogState { DesktopState* ds; - int btn_w, btn_h, btn_y, sleep_x, cancel_x; bool hover_sleep, hover_cancel; }; static void sleep_dialog_on_draw(Window* win, Framebuffer& fb) { + (void)fb; SleepDialogState* ss = (SleepDialogState*)win->app_data; if (!ss) return; - Canvas c(win); - c.fill(colors::WINDOW_BG); - - const char* msg = "Suspend the system?"; - int tw = text_width(msg); - c.text((c.w - tw) / 2, 30, msg, colors::TEXT_COLOR); - - int btn_w = 100; - int btn_h = 32; - int btn_y = c.h - btn_h - 20; - int gap = 20; - int total_w = btn_w * 2 + gap; - int bx = (c.w - total_w) / 2; - ss->btn_w = btn_w; - ss->btn_h = btn_h; - ss->btn_y = btn_y; - ss->sleep_x = bx; - ss->cancel_x = bx + btn_w + gap; - - Color sleep_bg = ss->hover_sleep - ? Color::from_rgb(0x44, 0x77, 0xDD) - : Color::from_rgb(0x33, 0x66, 0xCC); - c.button(ss->sleep_x, btn_y, btn_w, btn_h, "Sleep", sleep_bg, colors::WHITE, 4); - - Color cancel_bg = ss->hover_cancel - ? Color::from_rgb(0x99, 0x99, 0x99) - : Color::from_rgb(0x88, 0x88, 0x88); - c.button(ss->cancel_x, btn_y, btn_w, btn_h, "Cancel", cancel_bg, colors::WHITE, 4); + power_dialog_draw(win, power_dialog_theme(ss->ds), "Suspend the system?", + "Sleep", gui::mtk::BUTTON_PRIMARY, + ss->hover_sleep, ss->hover_cancel); } static void sleep_dialog_on_mouse(Window* win, MouseEvent& ev) { SleepDialogState* ss = (SleepDialogState*)win->app_data; if (!ss) return; - Rect cr = win->content_rect(); - int lx = ev.x - cr.x; - int ly = ev.y - cr.y; - - Rect sb = {ss->sleep_x, ss->btn_y, ss->btn_w, ss->btn_h}; - Rect cb = {ss->cancel_x, ss->btn_y, ss->btn_w, ss->btn_h}; - ss->hover_sleep = sb.contains(lx, ly); - ss->hover_cancel = cb.contains(lx, ly); + power_dialog_hover(win, ev, power_dialog_theme(ss->ds), + &ss->hover_sleep, &ss->hover_cancel); if (ev.left_pressed()) { if (ss->hover_sleep) { montauk::suspend(); // If suspend returns (resume from S3), close the dialog - for (int i = 0; i < ss->ds->window_count; i++) { - if (ss->ds->windows[i].app_data == ss) { - desktop_close_window(ss->ds, i); - return; - } - } - } - if (ss->hover_cancel) { - for (int i = 0; i < ss->ds->window_count; i++) { - if (ss->ds->windows[i].app_data == ss) { - desktop_close_window(ss->ds, i); - return; - } - } + power_dialog_close(ss->ds, ss); + return; } + if (ss->hover_cancel) power_dialog_close(ss->ds, ss); } } @@ -321,34 +262,18 @@ static void sleep_dialog_on_key(Window* win, const montauk::abi::KeyEvent& key) if (key.ascii == '\n' || key.ascii == '\r') { montauk::suspend(); // If suspend returns, close the dialog - for (int i = 0; i < ss->ds->window_count; i++) { - if (ss->ds->windows[i].app_data == ss) { - desktop_close_window(ss->ds, i); - return; - } - } + power_dialog_close(ss->ds, ss); + return; } if (key.scancode == 0x01) { // Escape - for (int i = 0; i < ss->ds->window_count; i++) { - if (ss->ds->windows[i].app_data == ss) { - desktop_close_window(ss->ds, i); - return; - } - } - } -} - -static void sleep_dialog_on_close(Window* win) { - if (win->app_data) { - montauk::mfree(win->app_data); - win->app_data = nullptr; + power_dialog_close(ss->ds, ss); } } void open_sleep_dialog(DesktopState* ds) { - int wx = (ds->screen_w - 300) / 2; - int wy = (ds->screen_h - 150) / 2; - int idx = desktop_create_window(ds, "Sleep", wx, wy, 300, 150); + int wx = (ds->screen_w - POWER_DIALOG_W) / 2; + int wy = (ds->screen_h - POWER_DIALOG_H) / 2; + int idx = desktop_create_window(ds, "Sleep", wx, wy, POWER_DIALOG_W, POWER_DIALOG_H); if (idx < 0) return; Window* win = &ds->windows[idx]; @@ -360,5 +285,5 @@ void open_sleep_dialog(DesktopState* ds) { win->on_draw = sleep_dialog_on_draw; win->on_mouse = sleep_dialog_on_mouse; win->on_key = sleep_dialog_on_key; - win->on_close = sleep_dialog_on_close; + win->on_close = power_dialog_on_close; }