feat: add drag-down spreadsheet functions for numbers, fix click mapping issue

This commit is contained in:
2026-04-10 19:07:43 +02:00
parent 00ee161962
commit aa815122be
5 changed files with 149 additions and 28 deletions
+24
View File
@@ -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; 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++) { for (int r = src_r1 + 1; r <= dst_r1; r++) {
int src_r = src_r0 + ((r - src_r0) % src_rows); int src_r = src_r0 + ((r - src_r0) % src_rows);
int drow = r - src_r; 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++) { for (int c = src_c0; c <= src_c1; c++) {
Cell* src = &g_cells[src_r][c]; Cell* src = &g_cells[src_r][c];
Cell* dst = &g_cells[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] == '=') { if (src->input[0] == '=') {
// Formula: adjust cell references // Formula: adjust cell references
adjust_formula_refs(src->input, dst->input, CELL_TEXT_MAX, 0, drow); 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 { } else {
str_cpy(dst->input, src->input, CELL_TEXT_MAX); str_cpy(dst->input, src->input, CELL_TEXT_MAX);
} }
+40
View File
@@ -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'); } 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; } 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) { double str_to_double(const char* s, bool* ok) {
*ok = false; *ok = false;
if (!s || !s[0]) return 0; if (!s || !s[0]) return 0;
+63 -14
View File
@@ -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<ToolBtnId>(id + 1));
}
bool handle_toolbar_click(int mx, int my) { 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 (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; 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(); if (g_editing) commit_edit();
open_file_dialog(); 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(); if (g_editing) commit_edit();
save_file_dialog(); 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(); 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(); 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(); 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(); 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); 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); 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); 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; 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(); 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(); redo_do();
} else { } else {
return false; return false;
@@ -237,7 +286,7 @@ bool handle_toolbar_click(int mx, int my) {
bool handle_fmt_dropdown_click(int mx, int my) { bool handle_fmt_dropdown_click(int mx, int my) {
if (!g_fmt_dropdown_open) return false; if (!g_fmt_dropdown_open) return false;
int dx = TB_FMT_X0; int dx = tb_btn_x0(TB_FMT);
int dy = TOOLBAR_H; int dy = TOOLBAR_H;
int dw = 80; int dw = 80;
int item_h = 26; int item_h = 26;
+1 -1
View File
@@ -379,7 +379,7 @@ void render(uint32_t* pixels) {
// ---- Format dropdown overlay ---- // ---- Format dropdown overlay ----
if (g_fmt_dropdown_open && g_font) { if (g_fmt_dropdown_open && g_font) {
int dx = TB_FMT_X0; int dx = tb_btn_x0(TB_FMT);
int dy = TOOLBAR_H; int dy = TOOLBAR_H;
int dw = 80; int dw = 80;
const char* items[] = { "Auto", "Decimal", "Currency $", "Percent %" }; const char* items[] = { "Auto", "Decimal", "Currency $", "Percent %" };
+21 -13
View File
@@ -118,21 +118,26 @@ struct UndoEntry {
static constexpr int PATHBAR_H = 32; 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; enum ToolBtnId {
static constexpr int TB_SAVE_X0 = 44, TB_SAVE_X1 = 80; TB_OPEN,
static constexpr int TB_CUT_X0 = 92, TB_CUT_X1 = 120; TB_SAVE,
static constexpr int TB_COPY_X0 = 124, TB_COPY_X1 = 160; TB_CUT,
static constexpr int TB_PASTE_X0 = 164, TB_PASTE_X1 = 200; TB_COPY,
static constexpr int TB_BOLD_X0 = 212, TB_BOLD_X1 = 236; TB_PASTE,
static constexpr int TB_AL_X0 = 248, TB_AL_X1 = 272; TB_BOLD,
static constexpr int TB_AC_X0 = 276, TB_AC_X1 = 300; TB_AL,
static constexpr int TB_AR_X0 = 304, TB_AR_X1 = 328; TB_AC,
static constexpr int TB_FMT_X0 = 340, TB_FMT_X1 = 404; TB_AR,
static constexpr int TB_UNDO_X0 = 416, TB_UNDO_X1 = 452; TB_FMT,
static constexpr int TB_REDO_X0 = 456, TB_REDO_X1 = 492; 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) // Global state (extern — defined in main.cpp)
@@ -215,6 +220,9 @@ bool is_alpha(char c);
char to_upper(char c); char to_upper(char c);
double str_to_double(const char* s, bool* ok); double str_to_double(const char* s, bool* ok);
void double_to_str(char* buf, int max, double v); 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 // Function declarations — formula.cpp