feat: improve MTK input boxes, right-click context menus

This commit is contained in:
2026-05-06 08:03:14 +02:00
parent ff264aa0f5
commit 77408ff432
15 changed files with 1337 additions and 256 deletions
+189 -49
View File
@@ -15,21 +15,6 @@ static mtk::WidgetState dialog_button_state(int mouse_x, int mouse_y,
return mtk::widget_state(active, rect.contains(mouse_x, mouse_y), enabled);
}
static void dialog_append(char* buf, int* len, int max, char ch) {
if (*len < max - 1) {
buf[*len] = ch;
(*len)++;
buf[*len] = '\0';
}
}
static void dialog_backspace(char* buf, int* len) {
if (*len > 0) {
(*len)--;
buf[*len] = '\0';
}
}
static void dialog_close_self(DesktopState* ds, void* app_data) {
for (int i = 0; i < ds->window_count; i++) {
if (ds->windows[i].app_data == app_data) {
@@ -55,8 +40,11 @@ struct AddUserDialogState {
int mouse_y;
int field;
char username[32]; int username_len;
mtk::TextInputState username_input;
char display[64]; int display_len;
mtk::TextInputState display_input;
char password[64]; int password_len;
mtk::TextInputState password_input;
bool role_admin;
char error[64];
};
@@ -76,15 +64,18 @@ static void adduser_on_draw(Window* win, Framebuffer& fb) {
int sfh = system_font_height();
mtk::draw_labeled_text_field(c, pad, y, fw, "Username", st->username,
st->username_len, st->field == 0, false, theme, USER_FIELD_H);
st->username_input.cursor, st->field == 0, false,
theme, USER_FIELD_H, st->username_input.selection_anchor);
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 10;
mtk::draw_labeled_text_field(c, pad, y, fw, "Display Name", st->display,
st->display_len, st->field == 1, false, theme, USER_FIELD_H);
st->display_input.cursor, st->field == 1, false,
theme, USER_FIELD_H, st->display_input.selection_anchor);
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 10;
mtk::draw_labeled_text_field(c, pad, y, fw, "Password", st->password,
st->password_len, st->field == 2, true, theme, USER_FIELD_H);
st->password_input.cursor, st->field == 2, true,
theme, USER_FIELD_H, st->password_input.selection_anchor);
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 12;
c.text(pad, y + 4, "Role:", theme.text_muted);
@@ -112,6 +103,20 @@ static void adduser_on_draw(Window* win, Framebuffer& fb) {
cancel_btn, "Cancel",
dialog_button_state(st->mouse_x, st->mouse_y, cancel_btn),
theme);
mtk::draw_text_input_context_menu(
c, st->username_input, theme,
mtk::text_input_has_selection(st->username_input,
st->username, (int)sizeof(st->username)));
mtk::draw_text_input_context_menu(
c, st->display_input, theme,
mtk::text_input_has_selection(st->display_input,
st->display, (int)sizeof(st->display)));
mtk::draw_text_input_context_menu(
c, st->password_input, theme,
mtk::text_input_has_selection(st->password_input,
st->password, (int)sizeof(st->password)),
true);
}
static void adduser_submit(AddUserDialogState* st) {
@@ -145,25 +150,90 @@ static void adduser_on_mouse(Window* win, MouseEvent& ev) {
if (!host.map_mouse(ev, &mx, &my)) return;
st->mouse_x = mx;
st->mouse_y = my;
if (!ev.left_pressed()) return;
mtk::Theme theme = mtk::make_theme(st->accent);
int pad = 16;
int fw = win->content_w - 2 * pad;
int y = pad;
bool right_pressed = ev.right_pressed();
Rect username_rect = mtk::labeled_text_input_rect(pad, y, fw, theme, USER_FIELD_H);
if (username_rect.contains(mx, my)) { st->field = 0; return; }
if (st->username_input.context.open || st->username_input.dragging) {
int result = mtk::text_input_handle_mouse(
st->username_input, username_rect, st->username, (int)sizeof(st->username),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
st->field == 0);
if (result & mtk::TEXT_INPUT_CHANGED)
st->username_len = mtk::text_input_len(st->username, (int)sizeof(st->username));
return;
}
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 10;
Rect display_rect = mtk::labeled_text_input_rect(pad, y, fw, theme, USER_FIELD_H);
if (display_rect.contains(mx, my)) { st->field = 1; return; }
if (st->display_input.context.open || st->display_input.dragging) {
int result = mtk::text_input_handle_mouse(
st->display_input, display_rect, st->display, (int)sizeof(st->display),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
st->field == 1);
if (result & mtk::TEXT_INPUT_CHANGED)
st->display_len = mtk::text_input_len(st->display, (int)sizeof(st->display));
return;
}
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 10;
Rect password_rect = mtk::labeled_text_input_rect(pad, y, fw, theme, USER_FIELD_H);
if (password_rect.contains(mx, my)) { st->field = 2; return; }
if (st->password_input.context.open || st->password_input.dragging) {
int result = mtk::text_input_handle_mouse(
st->password_input, password_rect, st->password, (int)sizeof(st->password),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
st->field == 2, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->password_len = mtk::text_input_len(st->password, (int)sizeof(st->password));
return;
}
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 12;
if (!ev.left_pressed() && !right_pressed) return;
if (username_rect.contains(mx, my)) {
st->field = 0;
mtk::context_menu_close(st->display_input.context);
mtk::context_menu_close(st->password_input.context);
int result = mtk::text_input_handle_mouse(
st->username_input, username_rect, st->username, (int)sizeof(st->username),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->username_len = mtk::text_input_len(st->username, (int)sizeof(st->username));
return;
}
if (display_rect.contains(mx, my)) {
st->field = 1;
mtk::context_menu_close(st->username_input.context);
mtk::context_menu_close(st->password_input.context);
int result = mtk::text_input_handle_mouse(
st->display_input, display_rect, st->display, (int)sizeof(st->display),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->display_len = mtk::text_input_len(st->display, (int)sizeof(st->display));
return;
}
if (password_rect.contains(mx, my)) {
st->field = 2;
mtk::context_menu_close(st->username_input.context);
mtk::context_menu_close(st->display_input.context);
int result = mtk::text_input_handle_mouse(
st->password_input, password_rect, st->password, (int)sizeof(st->password),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
true, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->password_len = mtk::text_input_len(st->password, (int)sizeof(st->password));
return;
}
if (!ev.left_pressed()) return;
Rect user_btn = {pad + 60, y, 60, USER_BTN_H};
Rect admin_btn = {pad + 128, y, 60, USER_BTN_H};
if (user_btn.contains(mx, my)) {
@@ -203,20 +273,28 @@ static void adduser_on_key(Window* win, const Montauk::KeyEvent& key) {
st->field = (st->field + 1) % 3;
return;
}
if (key.ascii == '\b' || key.scancode == 0x0E) {
switch (st->field) {
case 0: dialog_backspace(st->username, &st->username_len); break;
case 1: dialog_backspace(st->display, &st->display_len); break;
case 2: dialog_backspace(st->password, &st->password_len); break;
}
return;
}
if (key.ascii >= 0x20 && key.ascii < 0x7F) {
switch (st->field) {
case 0: dialog_append(st->username, &st->username_len, 31, key.ascii); break;
case 1: dialog_append(st->display, &st->display_len, 63, key.ascii); break;
case 2: dialog_append(st->password, &st->password_len, 63, key.ascii); break;
}
int result = mtk::TEXT_INPUT_NONE;
switch (st->field) {
case 0:
result = mtk::text_input_key(st->username_input, st->username,
(int)sizeof(st->username), key);
if (result & mtk::TEXT_INPUT_CHANGED)
st->username_len = mtk::text_input_len(st->username, (int)sizeof(st->username));
break;
case 1:
result = mtk::text_input_key(st->display_input, st->display,
(int)sizeof(st->display), key);
if (result & mtk::TEXT_INPUT_CHANGED)
st->display_len = mtk::text_input_len(st->display, (int)sizeof(st->display));
break;
case 2:
result = mtk::text_input_key(st->password_input, st->password,
(int)sizeof(st->password), key,
nullptr, nullptr, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->password_len = mtk::text_input_len(st->password, (int)sizeof(st->password));
break;
}
}
@@ -262,7 +340,9 @@ struct ChPwdDialogState {
char display_name[64];
int field;
char new_pwd[64]; int new_len;
mtk::TextInputState new_input;
char confirm[64]; int confirm_len;
mtk::TextInputState confirm_input;
char error[64];
};
@@ -286,11 +366,13 @@ static void chpwd_on_draw(Window* win, Framebuffer& fb) {
y += sfh + 12;
mtk::draw_labeled_text_field(c, pad, y, fw, "New Password", st->new_pwd,
st->new_len, st->field == 0, true, theme, USER_FIELD_H);
st->new_input.cursor, st->field == 0, true,
theme, USER_FIELD_H, st->new_input.selection_anchor);
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 10;
mtk::draw_labeled_text_field(c, pad, y, fw, "Confirm Password", st->confirm,
st->confirm_len, st->field == 1, true, theme, USER_FIELD_H);
st->confirm_input.cursor, st->field == 1, true,
theme, USER_FIELD_H, st->confirm_input.selection_anchor);
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 12;
if (st->error[0]) {
@@ -306,6 +388,17 @@ static void chpwd_on_draw(Window* win, Framebuffer& fb) {
cancel_btn, "Cancel",
dialog_button_state(st->mouse_x, st->mouse_y, cancel_btn),
theme);
mtk::draw_text_input_context_menu(
c, st->new_input, theme,
mtk::text_input_has_selection(st->new_input,
st->new_pwd, (int)sizeof(st->new_pwd)),
true);
mtk::draw_text_input_context_menu(
c, st->confirm_input, theme,
mtk::text_input_has_selection(st->confirm_input,
st->confirm, (int)sizeof(st->confirm)),
true);
}
static void chpwd_submit(ChPwdDialogState* st) {
@@ -333,19 +426,62 @@ static void chpwd_on_mouse(Window* win, MouseEvent& ev) {
if (!host.map_mouse(ev, &mx, &my)) return;
st->mouse_x = mx;
st->mouse_y = my;
if (!ev.left_pressed()) return;
mtk::Theme theme = mtk::make_theme(st->accent);
int pad = 16;
int fw = win->content_w - 2 * pad;
int y = pad + system_font_height() + 12;
bool right_pressed = ev.right_pressed();
Rect new_rect = mtk::labeled_text_input_rect(pad, y, fw, theme, USER_FIELD_H);
if (new_rect.contains(mx, my)) { st->field = 0; return; }
if (st->new_input.context.open || st->new_input.dragging) {
int result = mtk::text_input_handle_mouse(
st->new_input, new_rect, st->new_pwd, (int)sizeof(st->new_pwd),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
st->field == 0, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->new_len = mtk::text_input_len(st->new_pwd, (int)sizeof(st->new_pwd));
return;
}
y += mtk::labeled_text_height(theme, USER_FIELD_H) + 10;
Rect confirm_rect = mtk::labeled_text_input_rect(pad, y, fw, theme, USER_FIELD_H);
if (confirm_rect.contains(mx, my)) { st->field = 1; return; }
if (st->confirm_input.context.open || st->confirm_input.dragging) {
int result = mtk::text_input_handle_mouse(
st->confirm_input, confirm_rect, st->confirm, (int)sizeof(st->confirm),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
st->field == 1, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->confirm_len = mtk::text_input_len(st->confirm, (int)sizeof(st->confirm));
return;
}
if (!ev.left_pressed() && !right_pressed) return;
if (new_rect.contains(mx, my)) {
st->field = 0;
mtk::context_menu_close(st->confirm_input.context);
int result = mtk::text_input_handle_mouse(
st->new_input, new_rect, st->new_pwd, (int)sizeof(st->new_pwd),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
true, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->new_len = mtk::text_input_len(st->new_pwd, (int)sizeof(st->new_pwd));
return;
}
if (confirm_rect.contains(mx, my)) {
st->field = 1;
mtk::context_menu_close(st->new_input.context);
int result = mtk::text_input_handle_mouse(
st->confirm_input, confirm_rect, st->confirm, (int)sizeof(st->confirm),
mx, my, ev.buttons, ev.prev_buttons, win->content_w, win->content_h,
true, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->confirm_len = mtk::text_input_len(st->confirm, (int)sizeof(st->confirm));
return;
}
if (!ev.left_pressed()) return;
Rect save_btn = dialog_primary_button_rect(pad, win->content_h);
Rect cancel_btn = dialog_secondary_button_rect(pad, win->content_h);
@@ -375,14 +511,18 @@ static void chpwd_on_key(Window* win, const Montauk::KeyEvent& key) {
st->field = (st->field + 1) % 2;
return;
}
if (key.ascii == '\b' || key.scancode == 0x0E) {
if (st->field == 0) dialog_backspace(st->new_pwd, &st->new_len);
else dialog_backspace(st->confirm, &st->confirm_len);
return;
}
if (key.ascii >= 0x20 && key.ascii < 0x7F) {
if (st->field == 0) dialog_append(st->new_pwd, &st->new_len, 63, key.ascii);
else dialog_append(st->confirm, &st->confirm_len, 63, key.ascii);
if (st->field == 0) {
int result = mtk::text_input_key(st->new_input, st->new_pwd,
(int)sizeof(st->new_pwd), key,
nullptr, nullptr, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->new_len = mtk::text_input_len(st->new_pwd, (int)sizeof(st->new_pwd));
} else {
int result = mtk::text_input_key(st->confirm_input, st->confirm,
(int)sizeof(st->confirm), key,
nullptr, nullptr, true);
if (result & mtk::TEXT_INPUT_CHANGED)
st->confirm_len = mtk::text_input_len(st->confirm, (int)sizeof(st->confirm));
}
}