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/layout.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/layout.c (limited to 'src/layout.c') diff --git a/src/layout.c b/src/layout.c new file mode 100644 index 0000000..9d4b556 --- /dev/null +++ b/src/layout.c @@ -0,0 +1,146 @@ +#include + +#include "uiLibrary.h" +#include "widget/table.h" +#include "widget/widget.h" + +#include + +static void ResizeTable(uiTable* table, int width, int height) { + assert(table); + + if (table->cols == 0) { + return; + } + + // 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 = + (table->rows * g_ui.font->header.glyph_height) > + table->widget.rect.height; + + // Surface width: W. + // Columns: N + // + // First, find the minimum width of each column based on their contents. + // + // If the sum of column widths < N, then distribute the extra space first + // among the smallest columns and building up towards the larger. + // + // If the sum of column widths > N, subtract from the largest column first and + // move towards the smaller ones to distribute the space as evenly as + // possible. + + // Find the minimum width for each column. + int* widths = table->widths; + // Header. + for (int col = 0; col < table->cols; ++col) { + const uiCell* cell = &table->header[col]; + const uiLabel* label = (uiLabel*)cell->child; + const int length = (int)string_length(label->text); + + widths[col] = length; + } + // Table contents. + for (int row = 0; row < table->rows; ++row) { + for (int col = 0; col < table->cols; ++col) { + const uiCell* cell = GetCell(table, row, col); + if (cell->child) { + const uiLabel* label = (uiLabel*)cell->child; + const int length = (int)string_length(label->text); + + widths[col] = length > widths[col] ? length : widths[col]; + } + } + } + // Multiply string lengths times glyph width to compute pixel size. + for (int col = 0; col < table->cols; ++col) { + widths[col] *= g_ui.font->header.glyph_width; + } + + // Find the sum of widths. + int used_width = 0; + for (int col = 0; col < table->cols; ++col) { + used_width += widths[col]; + } + + // Pad if available width is larger than sum of widths. + if (used_width < width) { + // Divide evenly among columns. + // const int extra = width - used_width; + // const int pad = extra / table->cols; + // const int mod = extra % table->cols; + // for (int col = 0; col < table->cols; ++col) { + // table->widths[col] += pad + (col < mod ? 1 : 0); + // } + + int extra = width - used_width; + while (extra > 0) { + // Find smallest column. + int smallest = 0; + for (int col = 1; col < table->cols; ++col) { + if (widths[col] < widths[smallest]) { + smallest = col; + } + } + // Pad it and subtract from the budget. + widths[smallest] += 1; + extra--; + } + } + // Shrink if available width is smaller than the sum of widths. + else if (used_width > width) { + int deficit = used_width - width; + while (deficit > 0) { + // Find largest column. + int largest = 0; + for (int col = 1; col < table->cols; ++col) { + if (widths[col] > widths[largest]) { + largest = col; + } + } + // Shrink it and subtract from the deficit. + widths[largest] -= 1; + deficit--; + } + } + + // Now make room for the scroll bar, if necessary. + if (table->flags.vertical_overflow) { + const int offset = ScrollBarWidth / table->cols; + const int remainder = ScrollBarWidth % table->cols; + for (int col = 0; col < table->cols; ++col) { + table->widths[col] -= offset + (col < remainder ? 1 : 0); + assert(table->widths[col] >= 0); + } + } +} + +static void ResizeWidget(uiWidget* widget, int width, int height) { + assert(widget); + + widget->rect.width = width; + widget->rect.height = height; + + switch (widget->type) { + case uiTypeButton: + break; + case uiTypeFrame: + list_foreach_mut( + widget->children, child, { ResizeWidget(child, width, height); }); + break; + case uiTypeLabel: + break; + case uiTypeTable: + ResizeTable((uiTable*)widget, width, height); + break; + case uiTypeMax: + TRAP(); + break; + } +} + +void uiResizeFrame(uiFrame* frame, int width, int height) { + assert(frame); + ResizeWidget(&frame->widget, width, height); +} -- cgit v1.2.3