summaryrefslogtreecommitdiff
path: root/src/layout.c
blob: 9d4b5561078b04b4accd61ca9d44e1fd2af1b132 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#include <ui.h>

#include "uiLibrary.h"
#include "widget/table.h"
#include "widget/widget.h"

#include <cassert.h>

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);
}