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);
}