From aa815122be33ccf9a39285bf4741accdf8658ba8 Mon Sep 17 00:00:00 2001 From: Daniel Hammer Date: Fri, 10 Apr 2026 19:07:43 +0200 Subject: [PATCH] feat: add drag-down spreadsheet functions for numbers, fix click mapping issue --- programs/src/spreadsheet/edit.cpp | 24 ++++++++ programs/src/spreadsheet/helpers.cpp | 40 +++++++++++++ programs/src/spreadsheet/main.cpp | 77 +++++++++++++++++++++----- programs/src/spreadsheet/render.cpp | 2 +- programs/src/spreadsheet/spreadsheet.h | 34 +++++++----- 5 files changed, 149 insertions(+), 28 deletions(-) diff --git a/programs/src/spreadsheet/edit.cpp b/programs/src/spreadsheet/edit.cpp index 15da4f5..552ff59 100644 --- a/programs/src/spreadsheet/edit.cpp +++ b/programs/src/spreadsheet/edit.cpp @@ -225,9 +225,28 @@ void fill_down(int src_r0, int src_c0, int src_r1, int src_c1, int dst_r1) { int src_rows = src_r1 - src_r0 + 1; + // Detect integer-sequence fill: all source cells must be plain integers + bool is_int_seq = true; + int step[MAX_COLS]; + for (int c = src_c0; is_int_seq && c <= src_c1; c++) { + if (src_rows == 1) { + if (!str_is_int(g_cells[src_r0][c].input)) is_int_seq = false; + step[c] = 1; + } else { + if (!str_is_int(g_cells[src_r0][c].input) || + !str_is_int(g_cells[src_r1][c].input)) { + is_int_seq = false; + } else { + step[c] = str_to_int(g_cells[src_r1][c].input) - + str_to_int(g_cells[src_r0][c].input); + } + } + } + for (int r = src_r1 + 1; r <= dst_r1; r++) { int src_r = src_r0 + ((r - src_r0) % src_rows); int drow = r - src_r; + int fill_num = r - src_r1; // 1, 2, 3, ... past last source row for (int c = src_c0; c <= src_c1; c++) { Cell* src = &g_cells[src_r][c]; Cell* dst = &g_cells[r][c]; @@ -238,6 +257,11 @@ void fill_down(int src_r0, int src_c0, int src_r1, int src_c1, int dst_r1) { if (src->input[0] == '=') { // Formula: adjust cell references adjust_formula_refs(src->input, dst->input, CELL_TEXT_MAX, 0, drow); + } else if (is_int_seq) { + // Integer sequence: continue the arithmetic series + int last_val = str_to_int(g_cells[src_r1][c].input); + int val = last_val + fill_num * step[c]; + int_to_str(dst->input, CELL_TEXT_MAX, val); } else { str_cpy(dst->input, src->input, CELL_TEXT_MAX); } diff --git a/programs/src/spreadsheet/helpers.cpp b/programs/src/spreadsheet/helpers.cpp index 9e745d8..1913088 100644 --- a/programs/src/spreadsheet/helpers.cpp +++ b/programs/src/spreadsheet/helpers.cpp @@ -100,6 +100,46 @@ bool is_digit(char c) { return c >= '0' && c <= '9'; } bool is_alpha(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } char to_upper(char c) { return (c >= 'a' && c <= 'z') ? c - 32 : c; } +bool str_is_int(const char* s) { + if (!s || !s[0]) return false; + int i = 0; + if (s[i] == '-' || s[i] == '+') i++; + if (!is_digit(s[i])) return false; + while (is_digit(s[i])) i++; + return s[i] == '\0'; +} + +int str_to_int(const char* s) { + if (!s || !s[0]) return 0; + int i = 0; + bool neg = false; + if (s[i] == '-') { neg = true; i++; } + else if (s[i] == '+') i++; + int result = 0; + while (is_digit(s[i])) { + result = result * 10 + (s[i] - '0'); + i++; + } + return neg ? -result : result; +} + +void int_to_str(char* dst, int max, int v) { + if (max <= 0) return; + if (v == 0) { dst[0] = '0'; dst[1] = '\0'; return; } + int i = 0; + bool neg = false; + if (v < 0) { neg = true; v = -v; } + char buf[16]; + int j = 0; + while (v > 0 && j < 16) { + buf[j++] = '0' + (v % 10); + v /= 10; + } + if (neg && j < 15) buf[j++] = '-'; + for (int k = j - 1; k >= 0 && i < max - 1; k--) dst[i++] = buf[k]; + dst[i] = '\0'; +} + double str_to_double(const char* s, bool* ok) { *ok = false; if (!s || !s[0]) return 0; diff --git a/programs/src/spreadsheet/main.cpp b/programs/src/spreadsheet/main.cpp index 69dd5d1..11ec307 100644 --- a/programs/src/spreadsheet/main.cpp +++ b/programs/src/spreadsheet/main.cpp @@ -196,38 +196,87 @@ static void save_file_dialog() { } } +// ============================================================================ +// Toolbar button geometry (mirrors render.cpp layout exactly) +// ============================================================================ + +// Button widths matching render.cpp: icons=24, Bold/Align=24, Fmt=64, Undo/Redo=36 +static constexpr int TB_ICON_W = 24; +static constexpr int TB_FMT_W = 64; +static constexpr int TB_UNDOREDO_W = 36; + +int tb_btn_x0(ToolBtnId id) { + // Mirrors render.cpp toolbar button layout exactly + int bx = 4; // start + + // Open, Save (icon buttons: 24px wide, 4px gap) + if (id == TB_OPEN) return bx; bx += 24 + 4; + if (id == TB_SAVE) return bx; bx += 24 + 4; + bx += 8; // separator after Save + + // Cut, Copy, Paste (icon buttons) + if (id == TB_CUT) return bx; bx += 24 + 4; + if (id == TB_COPY) return bx; bx += 24 + 4; + if (id == TB_PASTE) return bx; bx += 24 + 4; + bx += 8; // separator after Paste + + // Bold (24px) + if (id == TB_BOLD) return bx; bx += 24 + 4; + bx += 8; // separator after Bold + + // AL, AC, AR (24px each) + if (id == TB_AL) return bx; bx += 24 + 4; + if (id == TB_AC) return bx; bx += 24 + 4; + if (id == TB_AR) return bx; bx += 24 + 4; + bx += 8; // separator after AR + + // FMT (64px) + if (id == TB_FMT) return bx; bx += 64 + 4; + bx += 8; // separator after FMT + + // Undo, Redo (36px each) + if (id == TB_UNDO) return bx; bx += 36 + 4; + if (id == TB_REDO) return bx; bx += 36 + 4; + + return 0; +} + +int tb_btn_x1(ToolBtnId id) { + return tb_btn_x0(static_cast(id + 1)); +} + bool handle_toolbar_click(int mx, int my) { if (my >= TOOLBAR_H || my < TB_BTN_Y || my >= TB_BTN_Y + TB_BTN_SIZE) return false; - if (g_fmt_dropdown_open && !(mx >= TB_FMT_X0 && mx < TB_FMT_X1)) { + if (g_fmt_dropdown_open && !(mx >= tb_btn_x0(TB_FMT) && mx < tb_btn_x1(TB_FMT))) { g_fmt_dropdown_open = false; } - if (mx >= TB_OPEN_X0 && mx < TB_OPEN_X1) { + if (mx >= tb_btn_x0(TB_OPEN) && mx < tb_btn_x1(TB_OPEN)) { if (g_editing) commit_edit(); open_file_dialog(); - } else if (mx >= TB_SAVE_X0 && mx < TB_SAVE_X1) { + } else if (mx >= tb_btn_x0(TB_SAVE) && mx < tb_btn_x1(TB_SAVE)) { if (g_editing) commit_edit(); save_file_dialog(); - } else if (mx >= TB_CUT_X0 && mx < TB_CUT_X1) { + } else if (mx >= tb_btn_x0(TB_CUT) && mx < tb_btn_x1(TB_CUT)) { if (!g_editing) cut_selection(); - } else if (mx >= TB_COPY_X0 && mx < TB_COPY_X1) { + } else if (mx >= tb_btn_x0(TB_COPY) && mx < tb_btn_x1(TB_COPY)) { if (!g_editing) copy_selection(); - } else if (mx >= TB_PASTE_X0 && mx < TB_PASTE_X1) { + } else if (mx >= tb_btn_x0(TB_PASTE) && mx < tb_btn_x1(TB_PASTE)) { if (!g_editing) paste_at_cursor(); - } else if (mx >= TB_BOLD_X0 && mx < TB_BOLD_X1) { + } else if (mx >= tb_btn_x0(TB_BOLD) && mx < tb_btn_x1(TB_BOLD)) { if (!g_editing) apply_bold_toggle(); - } else if (mx >= TB_AL_X0 && mx < TB_AL_X1) { + } else if (mx >= tb_btn_x0(TB_AL) && mx < tb_btn_x1(TB_AL)) { if (!g_editing) apply_align(ALIGN_LEFT); - } else if (mx >= TB_AC_X0 && mx < TB_AC_X1) { + } else if (mx >= tb_btn_x0(TB_AC) && mx < tb_btn_x1(TB_AC)) { if (!g_editing) apply_align(ALIGN_CENTER); - } else if (mx >= TB_AR_X0 && mx < TB_AR_X1) { + } else if (mx >= tb_btn_x0(TB_AR) && mx < tb_btn_x1(TB_AR)) { if (!g_editing) apply_align(ALIGN_RIGHT); - } else if (mx >= TB_FMT_X0 && mx < TB_FMT_X1) { + } else if (mx >= tb_btn_x0(TB_FMT) && mx < tb_btn_x1(TB_FMT)) { g_fmt_dropdown_open = !g_fmt_dropdown_open; - } else if (mx >= TB_UNDO_X0 && mx < TB_UNDO_X1) { + } else if (mx >= tb_btn_x0(TB_UNDO) && mx < tb_btn_x1(TB_UNDO)) { undo_do(); - } else if (mx >= TB_REDO_X0 && mx < TB_REDO_X1) { + } else if (mx >= tb_btn_x0(TB_REDO) && mx < tb_btn_x1(TB_REDO)) { redo_do(); } else { return false; @@ -237,7 +286,7 @@ bool handle_toolbar_click(int mx, int my) { bool handle_fmt_dropdown_click(int mx, int my) { if (!g_fmt_dropdown_open) return false; - int dx = TB_FMT_X0; + int dx = tb_btn_x0(TB_FMT); int dy = TOOLBAR_H; int dw = 80; int item_h = 26; diff --git a/programs/src/spreadsheet/render.cpp b/programs/src/spreadsheet/render.cpp index 80eb078..ef02b84 100644 --- a/programs/src/spreadsheet/render.cpp +++ b/programs/src/spreadsheet/render.cpp @@ -379,7 +379,7 @@ void render(uint32_t* pixels) { // ---- Format dropdown overlay ---- if (g_fmt_dropdown_open && g_font) { - int dx = TB_FMT_X0; + int dx = tb_btn_x0(TB_FMT); int dy = TOOLBAR_H; int dw = 80; const char* items[] = { "Auto", "Decimal", "Currency $", "Percent %" }; diff --git a/programs/src/spreadsheet/spreadsheet.h b/programs/src/spreadsheet/spreadsheet.h index f847052..a8f728a 100644 --- a/programs/src/spreadsheet/spreadsheet.h +++ b/programs/src/spreadsheet/spreadsheet.h @@ -118,21 +118,26 @@ struct UndoEntry { static constexpr int PATHBAR_H = 32; // ============================================================================ -// Toolbar button positions +// Toolbar button IDs (must match handle_toolbar_click and render order) // ============================================================================ -static constexpr int TB_OPEN_X0 = 4, TB_OPEN_X1 = 40; -static constexpr int TB_SAVE_X0 = 44, TB_SAVE_X1 = 80; -static constexpr int TB_CUT_X0 = 92, TB_CUT_X1 = 120; -static constexpr int TB_COPY_X0 = 124, TB_COPY_X1 = 160; -static constexpr int TB_PASTE_X0 = 164, TB_PASTE_X1 = 200; -static constexpr int TB_BOLD_X0 = 212, TB_BOLD_X1 = 236; -static constexpr int TB_AL_X0 = 248, TB_AL_X1 = 272; -static constexpr int TB_AC_X0 = 276, TB_AC_X1 = 300; -static constexpr int TB_AR_X0 = 304, TB_AR_X1 = 328; -static constexpr int TB_FMT_X0 = 340, TB_FMT_X1 = 404; -static constexpr int TB_UNDO_X0 = 416, TB_UNDO_X1 = 452; -static constexpr int TB_REDO_X0 = 456, TB_REDO_X1 = 492; +enum ToolBtnId { + TB_OPEN, + TB_SAVE, + TB_CUT, + TB_COPY, + TB_PASTE, + TB_BOLD, + TB_AL, + TB_AC, + TB_AR, + TB_FMT, + TB_UNDO, + TB_REDO, + TB_COUNT +}; +int tb_btn_x0(ToolBtnId id); +int tb_btn_x1(ToolBtnId id); // ============================================================================ // Global state (extern — defined in main.cpp) @@ -215,6 +220,9 @@ bool is_alpha(char c); char to_upper(char c); double str_to_double(const char* s, bool* ok); void double_to_str(char* buf, int max, double v); +bool str_is_int(const char* s); +int str_to_int(const char* s); +void int_to_str(char* dst, int max, int v); // ============================================================================ // Function declarations — formula.cpp