feat: port weather to MTK toolkit
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include <gui/standalone.hpp>
|
||||
#include <gui/svg.hpp>
|
||||
#include <gui/truetype.hpp>
|
||||
#include <gui/mtk.hpp>
|
||||
#include <tls/tls.hpp>
|
||||
|
||||
extern "C" {
|
||||
@@ -304,47 +305,63 @@ static void do_fetch() {
|
||||
// Theme colors
|
||||
// ============================================================================
|
||||
|
||||
static constexpr Color CONTENT_BG = Color::from_rgb(0xFF, 0xFF, 0xFF); // white
|
||||
static constexpr Color FOOTER_BG = Color::from_rgb(0xF5, 0xF5, 0xF5); // light gray
|
||||
static constexpr Color DIVIDER = Color::from_rgb(0xCC, 0xCC, 0xCC); // subtle border
|
||||
static constexpr Color DARK_TEXT = Color::from_rgb(0x33, 0x33, 0x33);
|
||||
static constexpr Color MID_TEXT = Color::from_rgb(0x88, 0x88, 0x88);
|
||||
static constexpr Color HINT_TEXT = Color::from_rgb(0x99, 0x99, 0x99);
|
||||
static constexpr Color ERR_TEXT = Color::from_rgb(0xCC, 0x22, 0x22);
|
||||
static constexpr Color BTN_BG = Color::from_rgb(0x36, 0x7B, 0xF0); // accent blue
|
||||
static constexpr Color WHITE_TEXT = Color::from_rgb(0xFF, 0xFF, 0xFF);
|
||||
|
||||
static mtk::Theme weather_theme() {
|
||||
mtk::Theme theme = mtk::make_theme();
|
||||
theme.window_bg = WHITE;
|
||||
theme.surface = Color::from_rgb(0xF5, 0xF5, 0xF5);
|
||||
theme.surface_alt = Color::from_rgb(0xF8, 0xF8, 0xF8);
|
||||
theme.surface_hover = mtk::mix(theme.surface, theme.accent, 18);
|
||||
theme.border = Color::from_rgb(0xCC, 0xCC, 0xCC);
|
||||
theme.text = Color::from_rgb(0x33, 0x33, 0x33);
|
||||
theme.text_muted = Color::from_rgb(0x88, 0x88, 0x88);
|
||||
theme.text_subtle = Color::from_rgb(0x99, 0x99, 0x99);
|
||||
return theme;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Rendering
|
||||
// ============================================================================
|
||||
|
||||
static constexpr int BTN_W = 110;
|
||||
static constexpr int BTN_H = 28;
|
||||
static constexpr int WEATHER_BTN_RADIUS = 6;
|
||||
static constexpr int BTN_H = 30;
|
||||
|
||||
static int g_mouse_x = -1;
|
||||
static int g_mouse_y = -1;
|
||||
|
||||
static Rect refresh_button_rect() {
|
||||
int btn_x = (g_win_w - BTN_W) / 2;
|
||||
int btn_y = g_win_h - FOOTER_H + (FOOTER_H - BTN_H) / 2;
|
||||
return {btn_x, btn_y, BTN_W, BTN_H};
|
||||
}
|
||||
|
||||
static void render(Canvas& canvas) {
|
||||
mtk::Theme theme = weather_theme();
|
||||
Rect btn = refresh_button_rect();
|
||||
|
||||
// ── Background ───────────────────────────────────────────────────────────
|
||||
canvas.fill_rect(0, 0, g_win_w, g_win_h - FOOTER_H, CONTENT_BG);
|
||||
canvas.fill_rect(0, g_win_h - FOOTER_H, g_win_w, FOOTER_H, FOOTER_BG);
|
||||
canvas.fill_rect(0, 0, g_win_w, g_win_h - FOOTER_H, theme.window_bg);
|
||||
canvas.fill_rect(0, g_win_h - FOOTER_H, g_win_w, FOOTER_H, theme.surface);
|
||||
|
||||
// Divider between content and location strip
|
||||
canvas.hline(0, HEADER_H, g_win_w, DIVIDER);
|
||||
canvas.hline(0, HEADER_H, g_win_w, theme.border);
|
||||
// Divider above footer
|
||||
canvas.hline(0, g_win_h - FOOTER_H, g_win_w, DIVIDER);
|
||||
canvas.hline(0, g_win_h - FOOTER_H, g_win_w, theme.border);
|
||||
|
||||
if (!g_font) return;
|
||||
|
||||
// ── Main content area (y=0..HEADER_H) ────────────────────────────────────
|
||||
if (g_phase == AppPhase::LOADING) {
|
||||
draw_text(canvas, g_font, 20, HEADER_H / 2 - 9,
|
||||
"Fetching weather data...", HINT_TEXT, 18);
|
||||
"Fetching weather data...", theme.text_subtle, 18);
|
||||
|
||||
} else if (g_phase == AppPhase::ERR) {
|
||||
draw_text(canvas, g_font, 20, 20, g_status, ERR_TEXT, 15);
|
||||
|
||||
} else if (g_phase == AppPhase::IDLE) {
|
||||
draw_text(canvas, g_font, 20, HEADER_H / 2 - 9,
|
||||
"Click Refresh to check weather.", HINT_TEXT, 18);
|
||||
"Click Refresh to check weather.", theme.text_subtle, 18);
|
||||
|
||||
} else { // DONE
|
||||
// Weather icon
|
||||
@@ -352,31 +369,26 @@ static void render(Canvas& canvas) {
|
||||
|
||||
// Temperature (large bold)
|
||||
TrueTypeFont* temp_font = g_font_bold ? g_font_bold : g_font;
|
||||
draw_text(canvas, temp_font, INFO_X, TEMP_Y, g_temp, DARK_TEXT, TEMP_SIZE);
|
||||
draw_text(canvas, temp_font, INFO_X, TEMP_Y, g_temp, theme.text, TEMP_SIZE);
|
||||
|
||||
// Weather description
|
||||
draw_text(canvas, g_font, INFO_X, DESC_Y, g_desc, DARK_TEXT, DESC_SIZE);
|
||||
draw_text(canvas, g_font, INFO_X, DESC_Y, g_desc, theme.text, DESC_SIZE);
|
||||
|
||||
// Feels like
|
||||
draw_text(canvas, g_font, INFO_X, FEELS_Y, g_feels, MID_TEXT, LABEL_SIZE);
|
||||
draw_text(canvas, g_font, INFO_X, FEELS_Y, g_feels, theme.text_muted, LABEL_SIZE);
|
||||
}
|
||||
|
||||
// ── Location strip (y=HEADER_H..g_win_h-FOOTER_H) ────────────────────────
|
||||
if (g_phase == AppPhase::DONE) {
|
||||
draw_text(canvas, g_font, 20, HEADER_H + 14, g_location, DARK_TEXT, LABEL_SIZE);
|
||||
draw_text(canvas, g_font, 20, HEADER_H + 14, g_location, theme.text, LABEL_SIZE);
|
||||
}
|
||||
|
||||
// ── Refresh button (rounded, in footer) ───────────────────────────────────
|
||||
int btn_x = (g_win_w - BTN_W) / 2;
|
||||
int btn_y = g_win_h - FOOTER_H + (FOOTER_H - BTN_H) / 2;
|
||||
|
||||
if (g_phase == AppPhase::LOADING) {
|
||||
draw_button(canvas, g_font, btn_x, btn_y, BTN_W, BTN_H,
|
||||
"Loading...", BTN_BG, WHITE_TEXT, WEATHER_BTN_RADIUS, 14);
|
||||
} else {
|
||||
draw_button(canvas, g_font, btn_x, btn_y, BTN_W, BTN_H,
|
||||
"Refresh", BTN_BG, WHITE_TEXT, WEATHER_BTN_RADIUS, 15);
|
||||
}
|
||||
bool hovered = btn.contains(g_mouse_x, g_mouse_y);
|
||||
bool loading = (g_phase == AppPhase::LOADING);
|
||||
mtk::WidgetState state = mtk::widget_state(false, hovered, !loading);
|
||||
mtk::draw_button(canvas, btn, loading ? "Loading..." : "Refresh",
|
||||
mtk::BUTTON_PRIMARY, state, theme);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
@@ -384,6 +396,9 @@ static void render(Canvas& canvas) {
|
||||
// ============================================================================
|
||||
|
||||
extern "C" void _start() {
|
||||
if (!fonts::init())
|
||||
montauk::exit(1);
|
||||
|
||||
// Allocate response buffer from heap
|
||||
g_resp_buf = (char*)malloc(RESP_MAX + 1);
|
||||
if (!g_resp_buf) montauk::exit(1);
|
||||
@@ -406,12 +421,13 @@ extern "C" void _start() {
|
||||
if (!win.create("Weather", INIT_W, INIT_H))
|
||||
montauk::exit(1);
|
||||
|
||||
Canvas canvas = win.canvas();
|
||||
mtk::StandaloneHost host(&win);
|
||||
Canvas canvas = host.canvas();
|
||||
|
||||
// Initial fetch on startup
|
||||
g_phase = AppPhase::LOADING;
|
||||
render(canvas);
|
||||
win.present();
|
||||
host.present();
|
||||
do_fetch();
|
||||
|
||||
// Event loop
|
||||
@@ -423,9 +439,9 @@ extern "C" void _start() {
|
||||
|
||||
if (r == 0) {
|
||||
montauk::sleep_ms(16);
|
||||
canvas = win.canvas();
|
||||
canvas = host.canvas();
|
||||
render(canvas);
|
||||
win.present();
|
||||
host.present();
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -438,30 +454,32 @@ extern "C" void _start() {
|
||||
if (ev.type == 2) {
|
||||
g_win_w = win.width;
|
||||
g_win_h = win.height;
|
||||
canvas = win.canvas();
|
||||
canvas = host.canvas();
|
||||
}
|
||||
|
||||
if (ev.type == 1) {
|
||||
// Track mouse position for hover states
|
||||
g_mouse_x = ev.mouse.x;
|
||||
g_mouse_y = ev.mouse.y;
|
||||
|
||||
// Mouse — check for Refresh button click
|
||||
bool just_clicked = (ev.mouse.buttons & 1) && !(ev.mouse.prev_buttons & 1);
|
||||
if (just_clicked && g_phase != AppPhase::LOADING) {
|
||||
int btn_x = (g_win_w - BTN_W) / 2;
|
||||
int btn_y = g_win_h - FOOTER_H + (FOOTER_H - BTN_H) / 2;
|
||||
Rect btn = refresh_button_rect();
|
||||
int mx = ev.mouse.x, my = ev.mouse.y;
|
||||
if (mx >= btn_x && mx < btn_x + BTN_W &&
|
||||
my >= btn_y && my < btn_y + BTN_H) {
|
||||
if (btn.contains(mx, my)) {
|
||||
g_phase = AppPhase::LOADING;
|
||||
canvas = win.canvas();
|
||||
canvas = host.canvas();
|
||||
render(canvas);
|
||||
win.present();
|
||||
host.present();
|
||||
do_fetch();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canvas = win.canvas();
|
||||
canvas = host.canvas();
|
||||
render(canvas);
|
||||
win.present();
|
||||
host.present();
|
||||
}
|
||||
|
||||
if (g_icon.pixels) svg_free(g_icon);
|
||||
|
||||
Reference in New Issue
Block a user