feat: port Printers app to MTK toolkit

This commit is contained in:
2026-04-15 17:49:07 +02:00
parent a7499c57c9
commit ca954517e9
6 changed files with 179 additions and 159 deletions
View File
+171 -148
View File
@@ -10,6 +10,7 @@
#include <montauk/syscall.h> #include <montauk/syscall.h>
#include <gui/gui.hpp> #include <gui/gui.hpp>
#include <gui/canvas.hpp> #include <gui/canvas.hpp>
#include <gui/mtk.hpp>
#include <gui/svg.hpp> #include <gui/svg.hpp>
#include <gui/standalone.hpp> #include <gui/standalone.hpp>
#include <gui/truetype.hpp> #include <gui/truetype.hpp>
@@ -32,7 +33,6 @@ static constexpr int TAB_BAR_H = 36;
static constexpr int PAD_X = 16; static constexpr int PAD_X = 16;
static constexpr int PAD_Y = 20; static constexpr int PAD_Y = 20;
static constexpr int BTN_H = 30; static constexpr int BTN_H = 30;
static constexpr int ACTION_RADIUS = 4;
static constexpr int FIELD_H = 32; static constexpr int FIELD_H = 32;
static constexpr int PRINTER_ICON_SIZE = 24; static constexpr int PRINTER_ICON_SIZE = 24;
@@ -79,6 +79,10 @@ struct AppState {
char edit_uri[MAX_PATH_LEN]; char edit_uri[MAX_PATH_LEN];
int edit_len; int edit_len;
char edit_error[128]; char edit_error[128];
int mouse_x;
int mouse_y;
int config_mouse_x;
int config_mouse_y;
Color accent; Color accent;
}; };
@@ -94,6 +98,7 @@ struct Layout {
}; };
struct ConfigLayout { struct ConfigLayout {
int input_label_y;
Rect input; Rect input;
Rect save_btn; Rect save_btn;
Rect cancel_btn; Rect cancel_btn;
@@ -153,6 +158,13 @@ static void set_accent(Color accent) {
g_app.accent = accent; g_app.accent = accent;
} }
static mtk::Theme printers_theme() {
mtk::Theme theme = mtk::make_theme(g_app.accent);
theme.disabled_bg = Color::from_rgb(0xE6, 0xE6, 0xE6);
theme.disabled_fg = Color::from_rgb(0x9A, 0x9A, 0x9A);
return theme;
}
static void load_accent() { static void load_accent() {
set_accent(colors::ACCENT); set_accent(colors::ACCENT);
@@ -255,95 +267,39 @@ static void text_fit(const char* src, char* out, int out_len, int max_w) {
montauk::strncpy(out, ell, out_len - 1); montauk::strncpy(out, ell, out_len - 1);
} }
static void draw_action_btn(Canvas& c, const Rect& r, const char* label, static bool main_mouse_in_rect(const Rect& rect) {
bool enabled, bool primary) { return rect.contains(g_app.mouse_x, g_app.mouse_y);
Color bg;
Color fg;
if (!enabled) {
bg = Color::from_rgb(0xE6, 0xE6, 0xE6);
fg = Color::from_rgb(0x9A, 0x9A, 0x9A);
} else if (primary) {
bg = g_app.accent;
fg = colors::WHITE;
} else {
bg = colors::WINDOW_BG;
fg = colors::TEXT_COLOR;
}
c.fill_rounded_rect(r.x, r.y, r.w, r.h, ACTION_RADIUS, bg);
if (enabled && !primary) c.rect(r.x, r.y, r.w, r.h, colors::BORDER);
if (!enabled) c.rect(r.x, r.y, r.w, r.h, Color::from_rgb(0xD4, 0xD4, 0xD4));
int tw = text_width(label);
int fh = system_font_height();
c.text(r.x + (r.w - tw) / 2, r.y + (r.h - fh) / 2, label, fg);
} }
static void draw_chip(Canvas& c, int x, int y, const char* label, Color bg, Color fg = colors::WHITE) { static bool config_mouse_in_rect(const Rect& rect) {
int sfh = system_font_height(); return rect.contains(g_app.config_mouse_x, g_app.config_mouse_y);
int chip_h = sfh + 8;
int chip_w = text_width(label) + 18;
c.fill_rounded_rect(x, y, chip_w, chip_h, chip_h / 2, bg);
c.text(x + 9, y + (chip_h - sfh) / 2, label, fg);
} }
static int chip_height() { static mtk::WidgetState main_button_state(const Rect& rect, bool enabled, bool active = false) {
return system_font_height() + 8; return mtk::widget_state(active, main_mouse_in_rect(rect), enabled);
}
static mtk::WidgetState config_button_state(const Rect& rect, bool enabled, bool active = false) {
return mtk::widget_state(active, config_mouse_in_rect(rect), enabled);
} }
static int control_row_text_y(int row_y) { static int control_row_text_y(int row_y) {
return row_y + 6; return row_y + (BTN_H - system_font_height()) / 2;
} }
static int control_row_chip_y(int row_y) { static int control_row_badge_y(int row_y) {
return row_y + 2; return row_y + (BTN_H - mtk::badge_height()) / 2;
} }
static void draw_overview_stat_row(Canvas& c, int x, int y, int w, static void draw_overview_stat_row(Canvas& c, int x, int y, int w,
const char* label, int value) { const char* label, int value,
const mtk::Theme& theme) {
char value_buf[16]; char value_buf[16];
snprintf(value_buf, sizeof(value_buf), "%d", value); snprintf(value_buf, sizeof(value_buf), "%d", value);
c.text(x, y, label, colors::TEXT_COLOR); c.text(x, y, label, theme.text);
int value_w = text_width(value_buf); int value_w = text_width(value_buf);
c.text(x + w - value_w, y, value_buf, colors::TEXT_COLOR); c.text(x + w - value_w, y, value_buf, theme.text);
}
static void draw_tabs(Canvas& c) {
int sfh = system_font_height();
c.fill_rect(0, 0, g_win.width, TAB_BAR_H, Color::from_rgb(0xF5, 0xF5, 0xF5));
c.hline(0, TAB_BAR_H - 1, g_win.width, colors::BORDER);
int tab_w = g_win.width / TAB_COUNT;
for (int i = 0; i < TAB_COUNT; i++) {
int tx = i * tab_w;
bool active = (i == g_app.active_tab);
if (active) {
c.fill_rect(tx, 0, tab_w, TAB_BAR_H, colors::WINDOW_BG);
c.fill_rect(tx + 4, TAB_BAR_H - 3, tab_w - 8, 3, g_app.accent);
}
int tw = text_width(g_tab_labels[i]);
Color tc = active ? g_app.accent : Color::from_rgb(0x66, 0x66, 0x66);
c.text(tx + (tab_w - tw) / 2, (TAB_BAR_H - sfh) / 2, g_tab_labels[i], tc);
}
}
static void draw_input_field(Canvas& c, int x, int y, int w, int h,
const char* label, const char* value, bool focused) {
int sfh = system_font_height();
Color dim = Color::from_rgb(0x88, 0x88, 0x88);
c.text(x, y, label, dim);
y += sfh + 2;
c.fill_rounded_rect(x, y, w, h, 3, Color::from_rgb(0xF5, 0xF5, 0xF5));
c.rect(x, y, w, h, focused ? g_app.accent : colors::BORDER);
c.text(x + 8, y + (h - sfh) / 2, value, colors::TEXT_COLOR);
if (focused) {
int tw = text_width(value);
c.fill_rect(x + 8 + tw, y + 6, 2, h - 12, g_app.accent);
}
} }
static void compute_layout(Layout* lo) { static void compute_layout(Layout* lo) {
@@ -444,14 +400,19 @@ static void compute_layout(Layout* lo) {
} }
static Rect tab_bar_rect() {
return {0, 0, g_win.width, TAB_BAR_H};
}
static void compute_config_layout(ConfigLayout* lo) { static void compute_config_layout(ConfigLayout* lo) {
mtk::Theme theme = printers_theme();
int pad = 16; int pad = 16;
int sfh = system_font_height(); int sfh = system_font_height();
int field_w = g_config.width - pad * 2; int field_w = g_config.width - pad * 2;
if (field_w < 120) field_w = 120; if (field_w < 120) field_w = 120;
int input_box_y = 16 + sfh + 6 + sfh + 10 + sfh + 2; lo->input_label_y = 16 + sfh + 6 + sfh + 10;
lo->input = {pad, input_box_y, field_w, FIELD_H}; lo->input = mtk::labeled_text_input_rect(pad, lo->input_label_y, field_w, theme, FIELD_H);
lo->save_btn = {pad, g_config.height - BTN_H - 16, 80, BTN_H}; lo->save_btn = {pad, g_config.height - BTN_H - 16, 80, BTN_H};
lo->cancel_btn = {pad + 88, g_config.height - BTN_H - 16, 80, BTN_H}; lo->cancel_btn = {pad + 88, g_config.height - BTN_H - 16, 80, BTN_H};
} }
@@ -509,6 +470,8 @@ static void open_config_popup() {
g_app.edit_len = montauk::slen(g_app.edit_uri); g_app.edit_len = montauk::slen(g_app.edit_uri);
g_app.edit_error[0] = '\0'; g_app.edit_error[0] = '\0';
g_app.config_button_down = 0; g_app.config_button_down = 0;
g_app.config_mouse_x = -1;
g_app.config_mouse_y = -1;
if (!g_config.open) { if (!g_config.open) {
Montauk::WinCreateResult wres; Montauk::WinCreateResult wres;
@@ -554,48 +517,55 @@ static void render_config_popup() {
if (!g_app.config_open || !g_config.open || g_config.win_id < 0 || g_config.pixels == nullptr) return; if (!g_app.config_open || !g_config.open || g_config.win_id < 0 || g_config.pixels == nullptr) return;
Canvas c(g_config.pixels, g_config.width, g_config.height); Canvas c(g_config.pixels, g_config.width, g_config.height);
c.fill(colors::WINDOW_BG); mtk::Theme theme = printers_theme();
c.fill(theme.window_bg);
ConfigLayout lo = {}; ConfigLayout lo = {};
compute_config_layout(&lo); compute_config_layout(&lo);
int sfh = system_font_height(); int sfh = system_font_height();
int x = 16; int x = 16;
int y = 16; int y = 16;
c.text(x, y, "Default Printer", colors::TEXT_COLOR); c.text(x, y, "Default Printer", theme.text);
y += sfh + 6; y += sfh + 6;
c.text(x, y, "Enter an IPP or IPPS destination URI.", Color::from_rgb(0x88, 0x88, 0x88)); c.text(x, y, "Enter an IPP or IPPS destination URI.", theme.text_muted);
y += sfh + 10;
draw_input_field(c, lo.input.x, lo.input.y - (sfh + 2), mtk::draw_labeled_text_field(c, lo.input.x, lo.input_label_y, lo.input.w,
lo.input.w, lo.input.h, "Printer URI", "Printer URI", g_app.edit_uri, g_app.edit_len,
g_app.edit_uri, true); true, false, theme, lo.input.h);
y = lo.input.y + lo.input.h + 12; y = lo.input.y + lo.input.h + 12;
c.text(x, y, "Example: ipp://printer.local/ipp/print", Color::from_rgb(0x88, 0x88, 0x88)); c.text(x, y, "Example: ipp://printer.local/ipp/print", theme.text_muted);
y += sfh + 10; y += sfh + 10;
if (g_app.edit_error[0]) if (g_app.edit_error[0])
c.text(x, y, g_app.edit_error, Color::from_rgb(0xD0, 0x3E, 0x3E)); c.text(x, y, g_app.edit_error, theme.danger);
draw_action_btn(c, lo.save_btn, "Save", true, true); mtk::draw_modal_actions(c,
draw_action_btn(c, lo.cancel_btn, "Cancel", true, false); lo.save_btn, "Save",
mtk::BUTTON_PRIMARY,
config_button_state(lo.save_btn, true, g_app.config_button_down == 1),
lo.cancel_btn, "Cancel",
config_button_state(lo.cancel_btn, true, g_app.config_button_down == 2),
theme);
montauk::win_present(g_config.win_id); montauk::win_present(g_config.win_id);
} }
static bool handle_config_mouse(int mx, int my, uint8_t buttons, uint8_t prev_buttons) { static bool handle_config_mouse(int mx, int my, uint8_t buttons, uint8_t prev_buttons) {
ConfigLayout lo = {}; ConfigLayout lo = {};
compute_config_layout(&lo); compute_config_layout(&lo);
g_app.config_mouse_x = mx;
g_app.config_mouse_y = my;
bool left_pressed = (buttons & 1) && !(prev_buttons & 1); bool left_pressed = (buttons & 1) && !(prev_buttons & 1);
bool left_released = !(buttons & 1) && (prev_buttons & 1); bool left_released = !(buttons & 1) && (prev_buttons & 1);
if (left_pressed) { if (left_pressed) {
int button_down = 0;
if (lo.save_btn.contains(mx, my)) if (lo.save_btn.contains(mx, my))
g_app.config_button_down = 1; button_down = 1;
else if (lo.cancel_btn.contains(mx, my)) else if (lo.cancel_btn.contains(mx, my))
g_app.config_button_down = 2; button_down = 2;
else g_app.config_button_down = button_down;
g_app.config_button_down = 0;
return false; return false;
} }
@@ -642,10 +612,10 @@ static bool handle_config_key(const Montauk::KeyEvent& key) {
return false; return false;
} }
static void render_overview(Canvas& c, const Layout& lo) { static void render_overview(Canvas& c, const Layout& lo, const mtk::Theme& theme) {
Color dim = Color::from_rgb(0x88, 0x88, 0x88); Color dim = theme.text_muted;
Color success = Color::from_rgb(0x22, 0x88, 0x22); Color success = Color::from_rgb(0x22, 0x88, 0x22);
Color danger = Color::from_rgb(0xCC, 0x33, 0x33); Color danger = theme.danger;
Color quiet_bg = Color::from_rgb(0xE6, 0xE6, 0xE6); Color quiet_bg = Color::from_rgb(0xE6, 0xE6, 0xE6);
int sfh = system_font_height(); int sfh = system_font_height();
int x = PAD_X; int x = PAD_X;
@@ -655,13 +625,13 @@ static void render_overview(Canvas& c, const Layout& lo) {
char title[MAX_DEBUG_LEN]; char title[MAX_DEBUG_LEN];
if (status_visible()) { if (status_visible()) {
c.text(x, y, g_app.status_msg, g_app.accent); c.text(x, y, g_app.status_msg, theme.accent);
y += sfh + 12; y += sfh + 12;
} }
const char* chip_label = "Not Set"; const char* chip_label = "Not Set";
Color chip_bg = quiet_bg; Color chip_bg = quiet_bg;
Color chip_fg = colors::TEXT_COLOR; Color chip_fg = theme.text;
if (g_app.printer_uri[0] && g_app.probe_ok) { if (g_app.printer_uri[0] && g_app.probe_ok) {
chip_label = "Ready"; chip_label = "Ready";
chip_bg = success; chip_bg = success;
@@ -672,11 +642,11 @@ static void render_overview(Canvas& c, const Layout& lo) {
chip_fg = colors::WHITE; chip_fg = colors::WHITE;
} else if (g_app.printer_uri[0]) { } else if (g_app.printer_uri[0]) {
chip_label = "Checking"; chip_label = "Checking";
chip_bg = Color::from_rgb(0xE3, 0xE8, 0xEF); chip_bg = theme.accent_soft;
chip_fg = colors::TEXT_COLOR; chip_fg = theme.text;
} }
int chip_w = text_width(chip_label) + 18; int chip_w = mtk::badge_width(chip_label, theme);
int chip_x = lo.configure_btn.x - chip_w - 12; int chip_x = lo.configure_btn.x - chip_w - 12;
if (chip_x < x + 74) chip_x = x + 74; if (chip_x < x + 74) chip_x = x + 74;
@@ -696,10 +666,12 @@ static void render_overview(Canvas& c, const Layout& lo) {
if (lo.printer_icon.w > 0 && g_printer_icon.pixels) if (lo.printer_icon.w > 0 && g_printer_icon.pixels)
c.icon(lo.printer_icon.x, lo.printer_icon.y, g_printer_icon); c.icon(lo.printer_icon.x, lo.printer_icon.y, g_printer_icon);
c.text(title_x, control_row_text_y(y), title, colors::TEXT_COLOR); c.text(title_x, control_row_text_y(y), title, theme.text);
draw_chip(c, chip_x, control_row_chip_y(y), chip_label, chip_bg, chip_fg); mtk::draw_badge(c, chip_x, control_row_badge_y(y), chip_label, chip_bg, chip_fg, theme);
draw_action_btn(c, lo.configure_btn, "Configure", true, true); mtk::draw_button(c, lo.configure_btn, "Configure", mtk::BUTTON_PRIMARY,
draw_action_btn(c, lo.clear_btn, "Clear", g_app.printer_uri[0] != '\0', false); main_button_state(lo.configure_btn, true), theme);
mtk::draw_button(c, lo.clear_btn, "Clear", mtk::BUTTON_SECONDARY,
main_button_state(lo.clear_btn, g_app.printer_uri[0] != '\0'), theme);
y += BTN_H + 8; y += BTN_H + 8;
char line[MAX_DEBUG_LEN]; char line[MAX_DEBUG_LEN];
@@ -709,7 +681,7 @@ static void render_overview(Canvas& c, const Layout& lo) {
else else
safe_copy(line, sizeof(line), "No printer configured"); safe_copy(line, sizeof(line), "No printer configured");
text_fit(line, line, sizeof(line), w); text_fit(line, line, sizeof(line), w);
c.text(x, y, line, colors::TEXT_COLOR); c.text(x, y, line, theme.text);
y += sfh + 4; y += sfh + 4;
} }
@@ -737,7 +709,7 @@ static void render_overview(Canvas& c, const Layout& lo) {
c.text(x, y, "Waiting for the spooler to probe the configured printer.", dim); c.text(x, y, "Waiting for the spooler to probe the configured printer.", dim);
} }
y += sfh + 14; y += sfh + 14;
c.hline(x, y, w, colors::BORDER); mtk::draw_separator(c, x, y, w, theme);
y += 16; y += 16;
if (!g_app.printer_uri[0]) { if (!g_app.printer_uri[0]) {
@@ -745,23 +717,23 @@ static void render_overview(Canvas& c, const Layout& lo) {
return; return;
} }
c.text(x, y, "Queue", colors::TEXT_COLOR); c.text(x, y, "Queue", theme.text);
y += sfh + 8; y += sfh + 8;
int row_h = sfh + 10; int row_h = sfh + 10;
draw_overview_stat_row(c, x, y, w, "Waiting", g_app.queue_count); draw_overview_stat_row(c, x, y, w, "Waiting", g_app.queue_count, theme);
y += row_h; y += row_h;
c.hline(x, y - 5, w, Color::from_rgb(0xEB, 0xEB, 0xEB)); mtk::draw_separator(c, x, y - 5, w, theme);
draw_overview_stat_row(c, x, y, w, "Printing", g_app.active_count); draw_overview_stat_row(c, x, y, w, "Printing", g_app.active_count, theme);
y += row_h; y += row_h;
c.hline(x, y - 5, w, Color::from_rgb(0xEB, 0xEB, 0xEB)); mtk::draw_separator(c, x, y - 5, w, theme);
draw_overview_stat_row(c, x, y, w, "Completed", g_app.done_count); draw_overview_stat_row(c, x, y, w, "Completed", g_app.done_count, theme);
y += row_h; y += row_h;
c.hline(x, y - 5, w, Color::from_rgb(0xEB, 0xEB, 0xEB)); mtk::draw_separator(c, x, y - 5, w, theme);
draw_overview_stat_row(c, x, y, w, "Failed", g_app.failed_count); draw_overview_stat_row(c, x, y, w, "Failed", g_app.failed_count, theme);
y += row_h; y += row_h;
y += 2; y += 2;
@@ -791,21 +763,22 @@ static void render_overview(Canvas& c, const Layout& lo) {
} }
y += 10; y += 10;
c.hline(x, y, w, colors::BORDER); mtk::draw_separator(c, x, y, w, theme);
y += 16; y += 16;
c.text(x, y + 6, "Test Page", colors::TEXT_COLOR); c.text(x, control_row_text_y(y), "Test Page", theme.text);
draw_action_btn(c, lo.test_btn, "Print Test Page", true, true); mtk::draw_button(c, lo.test_btn, "Print Test Page", mtk::BUTTON_PRIMARY,
main_button_state(lo.test_btn, true), theme);
y += BTN_H + 8; y += BTN_H + 8;
c.text(x, y, "Queue a simple test page through the print spooler.", dim); c.text(x, y, "Queue a simple test page through the print spooler.", dim);
y += sfh + 4; y += sfh + 4;
c.text(x, y, "Uses the configured default printer.", colors::TEXT_COLOR); c.text(x, y, "Uses the configured default printer.", theme.text);
} }
static void render_details(Canvas& c, const Layout& lo) { static void render_details(Canvas& c, const Layout& lo, const mtk::Theme& theme) {
Color dim = Color::from_rgb(0x88, 0x88, 0x88); Color dim = theme.text_muted;
Color success = Color::from_rgb(0x22, 0x88, 0x22); Color success = Color::from_rgb(0x22, 0x88, 0x22);
Color danger = Color::from_rgb(0xCC, 0x33, 0x33); Color danger = theme.danger;
int sfh = system_font_height(); int sfh = system_font_height();
int x = PAD_X; int x = PAD_X;
int y = TAB_BAR_H + PAD_Y; int y = TAB_BAR_H + PAD_Y;
@@ -813,12 +786,13 @@ static void render_details(Canvas& c, const Layout& lo) {
char line[MAX_DEBUG_LEN]; char line[MAX_DEBUG_LEN];
if (status_visible()) { if (status_visible()) {
c.text(x, y, g_app.status_msg, g_app.accent); c.text(x, y, g_app.status_msg, theme.accent);
y += sfh + 12; y += sfh + 12;
} }
c.text(x, y + 6, "Connection", colors::TEXT_COLOR); c.text(x, control_row_text_y(y), "Connection", theme.text);
draw_action_btn(c, lo.probe_btn, "Probe", g_app.printer_uri[0] != '\0', false); mtk::draw_button(c, lo.probe_btn, "Probe", mtk::BUTTON_SECONDARY,
main_button_state(lo.probe_btn, g_app.printer_uri[0] != '\0'), theme);
y += BTN_H + 8; y += BTN_H + 8;
if (g_app.printer_uri[0]) { if (g_app.printer_uri[0]) {
@@ -826,7 +800,7 @@ static void render_details(Canvas& c, const Layout& lo) {
char err[128] = {}; char err[128] = {};
if (normalize_ipp_uri(g_app.printer_uri, &normalized, err, sizeof(err))) { if (normalize_ipp_uri(g_app.printer_uri, &normalized, err, sizeof(err))) {
text_fit(g_app.printer_uri, line, sizeof(line), w); text_fit(g_app.printer_uri, line, sizeof(line), w);
c.text(x, y, line, colors::TEXT_COLOR); c.text(x, y, line, theme.text);
y += sfh + 4; y += sfh + 4;
snprintf(line, sizeof(line), "Host: %s", normalized.host); snprintf(line, sizeof(line), "Host: %s", normalized.host);
@@ -877,7 +851,7 @@ static void render_details(Canvas& c, const Layout& lo) {
snprintf(line, sizeof(line), "Probe: %s", snprintf(line, sizeof(line), "Probe: %s",
g_app.probe_message[0] ? g_app.probe_message : (g_app.probe_ok ? "ok" : "failed")); g_app.probe_message[0] ? g_app.probe_message : (g_app.probe_ok ? "ok" : "failed"));
text_fit(line, line, sizeof(line), w); text_fit(line, line, sizeof(line), w);
c.text(x, y, line, g_app.probe_ok ? colors::TEXT_COLOR : danger); c.text(x, y, line, g_app.probe_ok ? theme.text : danger);
y += sfh + 4; y += sfh + 4;
if (g_app.probe_ok) { if (g_app.probe_ok) {
@@ -907,18 +881,22 @@ static void render_details(Canvas& c, const Layout& lo) {
} }
y += 10; y += 10;
c.hline(x, y, w, colors::BORDER); mtk::draw_separator(c, x, y, w, theme);
y += 16; y += 16;
c.text(x, control_row_text_y(y), "Spooler", colors::TEXT_COLOR); c.text(x, control_row_text_y(y), "Spooler", theme.text);
draw_action_btn(c, lo.start_btn, "Start Daemon", !g_app.daemon_running, false); mtk::draw_button(c, lo.start_btn, "Start Daemon", mtk::BUTTON_TONAL,
draw_action_btn(c, lo.refresh_btn, "Refresh", true, false); main_button_state(lo.start_btn, !g_app.daemon_running), theme);
mtk::draw_button(c, lo.refresh_btn, "Refresh", mtk::BUTTON_SECONDARY,
main_button_state(lo.refresh_btn, true), theme);
const char* daemon_label = g_app.daemon_running ? "Running" : "Stopped"; const char* daemon_label = g_app.daemon_running ? "Running" : "Stopped";
int chip_w = text_width(daemon_label) + 18; int chip_w = mtk::badge_width(daemon_label, theme);
int chip_x = lo.start_btn.x - chip_w - 12; int chip_x = lo.start_btn.x - chip_w - 12;
if (chip_x < x + 70) chip_x = x + 70; if (chip_x < x + 70) chip_x = x + 70;
draw_chip(c, chip_x, control_row_chip_y(y), daemon_label, g_app.daemon_running ? success : danger); mtk::draw_badge(c, chip_x, control_row_badge_y(y), daemon_label,
g_app.daemon_running ? success : danger,
colors::WHITE, theme);
y += BTN_H + 8; y += BTN_H + 8;
if (g_app.daemon_running) if (g_app.daemon_running)
@@ -929,10 +907,10 @@ static void render_details(Canvas& c, const Layout& lo) {
y += sfh + 6; y += sfh + 6;
snprintf(line, sizeof(line), "Queued: %d Printing: %d", g_app.queue_count, g_app.active_count); snprintf(line, sizeof(line), "Queued: %d Printing: %d", g_app.queue_count, g_app.active_count);
c.text(x, y, line, colors::TEXT_COLOR); c.text(x, y, line, theme.text);
y += sfh + 4; y += sfh + 4;
snprintf(line, sizeof(line), "Completed: %d Failed: %d", g_app.done_count, g_app.failed_count); snprintf(line, sizeof(line), "Completed: %d Failed: %d", g_app.done_count, g_app.failed_count);
c.text(x, y, line, colors::TEXT_COLOR); c.text(x, y, line, theme.text);
y += sfh + 4; y += sfh + 4;
if (g_app.has_active) { if (g_app.has_active) {
@@ -967,22 +945,24 @@ static void render_details(Canvas& c, const Layout& lo) {
static void render() { static void render() {
refresh_state(); refresh_state();
Canvas c = g_win.canvas(); mtk::StandaloneHost host(&g_win);
c.fill(colors::WINDOW_BG); Canvas c = host.canvas();
mtk::Theme theme = printers_theme();
c.fill(theme.window_bg);
Layout lo = {}; Layout lo = {};
compute_layout(&lo); compute_layout(&lo);
draw_tabs(c); mtk::draw_tab_bar(c, tab_bar_rect(), g_tab_labels, TAB_COUNT, g_app.active_tab, theme);
switch (g_app.active_tab) { switch (g_app.active_tab) {
case TAB_OVERVIEW: case TAB_OVERVIEW:
render_overview(c, lo); render_overview(c, lo, theme);
break; break;
case TAB_DETAILS: case TAB_DETAILS:
render_details(c, lo); render_details(c, lo, theme);
break; break;
} }
g_win.present(); host.present();
} }
static void handle_click(int mx, int my) { static void handle_click(int mx, int my) {
@@ -992,10 +972,9 @@ static void handle_click(int mx, int my) {
if (g_app.config_open) if (g_app.config_open)
return; return;
if (my >= 0 && my < TAB_BAR_H) { int tab = mtk::hit_tab_bar(tab_bar_rect(), TAB_COUNT, mx, my);
int tab_w = g_win.width / TAB_COUNT; if (tab >= 0) {
int tab = mx / tab_w; g_app.active_tab = tab;
if (tab >= 0 && tab < TAB_COUNT) g_app.active_tab = tab;
return; return;
} }
@@ -1048,6 +1027,49 @@ static void handle_click(int mx, int my) {
} }
} }
static bool hover_changed(const Rect& rect, bool enabled,
int prev_x, int prev_y, int next_x, int next_y) {
if (!enabled) return false;
return rect.contains(prev_x, prev_y) != rect.contains(next_x, next_y);
}
static bool handle_mouse(const Montauk::WinEvent& ev) {
if (g_app.config_open)
return false;
Layout lo = {};
compute_layout(&lo);
int prev_x = g_app.mouse_x;
int prev_y = g_app.mouse_y;
g_app.mouse_x = ev.mouse.x;
g_app.mouse_y = ev.mouse.y;
bool redraw = false;
if (g_app.active_tab == TAB_OVERVIEW) {
redraw |= hover_changed(lo.configure_btn, true, prev_x, prev_y, g_app.mouse_x, g_app.mouse_y);
redraw |= hover_changed(lo.clear_btn, g_app.printer_uri[0] != '\0',
prev_x, prev_y, g_app.mouse_x, g_app.mouse_y);
redraw |= hover_changed(lo.test_btn, g_app.printer_uri[0] != '\0',
prev_x, prev_y, g_app.mouse_x, g_app.mouse_y);
} else {
redraw |= hover_changed(lo.probe_btn, g_app.printer_uri[0] != '\0',
prev_x, prev_y, g_app.mouse_x, g_app.mouse_y);
redraw |= hover_changed(lo.start_btn, !g_app.daemon_running,
prev_x, prev_y, g_app.mouse_x, g_app.mouse_y);
redraw |= hover_changed(lo.refresh_btn, true,
prev_x, prev_y, g_app.mouse_x, g_app.mouse_y);
}
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
if (clicked) {
handle_click(ev.mouse.x, ev.mouse.y);
return true;
}
return redraw;
}
static void cycle_tab(int delta) { static void cycle_tab(int delta) {
int next = g_app.active_tab + delta; int next = g_app.active_tab + delta;
while (next < 0) next += TAB_COUNT; while (next < 0) next += TAB_COUNT;
@@ -1105,7 +1127,12 @@ extern "C" void _start() {
if (!fonts::init()) if (!fonts::init())
montauk::exit(1); montauk::exit(1);
montauk::memset(&g_app, 0, sizeof(g_app));
g_app.active_tab = TAB_OVERVIEW; g_app.active_tab = TAB_OVERVIEW;
g_app.mouse_x = -1;
g_app.mouse_y = -1;
g_app.config_mouse_x = -1;
g_app.config_mouse_y = -1;
load_accent(); load_accent();
load_icons(); load_icons();
refresh_state(true); refresh_state(true);
@@ -1176,11 +1203,7 @@ extern "C" void _start() {
handle_key(ev.key); handle_key(ev.key);
redraw_main = true; redraw_main = true;
} else if (ev.type == 1) { } else if (ev.type == 1) {
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1); redraw_main |= handle_mouse(ev);
if (clicked) {
handle_click(ev.mouse.x, ev.mouse.y);
redraw_main = true;
}
} }
if (redraw_main) if (redraw_main)
+4
View File
@@ -24,6 +24,7 @@ SvgIcon g_icon_list_bullet = {};
SvgIcon g_icon_list_number = {}; SvgIcon g_icon_list_number = {};
SvgIcon g_icon_indent_less = {}; SvgIcon g_icon_indent_less = {};
SvgIcon g_icon_indent_more = {}; SvgIcon g_icon_indent_more = {};
SvgIcon g_icon_page_view = {};
TrueTypeFont* g_ui_font = nullptr; TrueTypeFont* g_ui_font = nullptr;
TrueTypeFont* g_ui_bold = nullptr; TrueTypeFont* g_ui_bold = nullptr;
@@ -58,6 +59,8 @@ void wp_load_icons() {
g_icon_indent_less = svg_load("0:/icons/format-indent-less-symbolic.svg", 16, 16, def_color); g_icon_indent_less = svg_load("0:/icons/format-indent-less-symbolic.svg", 16, 16, def_color);
if (!g_icon_indent_more.pixels) if (!g_icon_indent_more.pixels)
g_icon_indent_more = svg_load("0:/icons/format-indent-more-symbolic.svg", 16, 16, def_color); g_icon_indent_more = svg_load("0:/icons/format-indent-more-symbolic.svg", 16, 16, def_color);
if (!g_icon_page_view.pixels)
g_icon_page_view = svg_load("0:/icons/view-paged-symbolic.svg", 16, 16, def_color);
} }
void wp_cleanup_state() { void wp_cleanup_state() {
@@ -75,6 +78,7 @@ void wp_cleanup_state() {
if (g_icon_list_number.pixels) svg_free(g_icon_list_number); if (g_icon_list_number.pixels) svg_free(g_icon_list_number);
if (g_icon_indent_less.pixels) svg_free(g_icon_indent_less); if (g_icon_indent_less.pixels) svg_free(g_icon_indent_less);
if (g_icon_indent_more.pixels) svg_free(g_icon_indent_more); if (g_icon_indent_more.pixels) svg_free(g_icon_indent_more);
if (g_icon_page_view.pixels) svg_free(g_icon_page_view);
} }
extern "C" void _start() { extern "C" void _start() {
+2 -11
View File
@@ -297,17 +297,8 @@ void wp_render() {
Color pageview_bg = wp->page_view_mode ? btn_active : btn_bg; Color pageview_bg = wp->page_view_mode ? btn_active : btn_bg;
c.fill_rounded_rect(WP_BTN_PAGEVIEW_X, 6, 24, 24, 3, pageview_bg); c.fill_rounded_rect(WP_BTN_PAGEVIEW_X, 6, 24, 24, 3, pageview_bg);
{ wp_draw_ui_icon_button(c, WP_BTN_PAGEVIEW_X, 6, 24, 24, g_icon_page_view, "\xA7",
// Draw a small page icon: rectangle with lines pageview_bg, colors::TEXT_COLOR);
int px = WP_BTN_PAGEVIEW_X + 4;
int py = 9;
// Page outline
c.rect(px, py, 14, 18, colors::TEXT_COLOR);
// Lines inside page
c.hline(px + 2, py + 4, 10, colors::TEXT_COLOR);
c.hline(px + 2, py + 8, 10, colors::TEXT_COLOR);
c.hline(px + 2, py + 12, 6, colors::TEXT_COLOR);
}
c.hline(0, WP_TOOLBAR_H - 1, c.w, colors::BORDER); c.hline(0, WP_TOOLBAR_H - 1, c.w, colors::BORDER);
@@ -396,6 +396,7 @@ extern SvgIcon g_icon_list_bullet;
extern SvgIcon g_icon_list_number; extern SvgIcon g_icon_list_number;
extern SvgIcon g_icon_indent_less; extern SvgIcon g_icon_indent_less;
extern SvgIcon g_icon_indent_more; extern SvgIcon g_icon_indent_more;
extern SvgIcon g_icon_page_view;
extern TrueTypeFont* g_ui_font; extern TrueTypeFont* g_ui_font;
extern TrueTypeFont* g_ui_bold; extern TrueTypeFont* g_ui_bold;
+1
View File
@@ -127,6 +127,7 @@ ICONS=(
"apps/scalable/sleep.svg" "apps/scalable/sleep.svg"
"apps/scalable/kolourpaint.svg" # paint app "apps/scalable/kolourpaint.svg" # paint app
"panel/sensors-temperature-symbolic.svg" # temperature panel icon "panel/sensors-temperature-symbolic.svg" # temperature panel icon
"actions/symbolic/view-paged-symbolic.svg" # page view toggle (word processor)
) )
copied=0 copied=0