From 68a3532728b55b73d8bcadb8ccfc1d9396346cd2 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Sat, 13 Jul 2024 11:44:32 -0700
Subject: Basic table scrollbar rendering.

---
 src/constants.h     |  2 +-
 src/input.c         |  5 ++++-
 src/layout.c        |  6 ++++++
 src/render.c        | 22 ++++++++++++++++++++++
 src/widget/widget.h | 10 ++++++----
 5 files changed, 39 insertions(+), 6 deletions(-)

diff --git a/src/constants.h b/src/constants.h
index 457f461..0d93d14 100644
--- a/src/constants.h
+++ b/src/constants.h
@@ -4,4 +4,4 @@
 #define MaxWidgetEvents 8
 
 // Width of scroll bars in pixels.
-#define ScrollBarWidth 16
+#define ScrollBarWidth 32
diff --git a/src/input.c b/src/input.c
index c4b1be7..20551a6 100644
--- a/src/input.c
+++ b/src/input.c
@@ -6,6 +6,7 @@
 
 #include <cassert.h>
 
+#define Min(a, b) ((a) < (b) ? (a) : (b))
 #define Max(a, b) ((a) > (b) ? (a) : (b))
 
 /// Return true if the rectangle contains the point.
@@ -85,7 +86,9 @@ static void ClickTable(uiTable* table, const uiMouseClickEvent* event) {
 static void ScrollTable(uiTable* table, const uiMouseScrollEvent* event) {
   assert(table);
   assert(event);
-  table->offset = Max(0, table->offset - event->scroll_offset);
+  table->offset =
+      Min(table->rows - table->num_visible_rows,
+          Max(0, table->offset - event->scroll_offset));
 }
 
 /// Process a scroll event.
diff --git a/src/layout.c b/src/layout.c
index 9d4b556..f83976f 100644
--- a/src/layout.c
+++ b/src/layout.c
@@ -13,6 +13,12 @@ static void ResizeTable(uiTable* table, int width, int height) {
     return;
   }
 
+  table->height = height;
+
+  // Compute the number of rows that are visible at once.
+  table->num_visible_rows = height / g_ui.font->header.glyph_height;
+  assert(table->num_visible_rows <= table->rows);
+
   // Determine if there is vertical overflow. This determines whether we need to
   // render a scroll bar, in which case room must be made for it.
   table->flags.vertical_overflow =
diff --git a/src/render.c b/src/render.c
index 24490c0..b1fd3e8 100644
--- a/src/render.c
+++ b/src/render.c
@@ -186,6 +186,8 @@ static void RenderTable(const uiTable* table, RenderState* state) {
   uiRect  original_subsurface = {0};
   uiPoint original_pen        = {0};
 
+  int col_widths_sum = 0;
+
   // Render header.
   if (table->header) {
     for (int col = 0; col < table->cols; ++col) {
@@ -201,6 +203,9 @@ static void RenderTable(const uiTable* table, RenderState* state) {
       // Reset the original subsurface and pen for subsequent columns.
       PopSubsurface(state, &original_subsurface, &original_pen);
 
+      // Keep track of the sum of column widths to later render the scroll bar.
+      col_widths_sum += table->widths[col];
+
       // Next column.
       state->pen.x += table->widths[col];
     }
@@ -235,6 +240,23 @@ static void RenderTable(const uiTable* table, RenderState* state) {
     state->pen.y += g_ui.font->header.glyph_height;
   }
   state->pen.y = y0;
+
+  // Render scrollbar.
+  if (table->flags.vertical_overflow) {
+    state->pen.x = col_widths_sum;
+
+    const int y_start = (int)((double)table->offset / (double)table->rows *
+                              (double)table->height);
+
+    const int height = (int)((double)table->num_visible_rows /
+                             (double)table->rows * (double)table->height);
+
+    FillRect(
+        &(uiRect){.y = y_start, .width = ScrollBarWidth, .height = height},
+        uiPink, state);
+
+    state->pen.x = x0;
+  }
 }
 
 /// Render a widget.
diff --git a/src/widget/widget.h b/src/widget/widget.h
index a2c96bc..c75bd65 100644
--- a/src/widget/widget.h
+++ b/src/widget/widget.h
@@ -54,10 +54,12 @@ typedef struct uiTable {
   uiWidget widget;
   int      rows;
   int      cols;
-  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      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.
   struct {
     bool vertical_overflow : 1; // True if contents overflow vertically.
   } flags;
-- 
cgit v1.2.3