fix: native Copy button and copy confirmation in charmap

The hand-rolled Copy button (bold, custom-shaded accent fill) and the floating
accent "toast" pill both looked out of place. Replace the button with the
canonical mtk::draw_button (BUTTON_PRIMARY), matching the Refresh button in
Devices and buttons elsewhere. Replace the floating pill with a plain
"Copied to clipboard" status line in the footer, right-aligned beside the Copy
button, that fades after ~1.6s — no overlay drawn over the character grid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-06 21:18:44 +02:00
parent f8327c6b8c
commit 55867c841a
+20 -36
View File
@@ -205,14 +205,13 @@ static constexpr int GRID_PAD = 12;
static constexpr int CELL_GAP = 6;
static constexpr int CELL_TARGET = 54; // desired tile edge in px
static constexpr int TAB_SIZE = 16;
static constexpr int CELL_SIZE = 26; // glyph font size inside a tile
static constexpr int PREVIEW_SIZE = 40;
static constexpr int NAME_SIZE = 16;
static constexpr int META_SIZE = 14;
static constexpr int TOAST_SIZE = 14;
static constexpr int STATUS_SIZE = 14;
static constexpr uint64_t TOAST_MS = 1400;
static constexpr uint64_t COPIED_MS = 1600;
// ==== App state ====
@@ -225,8 +224,7 @@ static int g_scroll = 0; // vertical scroll offset in px
static bool g_sb_hover = false; // pointer over the scrollbar track
static bool g_sb_dragging = false; // dragging the scrollbar thumb
static int g_sb_drag_offset = 0; // grab point within the thumb
static uint64_t g_toast_until = 0;
static char g_toast[48] = {};
static uint64_t g_copied_until = 0; // show the "copied" status until this time
static mtk::Theme g_theme;
static const char* g_tab_labels[SET_COUNT] = {}; // populated from SETS in _start
@@ -336,16 +334,7 @@ static void copy_selected() {
char s[2];
glyph_str(g.byte, s);
gui::clipboard_set_text(s, 1);
// Toast: Copied "<glyph>"
int n = 0;
const char* pfx = "Copied ";
while (pfx[n]) { g_toast[n] = pfx[n]; n++; }
g_toast[n++] = '"';
g_toast[n++] = (char)g.byte;
g_toast[n++] = '"';
g_toast[n] = '\0';
g_toast_until = montauk::get_milliseconds() + TOAST_MS;
g_copied_until = montauk::get_milliseconds() + COPIED_MS;
}
// ==== Rendering ====
@@ -432,25 +421,20 @@ static void render(Canvas& c) {
draw_text(c, fonts::system_font, text_x, L.footer.y + 16 + NAME_SIZE + 8,
meta, g_theme.text_subtle, META_SIZE);
// Copy button.
Color btn_bg = g_theme.accent;
if (g_copy_pressed) btn_bg = g_theme.accent_hover;
else if (g_copy_hover) btn_bg = mtk::darken(g_theme.accent, 16);
draw_button(c, title_font, L.copy_btn.x, L.copy_btn.y, L.copy_btn.w,
L.copy_btn.h, "Copy", btn_bg, g_theme.accent_fg,
g_theme.radius_md, TAB_SIZE);
// ---- Toast (drawn last; floats over the lower-right of the grid) ----
if (g_toast[0] && montauk::get_milliseconds() < g_toast_until) {
int tw = text_width(fonts::system_font, g_toast, TOAST_SIZE);
int pill_w = tw + 24, pill_h = 28;
int px = c.w - GRID_PAD - pill_w;
int py = L.footer.y - GRID_PAD - pill_h;
c.fill_rounded_rect(px, py, pill_w, pill_h, pill_h / 2, g_theme.accent);
draw_text(c, fonts::system_font, px + 12,
py + (pill_h - text_height(fonts::system_font, TOAST_SIZE)) / 2,
g_toast, g_theme.accent_fg, TOAST_SIZE);
// Transient "copied" confirmation: a plain status line just left of the
// Copy button (no floating overlay over the grid).
if (g_copied_until && montauk::get_milliseconds() < g_copied_until) {
const char* msg = "Copied to clipboard";
int sw = text_width(fonts::system_font, msg, STATUS_SIZE);
int sh = text_height(fonts::system_font, STATUS_SIZE);
draw_text(c, fonts::system_font, L.copy_btn.x - 14 - sw,
L.copy_btn.y + (L.copy_btn.h - sh) / 2,
msg, g_theme.accent, STATUS_SIZE);
}
// Copy button — canonical MTK primary button, like other Montauk apps.
mtk::WidgetState copy_state = mtk::widget_state(false, g_copy_hover, true);
mtk::draw_button(c, L.copy_btn, "Copy", mtk::BUTTON_PRIMARY, copy_state, g_theme);
}
// ==== Event handling ====
@@ -596,9 +580,9 @@ extern "C" void _start() {
if (r < 0) break;
if (r == 0) {
// Expire the toast even when idle so the pill disappears.
if (toast_shown && g_toast[0] &&
montauk::get_milliseconds() >= g_toast_until) {
// Clear the "copied" status once it expires, even when idle.
if (toast_shown && g_copied_until &&
montauk::get_milliseconds() >= g_copied_until) {
Canvas canvas = win.canvas();
render(canvas);
win.present();