From 92978a10576d52a0f6c9983d3b6afae7c40eff40 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Thu, 12 Mar 2026 15:29:23 -0700 Subject: Support scrolling by dragging scrollbars --- src/widget/scrollbar.c | 8 +++++++ src/widget/table.c | 58 ++++++++++++++++++++++++++++++++++---------------- src/widget/widget.c | 21 ++++++++++++++++-- src/widget/widget.h | 49 ++++++++++++++++++++++++++---------------- 4 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 src/widget/scrollbar.c (limited to 'src/widget') diff --git a/src/widget/scrollbar.c b/src/widget/scrollbar.c new file mode 100644 index 0000000..9cece5d --- /dev/null +++ b/src/widget/scrollbar.c @@ -0,0 +1,8 @@ +#include "widget.h" + +int ScrollbarScroll(uiScrollbar* scrollbar, int y) { + assert(scrollbar); + scrollbar->handle_y = + Max(0, Min(scrollbar->height - scrollbar->handle_height, y)); + return scrollbar->handle_y; +} diff --git a/src/widget/table.c b/src/widget/table.c index 76a0413..e7d412e 100644 --- a/src/widget/table.c +++ b/src/widget/table.c @@ -1,20 +1,7 @@ #include "widget.h" -const uiCell* GetCell(const uiTable* table, int row, int col) { - assert(table); - return &table->cells[row][col]; -} - -uiCell* GetCellMut(uiTable* table, int row, int col) { - assert(table); - return (uiCell*)GetCell(table, row, col); -} - -uiCell** GetLastRow(uiTable* table) { - assert(table); - assert(table->rows > 0); - return &table->cells[table->rows - 1]; -} +#define Min(a, b) ((a) < (b) ? (a) : (b)) +#define Max(a, b) ((a) > (b) ? (a) : (b)) uiTable* uiMakeTable(int rows, int cols, const char** header) { uiTable* table = UI_NEW(uiTable); @@ -73,7 +60,7 @@ void uiTableAddRow(uiTable* table, const char** row) { ASSERT(cells); table->cells = cells; - uiCell** pLastRow = GetLastRow(table); + uiCell** pLastRow = TableGetLastRow(table); *pLastRow = calloc(table->cols, sizeof(uiCell)); ASSERT(*pLastRow); uiCell* lastRow = *pLastRow; @@ -86,10 +73,45 @@ void uiTableAddRow(uiTable* table, const char** row) { void uiTableSet(uiTable* table, int row, int col, const char* text) { assert(table); assert(text); - GetCellMut(table, row, col)->text = string_new(text); + TableGetCellMut(table, row, col)->text = string_new(text); } const char* uiTableGet(const uiTable* table, int row, int col) { assert(table); - return string_data(GetCell(table, row, col)->text); + return string_data(TableGetCell(table, row, col)->text); +} + +void uiTableScroll(uiTable* table, int row) { + assert(table); + table->offset = Min(table->rows - table->num_visible_rows, Max(0, row)); + SyncScrollbarToTable(table); +} + +void SyncScrollbarToTable(uiTable* table) { + assert(table); + ScrollbarScroll( + &table->scrollbar, (int)((double)table->offset / (double)table->rows * + (double)table->height)); +} + +void SyncTableToScrollbar(uiTable* table) { + assert(table); + table->offset = (int)((double)table->scrollbar.handle_y / + (double)table->height * (double)table->rows); +} + +const uiCell* TableGetCell(const uiTable* table, int row, int col) { + assert(table); + return &table->cells[row][col]; +} + +uiCell* TableGetCellMut(uiTable* table, int row, int col) { + assert(table); + return (uiCell*)TableGetCell(table, row, col); +} + +uiCell** TableGetLastRow(uiTable* table) { + assert(table); + assert(table->rows > 0); + return &table->cells[table->rows - 1]; } diff --git a/src/widget/widget.c b/src/widget/widget.c index ef79ac4..ebcaf10 100644 --- a/src/widget/widget.c +++ b/src/widget/widget.c @@ -63,11 +63,28 @@ uiPtr uiMakeTablePtr(uiTable* table) { return (uiPtr){.type = uiTypeTable, .table = table}; } -static uiPtr uiMakeWidgetPtr(uiWidget* widget) { +uiPtr uiMakeWidgetPtr(uiWidget* widget) { assert(widget); - return (uiPtr){.type = widget->type, .widget = widget}; + switch (widget->type) { + case uiTypeButton: + return uiMakeButtonPtr((uiButton*)widget); + case uiTypeFrame: + return uiMakeFramePtr((uiFrame*)widget); + case uiTypeLabel: + return uiMakeLabelPtr((uiLabel*)widget); + case uiTypeTable: + return uiMakeTablePtr((uiTable*)widget); + default: + ASSERT(false); + break; + } + return (uiPtr){0}; } +uiPtr uiNullptr(void) { return (uiPtr){0}; } + +bool uiIsNullptr(uiPtr ptr) { return ptr.widget == 0; } + uiButton* uiGetButtonPtr(uiPtr ptr) { assert(ptr.type == uiTypeButton); assert(ptr.button); diff --git a/src/widget/widget.h b/src/widget/widget.h index 63f3d94..db11164 100644 --- a/src/widget/widget.h +++ b/src/widget/widget.h @@ -16,39 +16,42 @@ typedef struct uiWidget { Widget_list children; } uiWidget; -/// Button. typedef struct uiButton { uiWidget widget; string text; } uiButton; -/// Frame. typedef struct uiFrame { uiWidget widget; } uiFrame; -/// Label. typedef struct uiLabel { uiWidget widget; string text; } uiLabel; -/// Table cell. +typedef struct uiScrollbar { + int width; + int height; // Total height: handle plus scrollable area. + int handle_height; // Height of the scroll handle. + int handle_y; // Starting y-coordinate of the handle. +} uiScrollbar; + typedef struct uiCell { string text; } uiCell; -/// Table. typedef struct uiTable { - uiWidget widget; - int rows; - int cols; - int height; // Height in pixels. - int* widths; // Width, in pixels, for each column. - uiCell* header; // If non-null, row of 'cols' header cells. - uiCell** cells; // Array of 'rows' rows, each of 'cols' cells. - int offset; // Offset into the rows of the table. Units: rows. - int num_visible_rows; // The number of rows that are visible at once. + uiWidget widget; + int rows; + int cols; + int height; // Height in pixels. + int* widths; // Width, in pixels, for each column. + uiCell* header; // If non-null, row of 'cols' header cells. + uiCell** cells; // Array of 'rows' rows, each of 'cols' cells. + int offset; // Offset into the rows of the table. Units: rows. + int num_visible_rows; // The number of rows that are visible at once. + uiScrollbar scrollbar; struct { bool vertical_overflow : 1; // True if contents overflow vertically. } flags; @@ -56,14 +59,24 @@ typedef struct uiTable { void DestroyWidget(uiWidget** ppWidget); -const uiCell* GetCell(const uiTable* table, int row, int col); -uiCell* GetCellMut(uiTable* table, int row, int col); -uiCell** GetLastRow(uiTable* table); +/// Set the scrollbar handle's y-coordinate, which is clipped to the scrollbar's +/// rectangle. +/// +/// Return the handle's y-coordinate after clipping. +int ScrollbarScroll(uiScrollbar*, int y); -#define UI_NEW(TYPE) (TYPE*)uiAlloc(1, sizeof(TYPE)) +const uiCell* TableGetCell(const uiTable*, int row, int col); +uiCell* TableGetCellMut(uiTable*, int row, int col); +uiCell** TableGetLastRow(uiTable*); +void SyncScrollbarToTable(uiTable*); +void SyncTableToScrollbar(uiTable*); + +static inline int Min(int a, int b) { return a < b ? a : b; } +static inline int Max(int a, int b) { return a > b ? a : b; } static inline void* uiAlloc(size_t count, size_t size) { void* mem = calloc(count, size); ASSERT(mem); return mem; } +#define UI_NEW(TYPE) (TYPE*)uiAlloc(1, sizeof(TYPE)) -- cgit v1.2.3