feat: screenshot app improvement, WIndow Server supports full-screen overlay, Disks app UI improvements

This commit is contained in:
2026-05-10 10:45:05 +02:00
parent 69fc184a4e
commit 72e4fa080a
29 changed files with 1483 additions and 395 deletions
+15 -27
View File
@@ -25,29 +25,17 @@ using namespace gui;
static constexpr int INIT_W = 640;
static constexpr int INIT_H = 460;
static constexpr int TOOLBAR_H = 40;
static constexpr int HEADER_H = 26;
static constexpr int TOOLBAR_H = 44;
static constexpr int HEADER_H = 28;
static constexpr int ITEM_H = 32;
static constexpr int MAP_H = 48;
static constexpr int MAP_PAD = 16;
static constexpr int MAX_PARTS = 32;
static constexpr int MAX_DISKS = 8;
static constexpr int STATUS_H = 26;
static constexpr int STATUS_H = 44;
static constexpr int TB_BTN_Y = 7;
static constexpr int TB_BTN_H = 26;
static constexpr int TB_BTN_RAD = 6;
static constexpr Color BG_COLOR = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr Color TOOLBAR_BG = Color::from_rgb(0xF5, 0xF5, 0xF5);
static constexpr Color HEADER_BG = Color::from_rgb(0xF0, 0xF0, 0xF0);
static constexpr Color BORDER_COLOR = Color::from_rgb(0xCC, 0xCC, 0xCC);
static constexpr Color TEXT_COLOR = Color::from_rgb(0x22, 0x22, 0x22);
static constexpr Color DIM_TEXT = Color::from_rgb(0x66, 0x66, 0x66);
static constexpr Color FAINT_TEXT = Color::from_rgb(0x88, 0x88, 0x88);
static constexpr Color HOVER_BG = Color::from_rgb(0xE8, 0xF0, 0xF8);
static constexpr Color STATUS_BG_COL = Color::from_rgb(0xF0, 0xF0, 0xF0);
static constexpr Color WHITE = Color::from_rgb(0xFF, 0xFF, 0xFF);
static constexpr int TB_BTN_H = 30;
static constexpr int NUM_PART_COLORS = 8;
@@ -119,6 +107,7 @@ struct DiskToolState {
extern int g_win_w, g_win_h;
extern DiskToolState g_state;
extern Color g_accent;
// ============================================================================
// Partition colors
@@ -133,33 +122,34 @@ extern const Color part_colors[NUM_PART_COLORS];
inline int disk_button_width(int idx) {
char label[8];
snprintf(label, sizeof(label), "Disk %d", idx);
return text_width(label) + 16;
return text_width(label) + 20;
}
inline Rect toolbar_disk_button_rect(int idx) {
int bx = 8;
for (int i = 0; i < idx; i++)
bx += disk_button_width(i) + 6;
bx += disk_button_width(i) + 8;
return {bx, TB_BTN_Y, disk_button_width(idx), TB_BTN_H};
}
inline Rect toolbar_refresh_button_rect() {
return {g_win_w - 8 - 64, TB_BTN_Y, 64, TB_BTN_H};
int y = g_win_h - STATUS_H + (STATUS_H - TB_BTN_H) / 2;
return {g_win_w - 12 - 84, y, 84, TB_BTN_H};
}
inline Rect toolbar_mount_button_rect() {
Rect refresh = toolbar_refresh_button_rect();
return {refresh.x - 6 - 60, TB_BTN_Y, 60, TB_BTN_H};
return {refresh.x - 8 - 72, refresh.y, 72, TB_BTN_H};
}
inline Rect toolbar_format_button_rect() {
Rect mount = toolbar_mount_button_rect();
return {mount.x - 6 - 64, TB_BTN_Y, 64, TB_BTN_H};
return {mount.x - 8 - 72, mount.y, 72, TB_BTN_H};
}
inline Rect toolbar_newpart_button_rect() {
Rect format = toolbar_format_button_rect();
return {format.x - 6 - 74, TB_BTN_Y, 74, TB_BTN_H};
return {format.x - 8 - 96, format.y, 96, TB_BTN_H};
}
inline int content_title_y() {
@@ -188,15 +178,13 @@ inline Rect format_option_rect(int dialog_w, int index) {
inline Rect dialog_primary_button_rect(int dialog_w, int dialog_h) {
int btn_w = 90;
int btn_h = 30;
int gap = 16;
int total_w = btn_w * 2 + gap;
int bx = (dialog_w - total_w) / 2;
return {bx, dialog_h - btn_h - 16, btn_w, btn_h};
int bx = dialog_w - btn_w - 16;
return {bx, dialog_h - btn_h - 12, btn_w, btn_h};
}
inline Rect dialog_secondary_button_rect(int dialog_w, int dialog_h) {
Rect primary = dialog_primary_button_rect(dialog_w, dialog_h);
return {primary.x + primary.w + 16, primary.y, primary.w, primary.h};
return {primary.x - 8 - primary.w, primary.y, primary.w, primary.h};
}
// ============================================================================
+17 -3
View File
@@ -5,6 +5,7 @@
*/
#include "disks.h"
#include <gui/mtk/settings.hpp>
#include <gui/standalone.hpp>
// ============================================================================
@@ -15,6 +16,7 @@ int g_win_w = INIT_W;
int g_win_h = INIT_H;
DiskToolState g_state;
Color g_accent = colors::ACCENT;
const Color part_colors[NUM_PART_COLORS] = {
Color::from_rgb(0x42, 0x7A, 0xB5),
@@ -73,7 +75,6 @@ static bool handle_toolbar_click(int mx, int my) {
auto& dt = g_state;
if (dt.disk_count == 0) return false;
bool has_sel = dt.selected_part >= 0;
// Disk selector buttons
for (int i = 0; i < dt.disk_count; i++) {
@@ -85,7 +86,16 @@ static bool handle_toolbar_click(int mx, int my) {
}
}
// Right-side buttons (must match render layout)
return false;
}
static bool handle_action_click(int mx, int my) {
if (my < g_win_h - STATUS_H) return false;
auto& dt = g_state;
if (dt.disk_count == 0) return false;
bool has_sel = dt.selected_part >= 0;
if (toolbar_refresh_button_rect().contains(mx, my)) {
disktool_refresh();
set_status("Refreshed");
@@ -285,7 +295,9 @@ static bool handle_mouse(const Montauk::WinEvent& ev) {
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
if (clicked) {
if (handle_toolbar_click(ev.mouse.x, ev.mouse.y) || handle_content_click(ev.mouse.x, ev.mouse.y))
if (handle_toolbar_click(ev.mouse.x, ev.mouse.y) ||
handle_action_click(ev.mouse.x, ev.mouse.y) ||
handle_content_click(ev.mouse.x, ev.mouse.y))
return true;
}
@@ -365,6 +377,8 @@ extern "C" void _start() {
if (!fonts::init())
montauk::exit(1);
g_accent = mtk::load_system_accent();
disktool_refresh();
WsWindow win;
+91 -58
View File
@@ -9,22 +9,20 @@
#include <gui/mtk.hpp>
static mtk::Theme disks_theme() {
mtk::Theme theme = mtk::make_theme();
theme.window_bg = BG_COLOR;
theme.surface = TOOLBAR_BG;
theme.surface_alt = HEADER_BG;
theme.surface_hover = HOVER_BG;
theme.border = BORDER_COLOR;
theme.text = TEXT_COLOR;
theme.text_muted = FAINT_TEXT;
theme.text_subtle = DIM_TEXT;
theme.selection = HOVER_BG;
theme.accent_soft = HOVER_BG;
theme.disabled_bg = Color::from_rgb(0xF0, 0xF0, 0xF0);
mtk::Theme theme = mtk::make_theme(g_accent);
theme.selection = theme.accent_soft;
theme.disabled_bg = Color::from_rgb(0xE4, 0xE4, 0xE4);
theme.disabled_fg = Color::from_rgb(0x9A, 0x9A, 0x9A);
return theme;
}
static mtk::TableStyle partition_table_style(const mtk::Theme& theme) {
mtk::TableStyle style = mtk::make_table_style(theme);
style.row_selected_bg = theme.accent_soft;
style.row_hover_bg = mtk::mix(theme.window_bg, theme.accent, 12);
return style;
}
static bool mouse_in_rect(const Rect& rect) {
return rect.contains(g_state.mouse_x, g_state.mouse_y);
}
@@ -79,6 +77,13 @@ static void draw_centered_text(Canvas& c, int width, int y, const char* text, Co
c.text((width - tw) / 2, y, text, color);
}
static void draw_dialog_footer(Canvas& c, int dialog_w, int dialog_h, const mtk::Theme& theme) {
constexpr int FOOTER_H = 54;
int y = dialog_h - FOOTER_H;
c.fill_rect(0, y, dialog_w, FOOTER_H, theme.surface);
mtk::draw_separator(c, 0, y, dialog_w, theme);
}
static void render_toolbar(Canvas& c, const mtk::Theme& theme) {
auto& dt = g_state;
int fh = system_font_height();
@@ -98,21 +103,6 @@ static void render_toolbar(Canvas& c, const mtk::Theme& theme) {
c.text(8, (TOOLBAR_H - fh) / 2, "No disks detected", theme.text);
return;
}
bool has_sel = dt.selected_part >= 0;
Rect newpart = toolbar_newpart_button_rect();
Rect format = toolbar_format_button_rect();
Rect mount = toolbar_mount_button_rect();
Rect refresh = toolbar_refresh_button_rect();
mtk::draw_button(c, newpart, "New Part", mtk::BUTTON_PRIMARY,
toolbar_button_state(newpart, true), theme);
mtk::draw_button(c, format, "Format", mtk::BUTTON_TONAL,
toolbar_button_state(format, has_sel), theme);
mtk::draw_button(c, mount, "Mount", mtk::BUTTON_TONAL,
toolbar_button_state(mount, has_sel), theme);
mtk::draw_button(c, refresh, "Refresh", mtk::BUTTON_SECONDARY,
toolbar_button_state(refresh, true), theme);
}
static void render_content(Canvas& c, const mtk::Theme& theme) {
@@ -140,7 +130,7 @@ static void render_content(Canvas& c, const mtk::Theme& theme) {
y = partition_map_y();
Rect map_rect = {MAP_PAD, y, g_win_w - MAP_PAD * 2, MAP_H};
c.fill_rounded_rect(map_rect.x, map_rect.y, map_rect.w, map_rect.h, theme.radius_md, theme.surface_alt);
mtk::draw_rounded_frame(c, map_rect, theme.radius_md, theme.surface_alt, theme.border);
int part_indices[MAX_PARTS];
int nparts = get_disk_parts(part_indices, MAX_PARTS);
@@ -155,45 +145,60 @@ static void render_content(Canvas& c, const mtk::Theme& theme) {
if (part_x + part_w > map_rect.x + map_rect.w)
part_w = map_rect.x + map_rect.w - part_x;
Color col = part_colors[pi % NUM_PART_COLORS];
Rect segment = {part_x, map_rect.y + 3, part_w, MAP_H - 6};
if (pi == dt.selected_part) {
col = Color::from_rgb((col.r + 255) / 2,
(col.g + 255) / 2,
(col.b + 255) / 2);
c.fill_rounded_rect(segment.x - 1, segment.y - 1,
segment.w + 2, segment.h + 2,
theme.radius_sm, theme.accent);
}
c.fill_rounded_rect(part_x, map_rect.y + 2, part_w, MAP_H - 4, theme.radius_sm, col);
Color col = part_colors[pi % NUM_PART_COLORS];
c.fill_rounded_rect(segment.x, segment.y, segment.w, segment.h,
theme.radius_sm, col);
char label[8];
snprintf(label, sizeof(label), "%d", pi);
int label_w = text_width(label);
if (part_w > label_w + 4) {
c.text(part_x + (part_w - label_w) / 2,
c.text(segment.x + (segment.w - label_w) / 2,
map_rect.y + (MAP_H - fh) / 2,
label, WHITE);
label, theme.text_inverse);
}
}
}
y = partition_header_y();
c.fill_rect(0, y, g_win_w, HEADER_H, theme.surface_alt);
mtk::draw_separator(c, 0, y + HEADER_H - 1, g_win_w, theme);
int list_y = partition_list_y();
int list_bottom = g_win_h - STATUS_H;
int hovered_row = hovered_partition_row();
mtk::TableStyle table_style = partition_table_style(theme);
mtk::TableLayout table = {
{0, y, g_win_w, HEADER_H},
{0, list_y, g_win_w, list_bottom - list_y},
ITEM_H
};
int ty = y + (HEADER_H - fh) / 2;
int col_idx = 12;
int col_name = 40;
int col_type = g_win_w / 2 - 20;
int col_size = g_win_w - 160;
int col_lba = g_win_w - 80;
mtk::TableColumn cols[5] = {
{"#", col_idx, col_name - col_idx - 8},
{"Name", col_name, col_type - col_name - 10},
{"Type", col_type, col_size - col_type - 10},
{"Size", col_size, col_lba - col_size - 10},
{"LBA", col_lba, g_win_w - col_lba - 12},
};
c.text(col_idx, ty, "#", theme.text_subtle);
c.text(col_name, ty, "Name", theme.text_subtle);
c.text(col_type, ty, "Type", theme.text_subtle);
c.text(col_size, ty, "Size", theme.text_subtle);
c.text(col_lba, ty, "LBA", theme.text_subtle);
c.fill_rect(table.header.x, table.header.y, table.header.w, table.header.h,
table_style.header_bg);
c.hline(table.header.x, table.header.y + table.header.h - 1,
table.header.w, table_style.header_border);
int list_y = partition_list_y();
int list_bottom = g_win_h - STATUS_H;
int hovered_row = hovered_partition_row();
int ty = table.header.y + (table.header.h - fh) / 2;
for (int i = 0; i < 5; i++)
c.text(table.header.x + cols[i].x, ty, cols[i].label, table_style.header_text);
for (int pi = 0; pi < nparts; pi++) {
int row_y = list_y + pi * ITEM_H - dt.scroll_y;
@@ -202,11 +207,11 @@ static void render_content(Canvas& c, const mtk::Theme& theme) {
Rect row_rect = {0, row_y, g_win_w, ITEM_H};
if (pi == dt.selected_part) {
mtk::draw_list_row(c, row_rect, true, false, theme);
c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h,
table_style.row_selected_bg);
} else if (pi == hovered_row) {
c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h, theme.surface_hover);
} else {
mtk::draw_list_row(c, row_rect, false, (pi & 1) != 0, theme);
c.fill_rect(row_rect.x, row_rect.y, row_rect.w, row_rect.h,
table_style.row_hover_bg);
}
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
@@ -216,17 +221,17 @@ static void render_content(Canvas& c, const mtk::Theme& theme) {
int ry = row_y + (ITEM_H - fh) / 2;
draw_text_fit(c, col_name, ry, p.name[0] ? p.name : "(unnamed)",
col_type - col_name - 10, theme.text);
cols[1].w, theme.text);
draw_text_fit(c, col_type, ry, p.typeName,
col_size - col_type - 10, theme.text_subtle);
cols[2].w, theme.text_subtle);
char sz[24];
format_disk_size(sz, sizeof(sz), p.sectorCount, disk.sectorSizeLog);
c.text(col_size, ry, sz, theme.text);
draw_text_fit(c, col_size, ry, sz, cols[3].w, theme.text);
char lba_str[24];
snprintf(lba_str, sizeof(lba_str), "%lu", (unsigned)p.startLba);
c.text(col_lba, ry, lba_str, theme.text_muted);
draw_text_fit(c, col_lba, ry, lba_str, cols[4].w, theme.text_muted);
}
if (nparts == 0)
@@ -238,13 +243,34 @@ static void render_status(Canvas& c, const mtk::Theme& theme) {
int fh = system_font_height();
int sy = g_win_h - STATUS_H;
c.fill_rect(0, sy, g_win_w, STATUS_H, theme.surface_alt);
c.fill_rect(0, sy, g_win_w, STATUS_H, theme.surface);
mtk::draw_separator(c, 0, sy, g_win_w, theme);
bool has_disks = dt.disk_count > 0;
bool has_sel = dt.selected_part >= 0;
int text_right = g_win_w - 16;
if (has_disks) {
Rect newpart = toolbar_newpart_button_rect();
Rect format = toolbar_format_button_rect();
Rect mount = toolbar_mount_button_rect();
Rect refresh = toolbar_refresh_button_rect();
text_right = newpart.x - 12;
mtk::draw_button(c, newpart, "New Part", mtk::BUTTON_PRIMARY,
toolbar_button_state(newpart, true), theme);
mtk::draw_button(c, format, "Format", mtk::BUTTON_TONAL,
toolbar_button_state(format, has_sel), theme);
mtk::draw_button(c, mount, "Mount", mtk::BUTTON_TONAL,
toolbar_button_state(mount, has_sel), theme);
mtk::draw_button(c, refresh, "Refresh", mtk::BUTTON_SECONDARY,
toolbar_button_state(refresh, true), theme);
}
if (dt.status[0]) {
uint64_t age = montauk::get_milliseconds() - dt.status_time;
Color color = age < 5000 ? theme.text : theme.text_muted;
c.text(8, sy + (STATUS_H - fh) / 2, dt.status, color);
draw_text_fit(c, 16, sy + (STATUS_H - fh) / 2, dt.status,
text_right - 16, color);
}
}
@@ -265,11 +291,14 @@ void render_format_window() {
for (int i = 0; i < NUM_FS_TYPES; i++) {
Rect option = format_option_rect(FMT_DLG_W, i);
if (i == dlg.selected_fs)
c.fill_rounded_rect(option.x, option.y, option.w, option.h, theme.radius_md, theme.accent_soft);
if (i == dlg.selected_fs) {
mtk::draw_rounded_frame(c, option, theme.radius_md,
theme.accent_soft, theme.accent_soft);
}
mtk::draw_radio(c, option, g_fsTypes[i].name, i == dlg.selected_fs, theme);
}
draw_dialog_footer(c, FMT_DLG_W, FMT_DLG_H, theme);
mtk::draw_modal_actions(c,
dialog_primary_button_rect(FMT_DLG_W, FMT_DLG_H), "Format",
mtk::BUTTON_DANGER,
@@ -294,11 +323,15 @@ void render_newpart_window() {
Color warn_color = dlg.will_init_gpt ? theme.danger : theme.text;
int warn_y = 12 + fh * 2 + 20;
Rect warn_panel = {16, warn_y - 8, NP_DLG_W - 32, fh * 2 + 20};
Color warn_bg = dlg.will_init_gpt ? theme.danger_soft : theme.surface_alt;
mtk::draw_rounded_frame(c, warn_panel, theme.radius_md, warn_bg, warn_bg);
draw_centered_text(c, NP_DLG_W, warn_y, dlg.warn_line1, warn_color);
draw_centered_text(c, NP_DLG_W, warn_y + fh + 4, dlg.warn_line2, warn_color);
const char* confirm_label = dlg.will_init_gpt ? "Initialize" : "Create";
mtk::ButtonVariant confirm_variant = dlg.will_init_gpt ? mtk::BUTTON_DANGER : mtk::BUTTON_PRIMARY;
draw_dialog_footer(c, NP_DLG_W, NP_DLG_H, theme);
mtk::draw_modal_actions(c,
dialog_primary_button_rect(NP_DLG_W, NP_DLG_H), confirm_label,
confirm_variant,