feat: port Printers app to MTK toolkit
This commit is contained in:
+171
-148
@@ -10,6 +10,7 @@
|
||||
#include <montauk/syscall.h>
|
||||
#include <gui/gui.hpp>
|
||||
#include <gui/canvas.hpp>
|
||||
#include <gui/mtk.hpp>
|
||||
#include <gui/svg.hpp>
|
||||
#include <gui/standalone.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_Y = 20;
|
||||
static constexpr int BTN_H = 30;
|
||||
static constexpr int ACTION_RADIUS = 4;
|
||||
static constexpr int FIELD_H = 32;
|
||||
static constexpr int PRINTER_ICON_SIZE = 24;
|
||||
|
||||
@@ -79,6 +79,10 @@ struct AppState {
|
||||
char edit_uri[MAX_PATH_LEN];
|
||||
int edit_len;
|
||||
char edit_error[128];
|
||||
int mouse_x;
|
||||
int mouse_y;
|
||||
int config_mouse_x;
|
||||
int config_mouse_y;
|
||||
|
||||
Color accent;
|
||||
};
|
||||
@@ -94,6 +98,7 @@ struct Layout {
|
||||
};
|
||||
|
||||
struct ConfigLayout {
|
||||
int input_label_y;
|
||||
Rect input;
|
||||
Rect save_btn;
|
||||
Rect cancel_btn;
|
||||
@@ -153,6 +158,13 @@ static void set_accent(Color 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() {
|
||||
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);
|
||||
}
|
||||
|
||||
static void draw_action_btn(Canvas& c, const Rect& r, const char* label,
|
||||
bool enabled, bool primary) {
|
||||
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 bool main_mouse_in_rect(const Rect& rect) {
|
||||
return rect.contains(g_app.mouse_x, g_app.mouse_y);
|
||||
}
|
||||
|
||||
static void draw_chip(Canvas& c, int x, int y, const char* label, Color bg, Color fg = colors::WHITE) {
|
||||
int sfh = system_font_height();
|
||||
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 bool config_mouse_in_rect(const Rect& rect) {
|
||||
return rect.contains(g_app.config_mouse_x, g_app.config_mouse_y);
|
||||
}
|
||||
|
||||
static int chip_height() {
|
||||
return system_font_height() + 8;
|
||||
static mtk::WidgetState main_button_state(const Rect& rect, bool enabled, bool active = false) {
|
||||
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) {
|
||||
return row_y + 6;
|
||||
return row_y + (BTN_H - system_font_height()) / 2;
|
||||
}
|
||||
|
||||
static int control_row_chip_y(int row_y) {
|
||||
return row_y + 2;
|
||||
static int control_row_badge_y(int row_y) {
|
||||
return row_y + (BTN_H - mtk::badge_height()) / 2;
|
||||
}
|
||||
|
||||
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];
|
||||
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);
|
||||
c.text(x + w - value_w, y, value_buf, colors::TEXT_COLOR);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
c.text(x + w - value_w, y, value_buf, theme.text);
|
||||
}
|
||||
|
||||
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) {
|
||||
mtk::Theme theme = printers_theme();
|
||||
int pad = 16;
|
||||
int sfh = system_font_height();
|
||||
int field_w = g_config.width - pad * 2;
|
||||
if (field_w < 120) field_w = 120;
|
||||
|
||||
int input_box_y = 16 + sfh + 6 + sfh + 10 + sfh + 2;
|
||||
lo->input = {pad, input_box_y, field_w, FIELD_H};
|
||||
lo->input_label_y = 16 + sfh + 6 + sfh + 10;
|
||||
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->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_error[0] = '\0';
|
||||
g_app.config_button_down = 0;
|
||||
g_app.config_mouse_x = -1;
|
||||
g_app.config_mouse_y = -1;
|
||||
|
||||
if (!g_config.open) {
|
||||
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;
|
||||
|
||||
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 = {};
|
||||
compute_config_layout(&lo);
|
||||
int sfh = system_font_height();
|
||||
int x = 16;
|
||||
int y = 16;
|
||||
c.text(x, y, "Default Printer", colors::TEXT_COLOR);
|
||||
c.text(x, y, "Default Printer", theme.text);
|
||||
y += sfh + 6;
|
||||
c.text(x, y, "Enter an IPP or IPPS destination URI.", Color::from_rgb(0x88, 0x88, 0x88));
|
||||
y += sfh + 10;
|
||||
c.text(x, y, "Enter an IPP or IPPS destination URI.", theme.text_muted);
|
||||
|
||||
draw_input_field(c, lo.input.x, lo.input.y - (sfh + 2),
|
||||
lo.input.w, lo.input.h, "Printer URI",
|
||||
g_app.edit_uri, true);
|
||||
mtk::draw_labeled_text_field(c, lo.input.x, lo.input_label_y, lo.input.w,
|
||||
"Printer URI", g_app.edit_uri, g_app.edit_len,
|
||||
true, false, theme, lo.input.h);
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
draw_action_btn(c, lo.cancel_btn, "Cancel", true, false);
|
||||
mtk::draw_modal_actions(c,
|
||||
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);
|
||||
}
|
||||
|
||||
static bool handle_config_mouse(int mx, int my, uint8_t buttons, uint8_t prev_buttons) {
|
||||
ConfigLayout 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_released = !(buttons & 1) && (prev_buttons & 1);
|
||||
|
||||
if (left_pressed) {
|
||||
int button_down = 0;
|
||||
if (lo.save_btn.contains(mx, my))
|
||||
g_app.config_button_down = 1;
|
||||
button_down = 1;
|
||||
else if (lo.cancel_btn.contains(mx, my))
|
||||
g_app.config_button_down = 2;
|
||||
else
|
||||
g_app.config_button_down = 0;
|
||||
button_down = 2;
|
||||
g_app.config_button_down = button_down;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -642,10 +612,10 @@ static bool handle_config_key(const Montauk::KeyEvent& key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static void render_overview(Canvas& c, const Layout& lo) {
|
||||
Color dim = Color::from_rgb(0x88, 0x88, 0x88);
|
||||
static void render_overview(Canvas& c, const Layout& lo, const mtk::Theme& theme) {
|
||||
Color dim = theme.text_muted;
|
||||
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);
|
||||
int sfh = system_font_height();
|
||||
int x = PAD_X;
|
||||
@@ -655,13 +625,13 @@ static void render_overview(Canvas& c, const Layout& lo) {
|
||||
char title[MAX_DEBUG_LEN];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const char* chip_label = "Not Set";
|
||||
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) {
|
||||
chip_label = "Ready";
|
||||
chip_bg = success;
|
||||
@@ -672,11 +642,11 @@ static void render_overview(Canvas& c, const Layout& lo) {
|
||||
chip_fg = colors::WHITE;
|
||||
} else if (g_app.printer_uri[0]) {
|
||||
chip_label = "Checking";
|
||||
chip_bg = Color::from_rgb(0xE3, 0xE8, 0xEF);
|
||||
chip_fg = colors::TEXT_COLOR;
|
||||
chip_bg = theme.accent_soft;
|
||||
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;
|
||||
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)
|
||||
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);
|
||||
draw_chip(c, chip_x, control_row_chip_y(y), chip_label, chip_bg, chip_fg);
|
||||
draw_action_btn(c, lo.configure_btn, "Configure", true, true);
|
||||
draw_action_btn(c, lo.clear_btn, "Clear", g_app.printer_uri[0] != '\0', false);
|
||||
c.text(title_x, control_row_text_y(y), title, theme.text);
|
||||
mtk::draw_badge(c, chip_x, control_row_badge_y(y), chip_label, chip_bg, chip_fg, theme);
|
||||
mtk::draw_button(c, lo.configure_btn, "Configure", mtk::BUTTON_PRIMARY,
|
||||
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;
|
||||
|
||||
char line[MAX_DEBUG_LEN];
|
||||
@@ -709,7 +681,7 @@ static void render_overview(Canvas& c, const Layout& lo) {
|
||||
else
|
||||
safe_copy(line, sizeof(line), "No printer configured");
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
y += sfh + 14;
|
||||
c.hline(x, y, w, colors::BORDER);
|
||||
mtk::draw_separator(c, x, y, w, theme);
|
||||
y += 16;
|
||||
|
||||
if (!g_app.printer_uri[0]) {
|
||||
@@ -745,23 +717,23 @@ static void render_overview(Canvas& c, const Layout& lo) {
|
||||
return;
|
||||
}
|
||||
|
||||
c.text(x, y, "Queue", colors::TEXT_COLOR);
|
||||
c.text(x, y, "Queue", theme.text);
|
||||
y += sfh + 8;
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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 += 2;
|
||||
|
||||
@@ -791,21 +763,22 @@ static void render_overview(Canvas& c, const Layout& lo) {
|
||||
}
|
||||
|
||||
y += 10;
|
||||
c.hline(x, y, w, colors::BORDER);
|
||||
mtk::draw_separator(c, x, y, w, theme);
|
||||
y += 16;
|
||||
|
||||
c.text(x, y + 6, "Test Page", colors::TEXT_COLOR);
|
||||
draw_action_btn(c, lo.test_btn, "Print Test Page", true, true);
|
||||
c.text(x, control_row_text_y(y), "Test Page", theme.text);
|
||||
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;
|
||||
c.text(x, y, "Queue a simple test page through the print spooler.", dim);
|
||||
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) {
|
||||
Color dim = Color::from_rgb(0x88, 0x88, 0x88);
|
||||
static void render_details(Canvas& c, const Layout& lo, const mtk::Theme& theme) {
|
||||
Color dim = theme.text_muted;
|
||||
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 x = PAD_X;
|
||||
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];
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
c.text(x, y + 6, "Connection", colors::TEXT_COLOR);
|
||||
draw_action_btn(c, lo.probe_btn, "Probe", g_app.printer_uri[0] != '\0', false);
|
||||
c.text(x, control_row_text_y(y), "Connection", theme.text);
|
||||
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;
|
||||
|
||||
if (g_app.printer_uri[0]) {
|
||||
@@ -826,7 +800,7 @@ static void render_details(Canvas& c, const Layout& lo) {
|
||||
char err[128] = {};
|
||||
if (normalize_ipp_uri(g_app.printer_uri, &normalized, err, sizeof(err))) {
|
||||
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;
|
||||
|
||||
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",
|
||||
g_app.probe_message[0] ? g_app.probe_message : (g_app.probe_ok ? "ok" : "failed"));
|
||||
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;
|
||||
|
||||
if (g_app.probe_ok) {
|
||||
@@ -907,18 +881,22 @@ static void render_details(Canvas& c, const Layout& lo) {
|
||||
}
|
||||
|
||||
y += 10;
|
||||
c.hline(x, y, w, colors::BORDER);
|
||||
mtk::draw_separator(c, x, y, w, theme);
|
||||
y += 16;
|
||||
|
||||
c.text(x, control_row_text_y(y), "Spooler", colors::TEXT_COLOR);
|
||||
draw_action_btn(c, lo.start_btn, "Start Daemon", !g_app.daemon_running, false);
|
||||
draw_action_btn(c, lo.refresh_btn, "Refresh", true, false);
|
||||
c.text(x, control_row_text_y(y), "Spooler", theme.text);
|
||||
mtk::draw_button(c, lo.start_btn, "Start Daemon", mtk::BUTTON_TONAL,
|
||||
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";
|
||||
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;
|
||||
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;
|
||||
|
||||
if (g_app.daemon_running)
|
||||
@@ -929,10 +907,10 @@ static void render_details(Canvas& c, const Layout& lo) {
|
||||
y += sfh + 6;
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
if (g_app.has_active) {
|
||||
@@ -967,22 +945,24 @@ static void render_details(Canvas& c, const Layout& lo) {
|
||||
static void render() {
|
||||
refresh_state();
|
||||
|
||||
Canvas c = g_win.canvas();
|
||||
c.fill(colors::WINDOW_BG);
|
||||
mtk::StandaloneHost host(&g_win);
|
||||
Canvas c = host.canvas();
|
||||
mtk::Theme theme = printers_theme();
|
||||
c.fill(theme.window_bg);
|
||||
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) {
|
||||
case TAB_OVERVIEW:
|
||||
render_overview(c, lo);
|
||||
render_overview(c, lo, theme);
|
||||
break;
|
||||
case TAB_DETAILS:
|
||||
render_details(c, lo);
|
||||
render_details(c, lo, theme);
|
||||
break;
|
||||
}
|
||||
|
||||
g_win.present();
|
||||
host.present();
|
||||
}
|
||||
|
||||
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)
|
||||
return;
|
||||
|
||||
if (my >= 0 && my < TAB_BAR_H) {
|
||||
int tab_w = g_win.width / TAB_COUNT;
|
||||
int tab = mx / tab_w;
|
||||
if (tab >= 0 && tab < TAB_COUNT) g_app.active_tab = tab;
|
||||
int tab = mtk::hit_tab_bar(tab_bar_rect(), TAB_COUNT, mx, my);
|
||||
if (tab >= 0) {
|
||||
g_app.active_tab = tab;
|
||||
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) {
|
||||
int next = g_app.active_tab + delta;
|
||||
while (next < 0) next += TAB_COUNT;
|
||||
@@ -1105,7 +1127,12 @@ extern "C" void _start() {
|
||||
if (!fonts::init())
|
||||
montauk::exit(1);
|
||||
|
||||
montauk::memset(&g_app, 0, sizeof(g_app));
|
||||
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_icons();
|
||||
refresh_state(true);
|
||||
@@ -1176,11 +1203,7 @@ extern "C" void _start() {
|
||||
handle_key(ev.key);
|
||||
redraw_main = true;
|
||||
} else if (ev.type == 1) {
|
||||
bool clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
|
||||
if (clicked) {
|
||||
handle_click(ev.mouse.x, ev.mouse.y);
|
||||
redraw_main = true;
|
||||
}
|
||||
redraw_main |= handle_mouse(ev);
|
||||
}
|
||||
|
||||
if (redraw_main)
|
||||
|
||||
@@ -24,6 +24,7 @@ SvgIcon g_icon_list_bullet = {};
|
||||
SvgIcon g_icon_list_number = {};
|
||||
SvgIcon g_icon_indent_less = {};
|
||||
SvgIcon g_icon_indent_more = {};
|
||||
SvgIcon g_icon_page_view = {};
|
||||
TrueTypeFont* g_ui_font = 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);
|
||||
if (!g_icon_indent_more.pixels)
|
||||
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() {
|
||||
@@ -75,6 +78,7 @@ void wp_cleanup_state() {
|
||||
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_more.pixels) svg_free(g_icon_indent_more);
|
||||
if (g_icon_page_view.pixels) svg_free(g_icon_page_view);
|
||||
}
|
||||
|
||||
extern "C" void _start() {
|
||||
|
||||
@@ -297,17 +297,8 @@ void wp_render() {
|
||||
|
||||
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);
|
||||
{
|
||||
// Draw a small page icon: rectangle with lines
|
||||
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);
|
||||
}
|
||||
wp_draw_ui_icon_button(c, WP_BTN_PAGEVIEW_X, 6, 24, 24, g_icon_page_view, "\xA7",
|
||||
pageview_bg, colors::TEXT_COLOR);
|
||||
|
||||
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_indent_less;
|
||||
extern SvgIcon g_icon_indent_more;
|
||||
extern SvgIcon g_icon_page_view;
|
||||
extern TrueTypeFont* g_ui_font;
|
||||
extern TrueTypeFont* g_ui_bold;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user