feat: port Disks app to MTK
This commit is contained in:
@@ -10,7 +10,7 @@
|
|||||||
#include <montauk/string.h>
|
#include <montauk/string.h>
|
||||||
#include <montauk/heap.h>
|
#include <montauk/heap.h>
|
||||||
#include <gui/gui.hpp>
|
#include <gui/gui.hpp>
|
||||||
#include <gui/truetype.hpp>
|
#include <gui/font.hpp>
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -33,7 +33,6 @@ static constexpr int MAP_PAD = 16;
|
|||||||
static constexpr int MAX_PARTS = 32;
|
static constexpr int MAX_PARTS = 32;
|
||||||
static constexpr int MAX_DISKS = 8;
|
static constexpr int MAX_DISKS = 8;
|
||||||
static constexpr int STATUS_H = 26;
|
static constexpr int STATUS_H = 26;
|
||||||
static constexpr int FONT_SIZE = 16;
|
|
||||||
|
|
||||||
static constexpr int TB_BTN_Y = 7;
|
static constexpr int TB_BTN_Y = 7;
|
||||||
static constexpr int TB_BTN_H = 26;
|
static constexpr int TB_BTN_H = 26;
|
||||||
@@ -120,7 +119,6 @@ struct DiskToolState {
|
|||||||
|
|
||||||
extern int g_win_w, g_win_h;
|
extern int g_win_w, g_win_h;
|
||||||
extern DiskToolState g_state;
|
extern DiskToolState g_state;
|
||||||
extern TrueTypeFont* g_font;
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Partition colors
|
// Partition colors
|
||||||
@@ -128,6 +126,79 @@ extern TrueTypeFont* g_font;
|
|||||||
|
|
||||||
extern const Color part_colors[NUM_PART_COLORS];
|
extern const Color part_colors[NUM_PART_COLORS];
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Shared layout helpers
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
inline int disk_button_width(int idx) {
|
||||||
|
char label[8];
|
||||||
|
snprintf(label, sizeof(label), "Disk %d", idx);
|
||||||
|
return text_width(label) + 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Rect toolbar_disk_button_rect(int idx) {
|
||||||
|
int bx = 8;
|
||||||
|
for (int i = 0; i < idx; i++)
|
||||||
|
bx += disk_button_width(i) + 6;
|
||||||
|
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};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Rect toolbar_mount_button_rect() {
|
||||||
|
Rect refresh = toolbar_refresh_button_rect();
|
||||||
|
return {refresh.x - 6 - 60, TB_BTN_Y, 60, 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};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Rect toolbar_newpart_button_rect() {
|
||||||
|
Rect format = toolbar_format_button_rect();
|
||||||
|
return {format.x - 6 - 74, TB_BTN_Y, 74, TB_BTN_H};
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int content_title_y() {
|
||||||
|
return TOOLBAR_H + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int partition_map_y() {
|
||||||
|
return content_title_y() + system_font_height() + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int partition_header_y() {
|
||||||
|
return partition_map_y() + MAP_H + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int partition_list_y() {
|
||||||
|
return partition_header_y() + HEADER_H;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Rect format_option_rect(int dialog_w, int index) {
|
||||||
|
int fh = system_font_height();
|
||||||
|
int sel_y = 12 + fh * 2 + 18;
|
||||||
|
int opt_y = sel_y + fh + 8;
|
||||||
|
return {24, opt_y + index * 32, dialog_w - 48, 28};
|
||||||
|
}
|
||||||
|
|
||||||
|
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};
|
||||||
|
}
|
||||||
|
|
||||||
|
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};
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Function declarations — helpers (main.cpp)
|
// Function declarations — helpers (main.cpp)
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|||||||
+94
-82
@@ -15,7 +15,6 @@ int g_win_w = INIT_W;
|
|||||||
int g_win_h = INIT_H;
|
int g_win_h = INIT_H;
|
||||||
|
|
||||||
DiskToolState g_state;
|
DiskToolState g_state;
|
||||||
TrueTypeFont* g_font = nullptr;
|
|
||||||
|
|
||||||
const Color part_colors[NUM_PART_COLORS] = {
|
const Color part_colors[NUM_PART_COLORS] = {
|
||||||
Color::from_rgb(0x42, 0x7A, 0xB5),
|
Color::from_rgb(0x42, 0x7A, 0xB5),
|
||||||
@@ -66,61 +65,44 @@ void format_disk_size(char* buf, int bufsize, uint64_t sectors, uint16_t sectorS
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Toolbar hit testing (returns button widths matching render layout)
|
// Toolbar hit testing
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
static int disk_btn_width(int idx) {
|
|
||||||
char label[8];
|
|
||||||
snprintf(label, sizeof(label), "Disk %d", idx);
|
|
||||||
int lw = g_font ? g_font->measure_text(label, FONT_SIZE) + 16 : 48;
|
|
||||||
return lw;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool handle_toolbar_click(int mx, int my) {
|
static bool handle_toolbar_click(int mx, int my) {
|
||||||
if (my >= TOOLBAR_H || my < TB_BTN_Y) return false;
|
if (my >= TOOLBAR_H || my < TB_BTN_Y) return false;
|
||||||
|
|
||||||
auto& dt = g_state;
|
auto& dt = g_state;
|
||||||
|
if (dt.disk_count == 0) return false;
|
||||||
|
bool has_sel = dt.selected_part >= 0;
|
||||||
|
|
||||||
// Disk selector buttons
|
// Disk selector buttons
|
||||||
int bx = 8;
|
|
||||||
for (int i = 0; i < dt.disk_count; i++) {
|
for (int i = 0; i < dt.disk_count; i++) {
|
||||||
int lw = disk_btn_width(i);
|
if (toolbar_disk_button_rect(i).contains(mx, my)) {
|
||||||
if (mx >= bx && mx < bx + lw && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
|
|
||||||
dt.selected_disk = i;
|
dt.selected_disk = i;
|
||||||
dt.selected_part = -1;
|
dt.selected_part = -1;
|
||||||
dt.scroll_y = 0;
|
dt.scroll_y = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bx += lw + 6;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right-side buttons (must match render layout)
|
// Right-side buttons (must match render layout)
|
||||||
int rx = g_win_w - 8;
|
if (toolbar_refresh_button_rect().contains(mx, my)) {
|
||||||
|
|
||||||
int ref_w = 64; rx -= ref_w;
|
|
||||||
if (mx >= rx && mx < rx + ref_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
|
|
||||||
disktool_refresh();
|
disktool_refresh();
|
||||||
set_status("Refreshed");
|
set_status("Refreshed");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rx -= 6;
|
|
||||||
|
|
||||||
int mnt_w = 60; rx -= mnt_w;
|
if (has_sel && toolbar_mount_button_rect().contains(mx, my)) {
|
||||||
if (mx >= rx && mx < rx + mnt_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
|
|
||||||
do_mount_partition();
|
do_mount_partition();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rx -= 6;
|
|
||||||
|
|
||||||
int fmt_w = 64; rx -= fmt_w;
|
if (has_sel && toolbar_format_button_rect().contains(mx, my)) {
|
||||||
if (mx >= rx && mx < rx + fmt_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
|
|
||||||
open_format_dialog();
|
open_format_dialog();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
rx -= 6;
|
|
||||||
|
|
||||||
int np_w = 74; rx -= np_w;
|
if (toolbar_newpart_button_rect().contains(mx, my)) {
|
||||||
if (mx >= rx && mx < rx + np_w && my >= TB_BTN_Y && my < TB_BTN_Y + TB_BTN_H) {
|
|
||||||
do_create_partition();
|
do_create_partition();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -136,8 +118,7 @@ static bool handle_content_click(int mx, int my) {
|
|||||||
auto& dt = g_state;
|
auto& dt = g_state;
|
||||||
if (dt.disk_count == 0 || dt.selected_disk < 0) return false;
|
if (dt.disk_count == 0 || dt.selected_disk < 0) return false;
|
||||||
|
|
||||||
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
|
int y = partition_map_y();
|
||||||
int y = TOOLBAR_H + 8 + fh + 8;
|
|
||||||
|
|
||||||
// Partition map bar click
|
// Partition map bar click
|
||||||
int map_x = MAP_PAD;
|
int map_x = MAP_PAD;
|
||||||
@@ -163,12 +144,9 @@ static bool handle_content_click(int mx, int my) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
y += MAP_H + 8 + HEADER_H;
|
|
||||||
|
|
||||||
// Partition list click
|
// Partition list click
|
||||||
int list_bottom = g_win_h - STATUS_H;
|
if (my >= partition_list_y() && my < g_win_h - STATUS_H) {
|
||||||
if (my >= y && my < list_bottom) {
|
int row = (my - partition_list_y() + dt.scroll_y) / ITEM_H;
|
||||||
int row = (my - y + dt.scroll_y) / ITEM_H;
|
|
||||||
int part_indices[MAX_PARTS];
|
int part_indices[MAX_PARTS];
|
||||||
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
||||||
if (row >= 0 && row < nparts)
|
if (row >= 0 && row < nparts)
|
||||||
@@ -189,16 +167,10 @@ static bool handle_newpart_dialog_click(int mx, int my, bool clicked) {
|
|||||||
auto& dlg = g_state.np_dlg;
|
auto& dlg = g_state.np_dlg;
|
||||||
if (!dlg.open) return false;
|
if (!dlg.open) return false;
|
||||||
|
|
||||||
int dw = NP_DLG_W, dh = NP_DLG_H;
|
Rect confirm = dialog_primary_button_rect(NP_DLG_W, NP_DLG_H);
|
||||||
|
Rect cancel = dialog_secondary_button_rect(NP_DLG_W, NP_DLG_H);
|
||||||
int btn_w = 90, btn_h = 30;
|
dlg.hover_confirm = confirm.contains(mx, my);
|
||||||
int btn_y = dh - btn_h - 16;
|
dlg.hover_cancel = cancel.contains(mx, my);
|
||||||
int gap = 16;
|
|
||||||
int total_w = btn_w * 2 + gap;
|
|
||||||
int bx = (dw - total_w) / 2;
|
|
||||||
|
|
||||||
dlg.hover_confirm = (mx >= bx && mx < bx + btn_w && my >= btn_y && my < btn_y + btn_h);
|
|
||||||
dlg.hover_cancel = (mx >= bx + btn_w + gap && mx < bx + btn_w * 2 + gap && my >= btn_y && my < btn_y + btn_h);
|
|
||||||
|
|
||||||
if (!clicked) return true;
|
if (!clicked) return true;
|
||||||
|
|
||||||
@@ -223,27 +195,17 @@ static bool handle_format_dialog_click(int mx, int my, bool clicked) {
|
|||||||
auto& dlg = g_state.fmt_dlg;
|
auto& dlg = g_state.fmt_dlg;
|
||||||
if (!dlg.open) return false;
|
if (!dlg.open) return false;
|
||||||
|
|
||||||
int dw = FMT_DLG_W, dh = FMT_DLG_H;
|
|
||||||
int fh = g_font ? g_font->get_cache(FONT_SIZE)->ascent - g_font->get_cache(FONT_SIZE)->descent : 16;
|
|
||||||
|
|
||||||
// Button hit testing
|
// Button hit testing
|
||||||
int btn_w = 90, btn_h = 30;
|
Rect format_btn = dialog_primary_button_rect(FMT_DLG_W, FMT_DLG_H);
|
||||||
int btn_y = dh - btn_h - 16;
|
Rect cancel_btn = dialog_secondary_button_rect(FMT_DLG_W, FMT_DLG_H);
|
||||||
int gap = 16;
|
dlg.hover_format = format_btn.contains(mx, my);
|
||||||
int total_w = btn_w * 2 + gap;
|
dlg.hover_cancel = cancel_btn.contains(mx, my);
|
||||||
int bx = (dw - total_w) / 2;
|
|
||||||
|
|
||||||
dlg.hover_format = (mx >= bx && mx < bx + btn_w && my >= btn_y && my < btn_y + btn_h);
|
|
||||||
dlg.hover_cancel = (mx >= bx + btn_w + gap && mx < bx + btn_w * 2 + gap && my >= btn_y && my < btn_y + btn_h);
|
|
||||||
|
|
||||||
if (!clicked) return true;
|
if (!clicked) return true;
|
||||||
|
|
||||||
// FS type selector
|
// FS type selector
|
||||||
int sel_y = 12 + fh * 2 + 18;
|
|
||||||
int opt_y = sel_y + fh + 8;
|
|
||||||
for (int i = 0; i < NUM_FS_TYPES; i++) {
|
for (int i = 0; i < NUM_FS_TYPES; i++) {
|
||||||
int iy = opt_y + i * 32;
|
if (format_option_rect(FMT_DLG_W, i).contains(mx, my)) {
|
||||||
if (mx >= 24 && mx < dw - 24 && my >= iy && my < iy + 28) {
|
|
||||||
dlg.selected_fs = i;
|
dlg.selected_fs = i;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -262,6 +224,74 @@ static bool handle_format_dialog_click(int mx, int my, bool clicked) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int hovered_disk_button(int mx, int my) {
|
||||||
|
for (int i = 0; i < g_state.disk_count; i++) {
|
||||||
|
if (toolbar_disk_button_rect(i).contains(mx, my))
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hovered_partition_row(int mx, int my) {
|
||||||
|
if (g_state.disk_count == 0 || g_state.selected_disk < 0) return -1;
|
||||||
|
if (my < partition_list_y() || my >= g_win_h - STATUS_H) return -1;
|
||||||
|
|
||||||
|
int part_indices[MAX_PARTS];
|
||||||
|
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
||||||
|
int row = (my - partition_list_y() + g_state.scroll_y) / ITEM_H;
|
||||||
|
if (row < 0 || row >= nparts) return -1;
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int max_scroll_y() {
|
||||||
|
int part_indices[MAX_PARTS];
|
||||||
|
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
||||||
|
int visible_h = g_win_h - STATUS_H - partition_list_y();
|
||||||
|
int total_h = nparts * ITEM_H;
|
||||||
|
if (total_h <= visible_h) return 0;
|
||||||
|
return total_h - visible_h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void clamp_scroll() {
|
||||||
|
g_state.scroll_y = gui_clamp(g_state.scroll_y, 0, max_scroll_y());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool handle_mouse(const Montauk::WinEvent& ev) {
|
||||||
|
int prev_x = g_state.mouse_x;
|
||||||
|
int prev_y = g_state.mouse_y;
|
||||||
|
int prev_disk_hover = hovered_disk_button(prev_x, prev_y);
|
||||||
|
int prev_part_hover = hovered_partition_row(prev_x, prev_y);
|
||||||
|
bool prev_refresh_hover = toolbar_refresh_button_rect().contains(prev_x, prev_y);
|
||||||
|
bool prev_mount_hover = toolbar_mount_button_rect().contains(prev_x, prev_y);
|
||||||
|
bool prev_format_hover = toolbar_format_button_rect().contains(prev_x, prev_y);
|
||||||
|
bool prev_newpart_hover = toolbar_newpart_button_rect().contains(prev_x, prev_y);
|
||||||
|
|
||||||
|
g_state.mouse_x = ev.mouse.x;
|
||||||
|
g_state.mouse_y = ev.mouse.y;
|
||||||
|
|
||||||
|
bool redraw = false;
|
||||||
|
redraw |= (prev_disk_hover != hovered_disk_button(g_state.mouse_x, g_state.mouse_y));
|
||||||
|
redraw |= (prev_part_hover != hovered_partition_row(g_state.mouse_x, g_state.mouse_y));
|
||||||
|
redraw |= (prev_refresh_hover != toolbar_refresh_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
|
||||||
|
redraw |= (prev_mount_hover != toolbar_mount_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
|
||||||
|
redraw |= (prev_format_hover != toolbar_format_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
|
||||||
|
redraw |= (prev_newpart_hover != toolbar_newpart_button_rect().contains(g_state.mouse_x, g_state.mouse_y));
|
||||||
|
|
||||||
|
if (ev.mouse.scroll != 0) {
|
||||||
|
g_state.scroll_y -= ev.mouse.scroll * 20;
|
||||||
|
clamp_scroll();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return redraw;
|
||||||
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Key handling
|
// Key handling
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -329,16 +359,11 @@ extern "C" void _start() {
|
|||||||
montauk::memset(&g_state, 0, sizeof(g_state));
|
montauk::memset(&g_state, 0, sizeof(g_state));
|
||||||
g_state.selected_disk = 0;
|
g_state.selected_disk = 0;
|
||||||
g_state.selected_part = -1;
|
g_state.selected_part = -1;
|
||||||
|
g_state.mouse_x = -1;
|
||||||
|
g_state.mouse_y = -1;
|
||||||
|
|
||||||
// Load font
|
if (!fonts::init())
|
||||||
{
|
montauk::exit(1);
|
||||||
TrueTypeFont* f = (TrueTypeFont*)montauk::malloc(sizeof(TrueTypeFont));
|
|
||||||
if (f) {
|
|
||||||
montauk::memset(f, 0, sizeof(TrueTypeFont));
|
|
||||||
if (!f->init("0:/fonts/Roboto-Medium.ttf")) { montauk::mfree(f); f = nullptr; }
|
|
||||||
}
|
|
||||||
g_font = f;
|
|
||||||
}
|
|
||||||
|
|
||||||
disktool_refresh();
|
disktool_refresh();
|
||||||
|
|
||||||
@@ -418,6 +443,7 @@ extern "C" void _start() {
|
|||||||
g_win_w = win.width;
|
g_win_w = win.width;
|
||||||
g_win_h = win.height;
|
g_win_h = win.height;
|
||||||
pixels = win.pixels;
|
pixels = win.pixels;
|
||||||
|
clamp_scroll();
|
||||||
redraw_main = true;
|
redraw_main = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -430,21 +456,7 @@ extern "C" void _start() {
|
|||||||
|
|
||||||
// Mouse
|
// Mouse
|
||||||
if (ev.type == 1) {
|
if (ev.type == 1) {
|
||||||
int mx = ev.mouse.x;
|
redraw_main |= handle_mouse(ev);
|
||||||
int my = ev.mouse.y;
|
|
||||||
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
|
|
||||||
|
|
||||||
// Scroll
|
|
||||||
if (ev.mouse.scroll != 0) {
|
|
||||||
g_state.scroll_y -= ev.mouse.scroll * 20;
|
|
||||||
if (g_state.scroll_y < 0) g_state.scroll_y = 0;
|
|
||||||
redraw_main = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clicked) {
|
|
||||||
if (handle_toolbar_click(mx, my) || handle_content_click(mx, my))
|
|
||||||
redraw_main = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (redraw_main) { render(pixels); win.present(); }
|
if (redraw_main) { render(pixels); win.present(); }
|
||||||
|
|||||||
+209
-288
@@ -1,184 +1,146 @@
|
|||||||
/*
|
/*
|
||||||
* render.cpp
|
* render.cpp
|
||||||
* MontaukOS Disk Tool — rendering
|
* MontaukOS Disk Tool - rendering
|
||||||
* Copyright (c) 2026 Daniel Hammer
|
* Copyright (c) 2026 Daniel Hammer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "disks.h"
|
#include "disks.h"
|
||||||
|
#include <gui/canvas.hpp>
|
||||||
|
#include <gui/mtk.hpp>
|
||||||
|
|
||||||
// ============================================================================
|
static mtk::Theme disks_theme() {
|
||||||
// Pixel helpers
|
mtk::Theme theme = mtk::make_theme();
|
||||||
// ============================================================================
|
theme.window_bg = BG_COLOR;
|
||||||
|
theme.surface = TOOLBAR_BG;
|
||||||
static void px_fill(uint32_t* px, int bw, int bh,
|
theme.surface_alt = HEADER_BG;
|
||||||
int x, int y, int w, int h, Color c) {
|
theme.surface_hover = HOVER_BG;
|
||||||
uint32_t v = c.to_pixel();
|
theme.border = BORDER_COLOR;
|
||||||
int x0 = x < 0 ? 0 : x, y0 = y < 0 ? 0 : y;
|
theme.text = TEXT_COLOR;
|
||||||
int x1 = x + w > bw ? bw : x + w;
|
theme.text_muted = FAINT_TEXT;
|
||||||
int y1 = y + h > bh ? bh : y + h;
|
theme.text_subtle = DIM_TEXT;
|
||||||
for (int row = y0; row < y1; row++)
|
theme.selection = HOVER_BG;
|
||||||
for (int col = x0; col < x1; col++)
|
theme.accent_soft = HOVER_BG;
|
||||||
px[row * bw + col] = v;
|
theme.disabled_bg = Color::from_rgb(0xF0, 0xF0, 0xF0);
|
||||||
|
theme.disabled_fg = Color::from_rgb(0x9A, 0x9A, 0x9A);
|
||||||
|
return theme;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void px_hline(uint32_t* px, int bw, int bh, int x, int y, int w, Color c) {
|
static bool mouse_in_rect(const Rect& rect) {
|
||||||
if (y < 0 || y >= bh) return;
|
return rect.contains(g_state.mouse_x, g_state.mouse_y);
|
||||||
uint32_t v = c.to_pixel();
|
|
||||||
int x0 = x < 0 ? 0 : x;
|
|
||||||
int x1 = x + w > bw ? bw : x + w;
|
|
||||||
for (int col = x0; col < x1; col++)
|
|
||||||
px[y * bw + col] = v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void px_fill_rounded(uint32_t* px, int bw, int bh,
|
static mtk::WidgetState toolbar_button_state(const Rect& rect, bool enabled, bool active = false) {
|
||||||
int x, int y, int w, int h, int r, Color c) {
|
return mtk::widget_state(active, mouse_in_rect(rect), enabled);
|
||||||
uint32_t v = c.to_pixel();
|
|
||||||
for (int row = 0; row < h; row++) {
|
|
||||||
int dy = y + row;
|
|
||||||
if (dy < 0 || dy >= bh) continue;
|
|
||||||
for (int col = 0; col < w; col++) {
|
|
||||||
int dx = x + col;
|
|
||||||
if (dx < 0 || dx >= bw) continue;
|
|
||||||
bool skip = false;
|
|
||||||
int cx, cy;
|
|
||||||
if (col < r && row < r) { cx = r - col - 1; cy = r - row - 1; if (cx*cx + cy*cy >= r*r) skip = true; }
|
|
||||||
else if (col >= w - r && row < r) { cx = col - (w - r); cy = r - row - 1; if (cx*cx + cy*cy >= r*r) skip = true; }
|
|
||||||
else if (col < r && row >= h - r) { cx = r - col - 1; cy = row - (h - r); if (cx*cx + cy*cy >= r*r) skip = true; }
|
|
||||||
else if (col >= w - r && row >= h - r) { cx = col - (w - r); cy = row - (h - r); if (cx*cx + cy*cy >= r*r) skip = true; }
|
|
||||||
if (!skip) px[dy * bw + dx] = v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void px_rect(uint32_t* px, int bw, int bh,
|
static void draw_text_fit(Canvas& c, int x, int y,
|
||||||
int x, int y, int w, int h, Color c) {
|
const char* text, int max_w, Color color) {
|
||||||
px_hline(px, bw, bh, x, y, w, c);
|
if (!text || !text[0] || max_w <= 4) return;
|
||||||
px_hline(px, bw, bh, x, y + h - 1, w, c);
|
if (text_width(text) <= max_w) {
|
||||||
for (int row = y; row < y + h; row++) {
|
c.text(x, y, text, color);
|
||||||
if (row < 0 || row >= bh) continue;
|
|
||||||
if (x >= 0 && x < bw) px[row * bw + x] = c.to_pixel();
|
|
||||||
int rx = x + w - 1;
|
|
||||||
if (rx >= 0 && rx < bw) px[row * bw + rx] = c.to_pixel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void px_text(uint32_t* px, int bw, int bh,
|
|
||||||
int x, int y, const char* text, Color c) {
|
|
||||||
if (g_font)
|
|
||||||
g_font->draw_to_buffer(px, bw, bh, x, y, text, c, FONT_SIZE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int text_w(const char* text) {
|
|
||||||
return g_font ? g_font->measure_text(text, FONT_SIZE) : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int font_h() {
|
|
||||||
if (!g_font) return 16;
|
|
||||||
auto* cache = g_font->get_cache(FONT_SIZE);
|
|
||||||
return cache->ascent - cache->descent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Button helper
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
static void px_button(uint32_t* px, int bw, int bh,
|
|
||||||
int x, int y, int w, int h,
|
|
||||||
const char* label, bool enabled, bool hovered, bool primary) {
|
|
||||||
Color bg, fg;
|
|
||||||
if (!enabled) {
|
|
||||||
bg = Color::from_rgb(0xF0, 0xF0, 0xF0);
|
|
||||||
fg = Color::from_rgb(0x9A, 0x9A, 0x9A);
|
|
||||||
} else if (primary) {
|
|
||||||
bg = hovered ? Color::from_rgb(0x2B, 0x6B, 0xE0) : colors::ACCENT;
|
|
||||||
fg = WHITE;
|
|
||||||
} else {
|
|
||||||
bg = hovered ? Color::from_rgb(0xDC, 0xDC, 0xDC) : Color::from_rgb(0xE8, 0xE8, 0xE8);
|
|
||||||
fg = TEXT_COLOR;
|
|
||||||
}
|
|
||||||
px_fill_rounded(px, bw, bh, x, y, w, h, 6, bg);
|
|
||||||
int tw = text_w(label);
|
|
||||||
int fh = font_h();
|
|
||||||
px_text(px, bw, bh, x + (w - tw) / 2, y + (h - fh) / 2, label, fg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Render: main view
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
static void render_toolbar(uint32_t* px) {
|
|
||||||
auto& dt = g_state;
|
|
||||||
int fh = font_h();
|
|
||||||
|
|
||||||
px_fill(px, g_win_w, g_win_h, 0, 0, g_win_w, TOOLBAR_H, TOOLBAR_BG);
|
|
||||||
px_hline(px, g_win_w, g_win_h, 0, TOOLBAR_H - 1, g_win_w, BORDER_COLOR);
|
|
||||||
|
|
||||||
int bx = 8;
|
|
||||||
for (int i = 0; i < dt.disk_count; i++) {
|
|
||||||
char label[8];
|
|
||||||
snprintf(label, sizeof(label), "Disk %d", i);
|
|
||||||
int lw = text_w(label) + 16;
|
|
||||||
px_button(px, g_win_w, g_win_h, bx, TB_BTN_Y, lw, TB_BTN_H, label, true, false, false);
|
|
||||||
bx += lw + 6;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dt.disk_count == 0) {
|
|
||||||
px_text(px, g_win_w, g_win_h, 8, (TOOLBAR_H - fh) / 2, "No disks detected", TEXT_COLOR);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right-side action buttons
|
const char* ellipsis = "...";
|
||||||
bool has_sel = dt.selected_part >= 0;
|
int ell_w = text_width(ellipsis);
|
||||||
int rx = g_win_w - 8;
|
if (ell_w >= max_w) return;
|
||||||
|
|
||||||
int ref_w = 64; rx -= ref_w;
|
char buf[96];
|
||||||
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, ref_w, TB_BTN_H,
|
int out = 0;
|
||||||
"Refresh", true, false, false);
|
while (text[out] && out < (int)sizeof(buf) - 4) {
|
||||||
rx -= 6;
|
buf[out] = text[out];
|
||||||
|
buf[out + 1] = '\0';
|
||||||
|
if (text_width(buf) + ell_w > max_w)
|
||||||
|
break;
|
||||||
|
out++;
|
||||||
|
}
|
||||||
|
|
||||||
int mnt_w = 60; rx -= mnt_w;
|
if (out <= 0) return;
|
||||||
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, mnt_w, TB_BTN_H,
|
buf[out] = '.';
|
||||||
"Mount", has_sel, false, true);
|
buf[out + 1] = '.';
|
||||||
rx -= 6;
|
buf[out + 2] = '.';
|
||||||
|
buf[out + 3] = '\0';
|
||||||
int fmt_w = 64; rx -= fmt_w;
|
c.text(x, y, buf, color);
|
||||||
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, fmt_w, TB_BTN_H,
|
|
||||||
"Format", has_sel, false, true);
|
|
||||||
rx -= 6;
|
|
||||||
|
|
||||||
int np_w = 74; rx -= np_w;
|
|
||||||
px_button(px, g_win_w, g_win_h, rx, TB_BTN_Y, np_w, TB_BTN_H,
|
|
||||||
"New Part", true, false, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_content(uint32_t* px) {
|
static int hovered_partition_row() {
|
||||||
|
if (g_state.disk_count == 0 || g_state.selected_disk < 0) return -1;
|
||||||
|
if (g_state.mouse_y < partition_list_y() || g_state.mouse_y >= g_win_h - STATUS_H) return -1;
|
||||||
|
|
||||||
|
int part_indices[MAX_PARTS];
|
||||||
|
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
||||||
|
int row = (g_state.mouse_y - partition_list_y() + g_state.scroll_y) / ITEM_H;
|
||||||
|
if (row < 0 || row >= nparts) return -1;
|
||||||
|
return row;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void draw_centered_text(Canvas& c, int width, int y, const char* text, Color color) {
|
||||||
|
int tw = text_width(text);
|
||||||
|
c.text((width - tw) / 2, y, text, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void render_toolbar(Canvas& c, const mtk::Theme& theme) {
|
||||||
auto& dt = g_state;
|
auto& dt = g_state;
|
||||||
int fh = font_h();
|
int fh = system_font_height();
|
||||||
|
|
||||||
|
c.fill_rect(0, 0, g_win_w, TOOLBAR_H, theme.surface);
|
||||||
|
mtk::draw_separator(c, 0, TOOLBAR_H - 1, g_win_w, theme);
|
||||||
|
|
||||||
|
for (int i = 0; i < dt.disk_count; i++) {
|
||||||
|
Rect button = toolbar_disk_button_rect(i);
|
||||||
|
char label[8];
|
||||||
|
snprintf(label, sizeof(label), "Disk %d", i);
|
||||||
|
mtk::draw_button(c, button, label, mtk::BUTTON_SECONDARY,
|
||||||
|
toolbar_button_state(button, true, i == dt.selected_disk), theme);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dt.disk_count == 0) {
|
||||||
|
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) {
|
||||||
|
auto& dt = g_state;
|
||||||
|
int fh = system_font_height();
|
||||||
|
|
||||||
if (dt.disk_count == 0 || dt.selected_disk < 0 || dt.selected_disk >= dt.disk_count)
|
if (dt.disk_count == 0 || dt.selected_disk < 0 || dt.selected_disk >= dt.disk_count)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Montauk::DiskInfo& disk = dt.disks[dt.selected_disk];
|
Montauk::DiskInfo& disk = dt.disks[dt.selected_disk];
|
||||||
int y = TOOLBAR_H + 8;
|
int y = content_title_y();
|
||||||
|
|
||||||
// Disk model name
|
char info_str[64];
|
||||||
px_text(px, g_win_w, g_win_h, MAP_PAD, y, disk.model, TEXT_COLOR);
|
char size_str[24];
|
||||||
|
|
||||||
// Size and type on the right
|
|
||||||
char info_str[64], size_str[24];
|
|
||||||
format_disk_size(size_str, sizeof(size_str), disk.sectorCount, disk.sectorSizeLog);
|
format_disk_size(size_str, sizeof(size_str), disk.sectorCount, disk.sectorSizeLog);
|
||||||
const char* dtype = disk.rpm == 1 ? "SSD" : "HDD";
|
const char* dtype = disk.rpm == 1 ? "SSD" : "HDD";
|
||||||
snprintf(info_str, sizeof(info_str), "%s %s", size_str, dtype);
|
snprintf(info_str, sizeof(info_str), "%s %s", size_str, dtype);
|
||||||
int iw = text_w(info_str);
|
|
||||||
px_text(px, g_win_w, g_win_h, g_win_w - iw - MAP_PAD, y, info_str, DIM_TEXT);
|
|
||||||
|
|
||||||
y += fh + 8;
|
int info_w = text_width(info_str);
|
||||||
|
int info_x = g_win_w - info_w - MAP_PAD;
|
||||||
|
int model_max_w = info_x - MAP_PAD - 12;
|
||||||
|
draw_text_fit(c, MAP_PAD, y, disk.model, model_max_w, theme.text);
|
||||||
|
c.text(info_x, y, info_str, theme.text_subtle);
|
||||||
|
|
||||||
// Partition map bar
|
y = partition_map_y();
|
||||||
int map_x = MAP_PAD;
|
|
||||||
int map_w = g_win_w - MAP_PAD * 2;
|
Rect map_rect = {MAP_PAD, y, g_win_w - MAP_PAD * 2, MAP_H};
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, map_x, y, map_w, MAP_H, 6,
|
c.fill_rounded_rect(map_rect.x, map_rect.y, map_rect.w, map_rect.h, theme.radius_md, theme.surface_alt);
|
||||||
Color::from_rgb(0xE4, 0xE4, 0xE4));
|
|
||||||
|
|
||||||
int part_indices[MAX_PARTS];
|
int part_indices[MAX_PARTS];
|
||||||
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
int nparts = get_disk_parts(part_indices, MAX_PARTS);
|
||||||
@@ -187,213 +149,172 @@ static void render_content(uint32_t* px) {
|
|||||||
if (total_sectors > 0 && nparts > 0) {
|
if (total_sectors > 0 && nparts > 0) {
|
||||||
for (int pi = 0; pi < nparts; pi++) {
|
for (int pi = 0; pi < nparts; pi++) {
|
||||||
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
|
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
|
||||||
int ppx = map_x + (int)((p.startLba * (uint64_t)map_w) / total_sectors);
|
int part_x = map_rect.x + (int)((p.startLba * (uint64_t)map_rect.w) / total_sectors);
|
||||||
int pw = (int)((p.sectorCount * (uint64_t)map_w) / total_sectors);
|
int part_w = (int)((p.sectorCount * (uint64_t)map_rect.w) / total_sectors);
|
||||||
if (pw < 2) pw = 2;
|
if (part_w < 2) part_w = 2;
|
||||||
if (ppx + pw > map_x + map_w) pw = map_x + map_w - ppx;
|
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];
|
Color col = part_colors[pi % NUM_PART_COLORS];
|
||||||
if (pi == dt.selected_part) {
|
if (pi == dt.selected_part) {
|
||||||
col = Color::from_rgb((col.r + 255) / 2, (col.g + 255) / 2, (col.b + 255) / 2);
|
col = Color::from_rgb((col.r + 255) / 2,
|
||||||
|
(col.g + 255) / 2,
|
||||||
|
(col.b + 255) / 2);
|
||||||
}
|
}
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, ppx, y + 2, pw, MAP_H - 4, 3, col);
|
c.fill_rounded_rect(part_x, map_rect.y + 2, part_w, MAP_H - 4, theme.radius_sm, col);
|
||||||
|
|
||||||
char plabel[8];
|
char label[8];
|
||||||
snprintf(plabel, sizeof(plabel), "%d", pi);
|
snprintf(label, sizeof(label), "%d", pi);
|
||||||
int plw = text_w(plabel);
|
int label_w = text_width(label);
|
||||||
if (pw > plw + 4)
|
if (part_w > label_w + 4) {
|
||||||
px_text(px, g_win_w, g_win_h, ppx + (pw - plw) / 2, y + (MAP_H - fh) / 2, plabel, WHITE);
|
c.text(part_x + (part_w - label_w) / 2,
|
||||||
|
map_rect.y + (MAP_H - fh) / 2,
|
||||||
|
label, WHITE);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
y += MAP_H + 8;
|
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);
|
||||||
|
|
||||||
// Column header
|
|
||||||
px_fill(px, g_win_w, g_win_h, 0, y, g_win_w, HEADER_H, HEADER_BG);
|
|
||||||
int ty = y + (HEADER_H - fh) / 2;
|
int ty = y + (HEADER_H - fh) / 2;
|
||||||
|
|
||||||
int col_idx = 12;
|
int col_idx = 12;
|
||||||
int col_name = 40;
|
int col_name = 40;
|
||||||
int col_type = g_win_w / 2 - 20;
|
int col_type = g_win_w / 2 - 20;
|
||||||
int col_size = g_win_w - 160;
|
int col_size = g_win_w - 160;
|
||||||
int col_lba = g_win_w - 80;
|
int col_lba = g_win_w - 80;
|
||||||
|
|
||||||
px_text(px, g_win_w, g_win_h, col_idx, ty, "#", DIM_TEXT);
|
c.text(col_idx, ty, "#", theme.text_subtle);
|
||||||
px_text(px, g_win_w, g_win_h, col_name, ty, "Name", DIM_TEXT);
|
c.text(col_name, ty, "Name", theme.text_subtle);
|
||||||
px_text(px, g_win_w, g_win_h, col_type, ty, "Type", DIM_TEXT);
|
c.text(col_type, ty, "Type", theme.text_subtle);
|
||||||
px_text(px, g_win_w, g_win_h, col_size, ty, "Size", DIM_TEXT);
|
c.text(col_size, ty, "Size", theme.text_subtle);
|
||||||
px_text(px, g_win_w, g_win_h, col_lba, ty, "LBA", DIM_TEXT);
|
c.text(col_lba, ty, "LBA", theme.text_subtle);
|
||||||
px_hline(px, g_win_w, g_win_h, 0, y + HEADER_H - 1, g_win_w, BORDER_COLOR);
|
|
||||||
y += HEADER_H;
|
|
||||||
|
|
||||||
// Partition rows
|
int list_y = partition_list_y();
|
||||||
int list_bottom = g_win_h - STATUS_H;
|
int list_bottom = g_win_h - STATUS_H;
|
||||||
int list_y = y;
|
int hovered_row = hovered_partition_row();
|
||||||
|
|
||||||
for (int pi = 0; pi < nparts; pi++) {
|
for (int pi = 0; pi < nparts; pi++) {
|
||||||
int row_y = list_y + pi * ITEM_H - dt.scroll_y;
|
int row_y = list_y + pi * ITEM_H - dt.scroll_y;
|
||||||
if (row_y + ITEM_H <= list_y) continue;
|
if (row_y + ITEM_H <= list_y) continue;
|
||||||
if (row_y >= list_bottom) break;
|
if (row_y >= list_bottom) break;
|
||||||
|
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
|
||||||
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
|
Montauk::PartInfo& p = dt.parts[part_indices[pi]];
|
||||||
|
|
||||||
if (pi == dt.selected_part)
|
c.fill_rounded_rect(col_idx, row_y + (ITEM_H - 10) / 2, 10, 10, 5,
|
||||||
px_fill(px, g_win_w, g_win_h, 0, row_y, g_win_w, ITEM_H, HOVER_BG);
|
|
||||||
|
|
||||||
px_fill_rounded(px, g_win_w, g_win_h, col_idx, row_y + (ITEM_H - 10) / 2, 10, 10, 5,
|
|
||||||
part_colors[pi % NUM_PART_COLORS]);
|
part_colors[pi % NUM_PART_COLORS]);
|
||||||
|
|
||||||
int ry = row_y + (ITEM_H - fh) / 2;
|
int ry = row_y + (ITEM_H - fh) / 2;
|
||||||
px_text(px, g_win_w, g_win_h, col_name, ry, p.name[0] ? p.name : "(unnamed)", TEXT_COLOR);
|
draw_text_fit(c, col_name, ry, p.name[0] ? p.name : "(unnamed)",
|
||||||
px_text(px, g_win_w, g_win_h, col_type, ry, p.typeName, DIM_TEXT);
|
col_type - col_name - 10, theme.text);
|
||||||
|
draw_text_fit(c, col_type, ry, p.typeName,
|
||||||
|
col_size - col_type - 10, theme.text_subtle);
|
||||||
|
|
||||||
char sz[24];
|
char sz[24];
|
||||||
format_disk_size(sz, sizeof(sz), p.sectorCount, disk.sectorSizeLog);
|
format_disk_size(sz, sizeof(sz), p.sectorCount, disk.sectorSizeLog);
|
||||||
px_text(px, g_win_w, g_win_h, col_size, ry, sz, TEXT_COLOR);
|
c.text(col_size, ry, sz, theme.text);
|
||||||
|
|
||||||
char lba_str[24];
|
char lba_str[24];
|
||||||
snprintf(lba_str, sizeof(lba_str), "%lu", (unsigned)p.startLba);
|
snprintf(lba_str, sizeof(lba_str), "%lu", (unsigned)p.startLba);
|
||||||
px_text(px, g_win_w, g_win_h, col_lba, ry, lba_str, FAINT_TEXT);
|
c.text(col_lba, ry, lba_str, theme.text_muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nparts == 0)
|
if (nparts == 0)
|
||||||
px_text(px, g_win_w, g_win_h, col_name, list_y + 8, "No partitions found", FAINT_TEXT);
|
c.text(col_name, list_y + 8, "No partitions found", theme.text_muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_status(uint32_t* px) {
|
static void render_status(Canvas& c, const mtk::Theme& theme) {
|
||||||
auto& dt = g_state;
|
auto& dt = g_state;
|
||||||
int fh = font_h();
|
int fh = system_font_height();
|
||||||
|
|
||||||
int sy = g_win_h - STATUS_H;
|
int sy = g_win_h - STATUS_H;
|
||||||
px_fill(px, g_win_w, g_win_h, 0, sy, g_win_w, STATUS_H, STATUS_BG_COL);
|
c.fill_rect(0, sy, g_win_w, STATUS_H, theme.surface_alt);
|
||||||
px_hline(px, g_win_w, g_win_h, 0, sy, g_win_w, BORDER_COLOR);
|
mtk::draw_separator(c, 0, sy, g_win_w, theme);
|
||||||
|
|
||||||
if (dt.status[0]) {
|
if (dt.status[0]) {
|
||||||
uint64_t age = montauk::get_milliseconds() - dt.status_time;
|
uint64_t age = montauk::get_milliseconds() - dt.status_time;
|
||||||
Color sc = (age < 5000)
|
Color color = age < 5000 ? theme.text : theme.text_muted;
|
||||||
? Color::from_rgb(0x33, 0x33, 0x33)
|
c.text(8, sy + (STATUS_H - fh) / 2, dt.status, color);
|
||||||
: Color::from_rgb(0xAA, 0xAA, 0xAA);
|
|
||||||
px_text(px, g_win_w, g_win_h, 8, sy + (STATUS_H - fh) / 2, dt.status, sc);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Render: format dialog window
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
void render_format_window() {
|
void render_format_window() {
|
||||||
auto& dlg = g_state.fmt_dlg;
|
auto& dlg = g_state.fmt_dlg;
|
||||||
if (!dlg.open) return;
|
if (!dlg.open || !dlg.pixels) return;
|
||||||
|
|
||||||
uint32_t* px = dlg.pixels;
|
Canvas c(dlg.pixels, FMT_DLG_W, FMT_DLG_H);
|
||||||
int dw = FMT_DLG_W, dh = FMT_DLG_H;
|
mtk::Theme theme = disks_theme();
|
||||||
int fh = font_h();
|
c.fill(theme.window_bg);
|
||||||
|
|
||||||
px_fill(px, dw, dh, 0, 0, dw, dh, BG_COLOR);
|
int fh = system_font_height();
|
||||||
|
draw_centered_text(c, FMT_DLG_W, 12, "Format Partition", theme.text);
|
||||||
|
draw_centered_text(c, FMT_DLG_W, 12 + fh + 6, dlg.part_desc, theme.text_subtle);
|
||||||
|
|
||||||
// Title
|
int label_y = 12 + fh * 2 + 18;
|
||||||
const char* title = "Format Partition";
|
c.text(16, label_y, "Filesystem", theme.text_muted);
|
||||||
int tw = text_w(title);
|
|
||||||
px_text(px, dw, dh, (dw - tw) / 2, 12, title, TEXT_COLOR);
|
|
||||||
|
|
||||||
// Partition description
|
|
||||||
int ddw = text_w(dlg.part_desc);
|
|
||||||
px_text(px, dw, dh, (dw - ddw) / 2, 12 + fh + 6, dlg.part_desc, DIM_TEXT);
|
|
||||||
|
|
||||||
// Filesystem selector
|
|
||||||
int sel_y = 12 + fh * 2 + 18;
|
|
||||||
px_text(px, dw, dh, 16, sel_y, "Filesystem:", TEXT_COLOR);
|
|
||||||
|
|
||||||
int opt_y = sel_y + fh + 8;
|
|
||||||
for (int i = 0; i < NUM_FS_TYPES; i++) {
|
for (int i = 0; i < NUM_FS_TYPES; i++) {
|
||||||
int ox = 24;
|
Rect option = format_option_rect(FMT_DLG_W, i);
|
||||||
int ow = dw - 48;
|
|
||||||
int oh = 28;
|
|
||||||
int iy = opt_y + i * (oh + 4);
|
|
||||||
|
|
||||||
if (i == dlg.selected_fs) {
|
|
||||||
px_fill_rounded(px, dw, dh, ox, iy, ow, oh, 6,
|
|
||||||
Color::from_rgb(0xD0, 0xE0, 0xF0));
|
|
||||||
px_fill_rounded(px, dw, dh, ox - 1, iy - 1, ow + 2, oh + 2, 7, Color::from_rgb(0x42, 0x7A, 0xB5));
|
|
||||||
px_fill_rounded(px, dw, dh, ox, iy, ow, oh, 6, Color::from_rgb(0xD0, 0xE0, 0xF0));
|
|
||||||
} else {
|
|
||||||
px_fill_rounded(px, dw, dh, ox - 1, iy - 1, ow + 2, oh + 2, 7, BORDER_COLOR);
|
|
||||||
px_fill_rounded(px, dw, dh, ox, iy, ow, oh, 6, BG_COLOR);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Radio indicator (circular)
|
|
||||||
int cx = ox + 14;
|
|
||||||
int cy = iy + oh / 2;
|
|
||||||
px_fill_rounded(px, dw, dh, cx - 5, cy - 5, 10, 10, 5, DIM_TEXT);
|
|
||||||
px_fill_rounded(px, dw, dh, cx - 4, cy - 4, 8, 8, 4, BG_COLOR);
|
|
||||||
if (i == dlg.selected_fs)
|
if (i == dlg.selected_fs)
|
||||||
px_fill_rounded(px, dw, dh, cx - 3, cy - 3, 6, 6, 3, Color::from_rgb(0x42, 0x7A, 0xB5));
|
c.fill_rounded_rect(option.x, option.y, option.w, option.h, theme.radius_md, theme.accent_soft);
|
||||||
|
mtk::draw_radio(c, option, g_fsTypes[i].name, i == dlg.selected_fs, theme);
|
||||||
px_text(px, dw, dh, cx + 12, iy + (oh - fh) / 2, g_fsTypes[i].name, TEXT_COLOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Buttons
|
mtk::draw_modal_actions(c,
|
||||||
int btn_w = 90, btn_h = 30;
|
dialog_primary_button_rect(FMT_DLG_W, FMT_DLG_H), "Format",
|
||||||
int btn_y = dh - btn_h - 16;
|
mtk::BUTTON_DANGER,
|
||||||
int gap = 16;
|
mtk::widget_state(false, dlg.hover_format),
|
||||||
int total_w = btn_w * 2 + gap;
|
dialog_secondary_button_rect(FMT_DLG_W, FMT_DLG_H), "Cancel",
|
||||||
int bx = (dw - total_w) / 2;
|
mtk::widget_state(false, dlg.hover_cancel),
|
||||||
|
theme);
|
||||||
px_button(px, dw, dh, bx, btn_y, btn_w, btn_h, "Format", true, dlg.hover_format, true);
|
|
||||||
px_button(px, dw, dh, bx + btn_w + gap, btn_y, btn_w, btn_h, "Cancel", true, dlg.hover_cancel, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Render: new partition confirmation dialog
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
void render_newpart_window() {
|
void render_newpart_window() {
|
||||||
auto& dlg = g_state.np_dlg;
|
auto& dlg = g_state.np_dlg;
|
||||||
if (!dlg.open) return;
|
if (!dlg.open || !dlg.pixels) return;
|
||||||
|
|
||||||
uint32_t* px = dlg.pixels;
|
Canvas c(dlg.pixels, NP_DLG_W, NP_DLG_H);
|
||||||
int dw = NP_DLG_W, dh = NP_DLG_H;
|
mtk::Theme theme = disks_theme();
|
||||||
int fh = font_h();
|
c.fill(theme.window_bg);
|
||||||
|
|
||||||
px_fill(px, dw, dh, 0, 0, dw, dh, BG_COLOR);
|
int fh = system_font_height();
|
||||||
|
|
||||||
// Title
|
|
||||||
const char* title = dlg.will_init_gpt ? "Initialize Disk" : "New Partition";
|
const char* title = dlg.will_init_gpt ? "Initialize Disk" : "New Partition";
|
||||||
int tw = text_w(title);
|
draw_centered_text(c, NP_DLG_W, 12, title, theme.text);
|
||||||
px_text(px, dw, dh, (dw - tw) / 2, 12, title, TEXT_COLOR);
|
draw_centered_text(c, NP_DLG_W, 12 + fh + 6, dlg.disk_desc, theme.text_subtle);
|
||||||
|
|
||||||
// Disk description
|
Color warn_color = dlg.will_init_gpt ? theme.danger : theme.text;
|
||||||
int ddw = text_w(dlg.disk_desc);
|
int warn_y = 12 + fh * 2 + 20;
|
||||||
px_text(px, dw, dh, (dw - ddw) / 2, 12 + fh + 6, dlg.disk_desc, DIM_TEXT);
|
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);
|
||||||
// Warning lines
|
|
||||||
Color warn_color = dlg.will_init_gpt
|
|
||||||
? Color::from_rgb(0xCC, 0x22, 0x22)
|
|
||||||
: TEXT_COLOR;
|
|
||||||
|
|
||||||
int wy = 12 + fh * 2 + 20;
|
|
||||||
int w1w = text_w(dlg.warn_line1);
|
|
||||||
px_text(px, dw, dh, (dw - w1w) / 2, wy, dlg.warn_line1, warn_color);
|
|
||||||
int w2w = text_w(dlg.warn_line2);
|
|
||||||
px_text(px, dw, dh, (dw - w2w) / 2, wy + fh + 4, dlg.warn_line2, warn_color);
|
|
||||||
|
|
||||||
// Buttons
|
|
||||||
int btn_w = 90, btn_h = 30;
|
|
||||||
int btn_y = dh - btn_h - 16;
|
|
||||||
int gap = 16;
|
|
||||||
int total_w = btn_w * 2 + gap;
|
|
||||||
int bx = (dw - total_w) / 2;
|
|
||||||
|
|
||||||
const char* confirm_label = dlg.will_init_gpt ? "Initialize" : "Create";
|
const char* confirm_label = dlg.will_init_gpt ? "Initialize" : "Create";
|
||||||
px_button(px, dw, dh, bx, btn_y, btn_w, btn_h, confirm_label, true, dlg.hover_confirm, true);
|
mtk::ButtonVariant confirm_variant = dlg.will_init_gpt ? mtk::BUTTON_DANGER : mtk::BUTTON_PRIMARY;
|
||||||
px_button(px, dw, dh, bx + btn_w + gap, btn_y, btn_w, btn_h, "Cancel", true, dlg.hover_cancel, false);
|
mtk::draw_modal_actions(c,
|
||||||
|
dialog_primary_button_rect(NP_DLG_W, NP_DLG_H), confirm_label,
|
||||||
|
confirm_variant,
|
||||||
|
mtk::widget_state(false, dlg.hover_confirm),
|
||||||
|
dialog_secondary_button_rect(NP_DLG_W, NP_DLG_H), "Cancel",
|
||||||
|
mtk::widget_state(false, dlg.hover_cancel),
|
||||||
|
theme);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Top-level render
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
void render(uint32_t* pixels) {
|
void render(uint32_t* pixels) {
|
||||||
px_fill(pixels, g_win_w, g_win_h, 0, 0, g_win_w, g_win_h, BG_COLOR);
|
if (!pixels) return;
|
||||||
render_toolbar(pixels);
|
|
||||||
render_content(pixels);
|
Canvas c(pixels, g_win_w, g_win_h);
|
||||||
render_status(pixels);
|
mtk::Theme theme = disks_theme();
|
||||||
|
c.fill(theme.window_bg);
|
||||||
|
render_toolbar(c, theme);
|
||||||
|
render_content(c, theme);
|
||||||
|
render_status(c, theme);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user