#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; } 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 = (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); }