From a4294e4a94189dffb1fdf99c9a60d87d77272926 Mon Sep 17 00:00:00 2001
From: 3gg <3gg@shellblade.net>
Date: Sat, 13 Jul 2024 10:52:24 -0700
Subject: Restructure project.

---
 src/widget/button.c |  19 ++++++++++
 src/widget/frame.c  |  19 ++++++++++
 src/widget/label.c  |  28 ++++++++++++++
 src/widget/table.c  | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/widget/table.h  |  11 ++++++
 src/widget/widget.c |  93 +++++++++++++++++++++++++++++++++++++++++++++++
 src/widget/widget.h |  66 +++++++++++++++++++++++++++++++++
 7 files changed, 339 insertions(+)
 create mode 100644 src/widget/button.c
 create mode 100644 src/widget/frame.c
 create mode 100644 src/widget/label.c
 create mode 100644 src/widget/table.c
 create mode 100644 src/widget/table.h
 create mode 100644 src/widget/widget.c
 create mode 100644 src/widget/widget.h

(limited to 'src/widget')

diff --git a/src/widget/button.c b/src/widget/button.c
new file mode 100644
index 0000000..f2313fd
--- /dev/null
+++ b/src/widget/button.c
@@ -0,0 +1,19 @@
+#include <ui.h>
+
+#include "widget.h"
+
+uiButton* uiMakeButton(const char* text) {
+  assert(text);
+
+  uiButton* button = UI_NEW(uiButton);
+
+  *button = (uiButton){
+      .widget =
+          (uiWidget){
+                     .type = uiTypeButton,
+                     .rect = {0},
+                     },
+      .text = string_new(text),
+  };
+  return button;
+}
diff --git a/src/widget/frame.c b/src/widget/frame.c
new file mode 100644
index 0000000..e1078be
--- /dev/null
+++ b/src/widget/frame.c
@@ -0,0 +1,19 @@
+#include <ui.h>
+
+#include "widget.h"
+
+uiFrame* uiMakeFrame(void) {
+  uiFrame* frame     = UI_NEW(uiFrame);
+  frame->widget.type = uiTypeFrame;
+  return frame;
+}
+
+void uiDestroyFrame(uiFrame** ppFrame) { DestroyWidget((uiWidget**)ppFrame); }
+
+uiSize uiGetFrameSize(const uiFrame* frame) {
+  assert(frame);
+  return (uiSize){
+      .width  = frame->widget.rect.width,
+      .height = frame->widget.rect.height,
+  };
+}
diff --git a/src/widget/label.c b/src/widget/label.c
new file mode 100644
index 0000000..30ca0ec
--- /dev/null
+++ b/src/widget/label.c
@@ -0,0 +1,28 @@
+#include <ui.h>
+
+#include "uiLibrary.h"
+#include "widget.h"
+
+uiLabel* uiMakeLabel(const char* text) {
+  assert(text);
+
+  uiLabel* label = UI_NEW(uiLabel);
+
+  *label = (uiLabel){
+      .widget =
+          (uiWidget){
+                     .type = uiTypeLabel,
+                     .rect =
+                  (uiRect){
+                      .width =
+                          (int)strlen(text) * g_ui.font->header.glyph_width,
+                      .height = g_ui.font->header.glyph_height}},
+      .text = string_new(text),
+  };
+  return label;
+}
+
+const char* uiLabelGetText(const uiLabel* label) {
+  assert(label);
+  return string_data(label->text);
+}
diff --git a/src/widget/table.c b/src/widget/table.c
new file mode 100644
index 0000000..7a0ea03
--- /dev/null
+++ b/src/widget/table.c
@@ -0,0 +1,103 @@
+#include "table.h"
+
+#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];
+}
+
+uiTable* uiMakeTable(int rows, int cols, const char** header) {
+  uiTable* table = UI_NEW(uiTable);
+
+  *table = (uiTable){
+      .widget = (uiWidget){.type = uiTypeTable},
+      .rows   = rows,
+      .cols   = cols,
+      .widths = (cols > 0) ? calloc(cols, sizeof(int)) : 0,
+      .header = header ? calloc(cols, sizeof(uiCell)) : 0,
+      .cells  = (rows * cols > 0) ? calloc(rows, sizeof(uiCell*)) : 0,
+      .flags  = {0},
+  };
+
+  if (header) {
+    for (int col = 0; col < cols; ++col) {
+      table->header[col].child = (uiWidget*)uiMakeLabel(header[col]);
+    }
+  }
+
+  return table;
+}
+
+void uiTableClear(uiTable* table) {
+  assert(table);
+
+  // Free row data.
+  if (table->cells) {
+    for (int row = 0; row < table->rows; ++row) {
+      for (int col = 0; col < table->cols; ++col) {
+        DestroyWidget(&table->cells[row][col].child);
+      }
+      free(table->cells[row]);
+    }
+    free(table->cells);
+    table->cells = 0;
+  }
+  table->rows = 0;
+
+  // Clear row widths.
+  for (int i = 0; i < table->cols; ++i) {
+    table->widths[i] = 0;
+  }
+
+  table->offset = 0;
+
+  table->flags.vertical_overflow = 0;
+}
+
+void uiTableAddRow(uiTable* table, const char** row) {
+  assert(table);
+
+  table->rows++;
+
+  uiCell** cells = realloc(table->cells, table->rows * sizeof(uiCell*));
+  ASSERT(cells);
+  table->cells = cells;
+
+  uiCell** pLastRow = GetLastRow(table);
+  *pLastRow         = calloc(table->cols, sizeof(uiCell));
+  ASSERT(*pLastRow);
+  uiCell* lastRow = *pLastRow;
+
+  for (int col = 0; col < table->cols; ++col) {
+    lastRow[col].child = (uiWidget*)uiMakeLabel(row[col]);
+  }
+}
+
+void uiTableSet(uiTable* table, int row, int col, uiPtr child) {
+  assert(table);
+  assert(child.widget);
+
+  GetCellMut(table, row, col)->child = child.widget;
+}
+
+const uiWidget* uiTableGet(const uiTable* table, int row, int col) {
+  assert(table);
+  return GetCell(table, row, col)->child;
+}
+
+uiWidget* uiTableGetMut(uiTable* table, int row, int col) {
+  assert(table);
+  return GetCellMut(table, row, col)->child;
+}
diff --git a/src/widget/table.h b/src/widget/table.h
new file mode 100644
index 0000000..9f466de
--- /dev/null
+++ b/src/widget/table.h
@@ -0,0 +1,11 @@
+#pragma once
+
+#include <ui.h>
+
+#include "widget.h"
+
+const uiCell* GetCell(const uiTable* table, int row, int col);
+
+uiCell* GetCellMut(uiTable* table, int row, int col);
+
+uiCell** GetLastRow(uiTable* table);
diff --git a/src/widget/widget.c b/src/widget/widget.c
new file mode 100644
index 0000000..ef79ac4
--- /dev/null
+++ b/src/widget/widget.c
@@ -0,0 +1,93 @@
+#include "widget.h"
+
+#include <cassert.h>
+
+// -----------------------------------------------------------------------------
+// Widget.
+
+#define UI_DEL(ppWidget)       \
+  {                            \
+    assert(ppWidget);          \
+    void* widget_ = *ppWidget; \
+    if (widget_) {             \
+      free(widget_);           \
+      *ppWidget = 0;           \
+    }                          \
+  }
+
+uiWidgetType uiWidgetGetType(const uiWidget* widget) {
+  assert(widget);
+  return widget->type;
+}
+
+void DestroyWidget(uiWidget** ppWidget) {
+  assert(ppWidget);
+
+  uiWidget* widget = *ppWidget;
+  if (widget) {
+    list_foreach_mut(widget->children, child, { DestroyWidget(&child); });
+  }
+  UI_DEL(ppWidget);
+}
+
+void uiWidgetSetParent(uiPtr child_, uiPtr parent_) {
+  uiWidget* child  = child_.widget;
+  uiWidget* parent = parent_.widget;
+
+  assert(child);
+  assert(parent);
+
+  list_add(parent->children, child);
+}
+
+// -----------------------------------------------------------------------------
+// Widget pointers.
+
+uiPtr uiMakeButtonPtr(uiButton* button) {
+  assert(button);
+  return (uiPtr){.type = uiTypeButton, .button = button};
+}
+
+uiPtr uiMakeFramePtr(uiFrame* frame) {
+  assert(frame);
+  return (uiPtr){.type = uiTypeFrame, .frame = frame};
+}
+
+uiPtr uiMakeLabelPtr(uiLabel* label) {
+  assert(label);
+  return (uiPtr){.type = uiTypeLabel, .label = label};
+}
+
+uiPtr uiMakeTablePtr(uiTable* table) {
+  assert(table);
+  return (uiPtr){.type = uiTypeTable, .table = table};
+}
+
+static uiPtr uiMakeWidgetPtr(uiWidget* widget) {
+  assert(widget);
+  return (uiPtr){.type = widget->type, .widget = widget};
+}
+
+uiButton* uiGetButtonPtr(uiPtr ptr) {
+  assert(ptr.type == uiTypeButton);
+  assert(ptr.button);
+  return ptr.button;
+}
+
+uiFrame* uiGetFramePtr(uiPtr ptr) {
+  assert(ptr.type == uiTypeFrame);
+  assert(ptr.frame);
+  return ptr.frame;
+}
+
+uiLabel* uiGetLabelPtr(uiPtr ptr) {
+  assert(ptr.type == uiTypeLabel);
+  assert(ptr.label);
+  return ptr.label;
+}
+
+uiTable* uiGetTablePtr(uiPtr ptr) {
+  assert(ptr.type == uiTypeTable);
+  assert(ptr.table);
+  return ptr.table;
+}
diff --git a/src/widget/widget.h b/src/widget/widget.h
new file mode 100644
index 0000000..a2c96bc
--- /dev/null
+++ b/src/widget/widget.h
@@ -0,0 +1,66 @@
+#pragma once
+
+#include <ui.h>
+
+#include <cstring.h>
+#include <list.h>
+
+#include <stdbool.h>
+
+DEF_LIST(Widget, uiWidget*)
+
+#define UI_NEW(TYPE) (TYPE*)uiAlloc(1, sizeof(TYPE))
+
+static inline void* uiAlloc(size_t count, size_t size) {
+  void* mem = calloc(count, size);
+  ASSERT(mem);
+  return mem;
+}
+
+// -----------------------------------------------------------------------------
+// Widgets.
+
+/// Base widget type.
+typedef struct uiWidget {
+  uiWidgetType type;
+  uiRect       rect;
+  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 uiCell {
+  uiWidget* child;
+} uiCell;
+
+/// Table.
+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.
+  struct {
+    bool vertical_overflow : 1; // True if contents overflow vertically.
+  } flags;
+} uiTable;
+
+void DestroyWidget(uiWidget** ppWidget);
-- 
cgit v1.2.3