From 867d56b34268ad43000557e41a3a2ac44ba6d3da Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Wed, 22 Apr 2026 20:27:55 +0200 Subject: [PATCH] feat: message box dynamic sizing, documentation page updates --- montaukos.org/docs/index.html | 13 ++--- montaukos.org/docs/introduction.html | 5 -- programs/src/dialogs/main.cpp | 6 ++- programs/src/dialogs/messagebox.cpp | 80 ++++++++++++++++++++++++++-- programs/src/dialogs/messagebox.hpp | 11 +++- programs/src/test_dialogs/main.cpp | 11 ++-- 6 files changed, 101 insertions(+), 25 deletions(-) diff --git a/montaukos.org/docs/index.html b/montaukos.org/docs/index.html index 0a5551c..cdedf5b 100644 --- a/montaukos.org/docs/index.html +++ b/montaukos.org/docs/index.html @@ -87,11 +87,6 @@
@@ -114,6 +109,10 @@ operating system, its components, and how to use and develop for it. Introduction

Installation, running on emulator, and your first steps with MontaukOS.

+
  • + Dialogs Library +

    Desktop internals: file, print, and message box dialogs exposed through the shared dialogs library.

    +
  • Kernel Architecture

    Overview of the preemptive multitasking kernel and its subsystems.

    @@ -130,10 +129,6 @@ operating system, its components, and how to use and develop for it. Building from Source

    How to compile and build MontaukOS yourself.

  • -
  • - Contributing -

    Guidelines for contributing to the project.

    -

  • diff --git a/montaukos.org/docs/introduction.html b/montaukos.org/docs/introduction.html index a12c3eb..ac95bd1 100644 --- a/montaukos.org/docs/introduction.html +++ b/montaukos.org/docs/introduction.html @@ -73,11 +73,6 @@
    diff --git a/programs/src/dialogs/main.cpp b/programs/src/dialogs/main.cpp index 5ceb7f5..b322621 100644 --- a/programs/src/dialogs/main.cpp +++ b/programs/src/dialogs/main.cpp @@ -166,7 +166,11 @@ extern "C" uint8_t dialogs_message_box(const char* title, const char* window_title = title && title[0] ? title : "Message"; messagebox::init(message ? message : "", buttons); - if (!g_app.win.create(window_title, messagebox::MESSAGE_INIT_W, messagebox::MESSAGE_INIT_H)) { + int init_w = 0; + int init_h = 0; + messagebox::preferred_size(window_title, &init_w, &init_h); + + if (!g_app.win.create(window_title, init_w, init_h)) { cleanup(); return dlg::MESSAGE_BOX_RESULT_NONE; } diff --git a/programs/src/dialogs/messagebox.cpp b/programs/src/dialogs/messagebox.cpp index 107345a..9b878d1 100644 --- a/programs/src/dialogs/messagebox.cpp +++ b/programs/src/dialogs/messagebox.cpp @@ -210,6 +210,48 @@ bool is_space(char ch) { return ch == ' ' || ch == '\t' || ch == '\r'; } +int clamp_int(int value, int min_value, int max_value) { + if (value < min_value) return min_value; + if (value > max_value) return max_value; + return value; +} + +int button_row_width(const MessageBoxState* st) { + if (!st || st->button_count <= 0) return 0; + int total_w = st->button_count * messagebox::BUTTON_W; + if (st->button_count > 1) + total_w += (st->button_count - 1) * messagebox::BUTTON_GAP; + return total_w; +} + +int widest_explicit_message_line(const MessageBoxState* st, int max_px) { + if (!st || !st->text[0]) return 0; + + char line[messagebox::MAX_LINE_LEN] = {}; + int line_len = 0; + int widest = 0; + + for (const char* p = st->text; ; p++) { + char ch = *p; + bool at_end = ch == '\0'; + if (at_end || ch == '\n') { + line[line_len] = '\0'; + int width = text_width(line); + if (width > widest) widest = width; + line_len = 0; + line[0] = '\0'; + if (at_end) break; + continue; + } + + if (line_len >= messagebox::MAX_LINE_LEN - 1) + return max_px; + line[line_len++] = ch; + } + + return widest > max_px ? max_px : widest; +} + void wrap_message(MessageBoxState* st, int max_px) { if (!st) return; if (max_px < 40) max_px = 40; @@ -267,11 +309,12 @@ MessageBoxLayout message_layout(const MessageBoxState* st) { lo.button_count = st ? st->button_count : 0; lo.footer_rect = {0, g_app.win.height - messagebox::FOOTER_H, g_app.win.width, messagebox::FOOTER_H}; - lo.text_rect = {20, 24, g_app.win.width - 40, lo.footer_rect.y - 32}; + lo.text_rect = {messagebox::TEXT_X, messagebox::TEXT_Y, + g_app.win.width - messagebox::TEXT_X * 2, + lo.footer_rect.y - messagebox::TEXT_Y - messagebox::TEXT_BOTTOM_GAP}; - int total_w = lo.button_count * messagebox::BUTTON_W; - if (lo.button_count > 1) total_w += (lo.button_count - 1) * messagebox::BUTTON_GAP; - int x = g_app.win.width - 16 - total_w; + int total_w = button_row_width(st); + int x = g_app.win.width - messagebox::BUTTON_RIGHT_PAD - total_w; int y = lo.footer_rect.y + (messagebox::FOOTER_H - messagebox::BUTTON_H) / 2; for (int i = 0; i < lo.button_count && i < messagebox::MAX_BUTTONS; i++) { lo.buttons[i] = {x, y, messagebox::BUTTON_W, messagebox::BUTTON_H}; @@ -387,7 +430,34 @@ void init(const char* message, uint8_t buttons, uint8_t default_choice) { configure_buttons(st); if (!has_choice(st, st->default_choice)) st->default_choice = default_choice_for(st->buttons); - wrap_message(st, MESSAGE_INIT_W - 40); + wrap_message(st, messagebox::MESSAGE_MIN_W - messagebox::TEXT_X * 2); +} + +void preferred_size(const char* title, int* out_w, int* out_h) { + MessageBoxState* st = &g_message; + + int max_text_w = messagebox::MESSAGE_MAX_W - messagebox::TEXT_X * 2; + int desired_text_w = widest_explicit_message_line(st, max_text_w); + if (title && title[0]) { + int title_w = text_width(title) + 120; + if (title_w > desired_text_w) desired_text_w = title_w; + } + + int desired_w = desired_text_w + messagebox::TEXT_X * 2; + int buttons_w = button_row_width(st) + messagebox::BUTTON_RIGHT_PAD * 2; + if (buttons_w > desired_w) desired_w = buttons_w; + desired_w = clamp_int(desired_w, messagebox::MESSAGE_MIN_W, messagebox::MESSAGE_MAX_W); + + wrap_message(st, desired_w - messagebox::TEXT_X * 2); + + int line_h = system_font_height() + 5; + int text_h = st->line_count > 0 ? st->line_count * line_h : line_h; + int desired_h = messagebox::TEXT_Y + text_h + messagebox::TEXT_BOTTOM_GAP + + messagebox::FOOTER_H; + desired_h = clamp_int(desired_h, messagebox::MESSAGE_MIN_H, messagebox::MESSAGE_MAX_H); + + if (out_w) *out_w = desired_w; + if (out_h) *out_h = desired_h; } void draw() { diff --git a/programs/src/dialogs/messagebox.hpp b/programs/src/dialogs/messagebox.hpp index a5293d4..ad3e368 100644 --- a/programs/src/dialogs/messagebox.hpp +++ b/programs/src/dialogs/messagebox.hpp @@ -19,9 +19,15 @@ struct MessageBoxLayout { namespace messagebox { -constexpr int MESSAGE_INIT_W = 460; -constexpr int MESSAGE_INIT_H = 220; +constexpr int MESSAGE_MIN_W = 300; +constexpr int MESSAGE_MAX_W = 560; +constexpr int MESSAGE_MIN_H = 124; +constexpr int MESSAGE_MAX_H = 280; constexpr int FOOTER_H = 56; +constexpr int TEXT_X = 20; +constexpr int TEXT_Y = 24; +constexpr int TEXT_BOTTOM_GAP = 24; +constexpr int BUTTON_RIGHT_PAD = 16; constexpr int BUTTON_W = 88; constexpr int BUTTON_H = 30; constexpr int BUTTON_GAP = 8; @@ -30,6 +36,7 @@ constexpr int MAX_LINES = 7; constexpr int MAX_LINE_LEN = 128; void init(const char* message, uint8_t buttons, uint8_t default_choice = 0); +void preferred_size(const char* title, int* out_w, int* out_h); void draw(); void handle_mouse(const Montauk::WinEvent& ev); void handle_key(const Montauk::KeyEvent& key); diff --git a/programs/src/test_dialogs/main.cpp b/programs/src/test_dialogs/main.cpp index 498f8dc..385b3df 100644 --- a/programs/src/test_dialogs/main.cpp +++ b/programs/src/test_dialogs/main.cpp @@ -8,8 +8,13 @@ #include extern "C" void _start() { - gui::dialogs::message_box("Test Dialogs", - "Hello from libdialogs::message_box.", - gui::dialogs::MESSAGE_BOX_OK); + auto result = gui::dialogs::message_box( + "Close Document", + "Discard unsaved changes?", + gui::dialogs::MESSAGE_BOX_YES_NO_CANCEL); + + if (result == gui::dialogs::MESSAGE_BOX_RESULT_YES) { + /* discard and close */ + } montauk::exit(0); }