bool tableAddChildWithSpanAlign(table *self, widget *child, int row, int rowspan, vAlign v, int column, int colspan, hAlign h) { int i, j; const int rowCount = tableGetRowCount(self); const int columnCount = tableGetColumnCount(self); childPositionInfo *pos; // First, check the row and column is valid assert(row > 0 && row <= tableGetRowCount(self) + 1); assert(column > 0 && column <= tableGetColumnCount(self) + 1); // Second, check all of the cells spanned are empty for (i = row; i < row + rowspan && i <= rowCount; i++) { for (j = column; j < column + colspan && j <= columnCount; j++) { assert(tableGetCell(self, i, j) == NULL); } } // Update the row and column counts self->numRows = MAX(rowCount, row + rowspan - 1); self->numColumns = MAX(columnCount, column + colspan - 1); // Add positional information regarding the child to our list pos = malloc(sizeof(childPositionInfo)); pos->row = row; pos->rowspan = rowspan; pos->column = column; pos->colspan = colspan; pos->vAlignment = v; pos->hAlignment = h; vectorAdd(self->childPositions, pos); // Call widgetAddChildImpl, which will add the child and re-do the layout if (widgetAddChildImpl(WIDGET(self), child)) { return true; } // Problem adding the child; positional information needs to be restored else { vectorRemoveAt(self->childPositions, vectorSize(self->childPositions) - 1); // Release the memory we malloc'ed earlier free(pos); // Restore the row and column counts self->numRows = rowCount; self->numColumns = columnCount; return false; } }
widget *tableGetCell(const table *self, int row, int column) { int i; const int numChildren = vectorSize(WIDGET(self)->children); // Ensure that row and column are valid assert(row > 0 && row <= tableGetRowCount(self)); assert(column > 0 && column <= tableGetColumnCount(self)); // Search through the list of children until we find one matching for (i = 0; i < numChildren; i++) { // Legal as widget->children and table->childPositions are siblings const childPositionInfo *pos = vectorAt(self->childPositions, i); // See if it is a match if (row >= pos->row && row < pos->row + pos->rowspan && column >= pos->column && column < pos->column + pos->colspan) { // We've found the widget return vectorAt(WIDGET(self)->children, i); } } // If the cell is empty, return NULL return NULL; }
bool hBoxAddChildImpl(widget *self, widget *child) { // We want to add the child to the next free column const int column = tableGetColumnCount(TABLE(self)) + 1; // Delegate to tableAddChild with row = 1 return tableAddChild(TABLE(self), child, 1, column); }
size tableGetMaxSizeImpl(widget *self) { size maxSize; const int numRows = tableGetRowCount(TABLE(self)); const int numColumns = tableGetColumnCount(TABLE(self)); int *maxRowHeight = alloca(sizeof(int) * numRows); int *maxColumnWidth = alloca(sizeof(int) * numColumns); tableGetMaximumCellSizes(TABLE(self), maxRowHeight, maxColumnWidth); // Sum up the widths and the heights to get the table size maxSize.x = tablePartialSum(maxColumnWidth, 0, numColumns); maxSize.y = tablePartialSum(maxRowHeight, 0, numRows); return maxSize; }
/** * Removes any rows from the table which no cells occupy. * * @param self The table to remove empty rows from. */ static void tableRemoveEmptyRows(table *self) { int i, j, k; const int columnCount = tableGetColumnCount(self); const int numChildren = vectorSize(WIDGET(self)->children); // Iterate over each row of the table for (i = 1; i <= tableGetRowCount(self); i++) { // See if any of the columns in the row are occupied for (j = 1; j <= columnCount; j++) { if (tableGetCell(self, i, j)) { break; } } // If the cells are all empty, we can remove the row if (j > columnCount) { // Find all cells in rows after the one to be removed for (k = 0; k < numChildren; k++) { childPositionInfo *pos = vectorAt(self->childPositions, k); // Decrease the row number of all rows > i by 1 if (pos->row > i) { pos->row--; } } // The table now has one fewer rows in it self->numRows--; i--; } } }
bool tableDoLayoutImpl(widget *self) { table *selfTable = TABLE(self); const int numChildren = vectorSize(self->children); const int numRows = tableGetRowCount(selfTable); const int numColumns = tableGetColumnCount(selfTable); int i; int dx, dy; int *minColumnWidth = alloca(sizeof(int) * numColumns); int *maxColumnWidth = alloca(sizeof(int) * numColumns); int *minRowHeight = alloca(sizeof(int) * numRows); int *maxRowHeight = alloca(sizeof(int) * numRows); // Syntatic sugar int *columnWidth = minColumnWidth; int *rowHeight = minRowHeight; // Get the minimum and maximum cell sizes tableGetMinimumCellSizes(selfTable, minRowHeight, minColumnWidth); tableGetMaximumCellSizes(selfTable, maxRowHeight, maxColumnWidth); // Calculate how much space we have left to fill dx = self->size.x - tablePartialSum(minColumnWidth, 0, numColumns); dy = self->size.y - tablePartialSum(minRowHeight, 0, numRows); // Increase the column size until we run out of space for (i = 0; dx; i = (i + 1) % numColumns) { // If the column is not maxed out, increases its size by one if (columnWidth[i] < maxColumnWidth[i]) { columnWidth[i]++; dx--; } } // Increase the row size until we run out of space for (i = 0; dy; i = (i + 1) % numRows) { // If the row is not maxed out, increase its size by one if (rowHeight[i] < minRowHeight[i]) { rowHeight[i]++; dy--; } } // Now we need to position the children, taking padding into account for (i = 0; i < numChildren; i++) { // Get the child and its position info widget *child = vectorAt(self->children, i); const childPositionInfo *pos = vectorAt(selfTable->childPositions, i); size maxChildSize = widgetGetMaxSize(child); // left is the sum of all of the preceding columns int left = tablePartialSum(columnWidth, 0, pos->column); // top is the sum of all of the preceding rows int top = tablePartialSum(rowHeight, 0, pos->row); // cellWidth is the sum of the columns we span int cellWidth = tablePartialSum(columnWidth, pos->column - 1, pos->colspan); // cellHeight is the sum of the rows we span int cellHeight = tablePartialSum(rowHeight, pos->row - 1, pos->rowspan); // Final width and height of the child int w, h; // Final x,y offsets of the child int x, y; // If we are not the last row/column, subtract the row/column padding if (pos->column + pos->colspan - 1 != numColumns) { cellWidth -= selfTable->columnPadding; } if (pos->row + pos->rowspan - 1 != numRows) { cellHeight -= selfTable->rowPadding; } // Compute the final width and height of the child w = MIN(cellWidth, maxChildSize.x); h = MIN(cellHeight, maxChildSize.y); // Pad out any remaining space switch (pos->hAlignment) { case LEFT: x = left; break; case CENTRE: x = left + (cellWidth - w) / 2; break; case RIGHT: x = left + cellWidth - w; break; } switch (pos->vAlignment) { case TOP: y = top; break; case MIDDLE: y = top + (cellHeight - h) / 2; break; case BOTTOM: y = top + cellHeight - h; break; } // Resize and reposition the widget widgetResize(child, w, h); widgetReposition(child, x, y); } return true; }
/** * Computes the maximum row/column size for each row/column in the table. It is * important to note that the widths/heights are inclusive of padding. Therefore * all but the rightmost column and bottom row will have either self->rowPadding * or self->columnPadding added onto their sizes. * * @param self The table to get the minimum cell sizes of. * @param maxRowHeight The array to place the maximum row heights for the table * into; assumed to be tableGetRowCount(self) in size. * @param maxColumnWidth The array to place the maximum column widths for the * table into; assumed to be tableGetColumnCount(self) * in size. */ static void tableGetMaximumCellSizes(const table *self, int *maxRowHeight, int *maxColumnWidth) { int i; int spanningIndex; const int numChildren = vectorSize(WIDGET(self)->children); const int numRows = tableGetRowCount(self); const int numColumns = tableGetColumnCount(self); size *maxChildSize = alloca(sizeof(size) * numChildren); // Zero the min row/column sizes memset(maxRowHeight, '\0', sizeof(int) * numRows); memset(maxColumnWidth, '\0', sizeof(int) * numColumns); // Get the maximum row/column sizes for single-spanning cells for (i = 0; i < numChildren; i++) { const childPositionInfo *pos = vectorAt(self->childPositions, i); const int row = pos->row - 1; const int col = pos->column - 1; // Get the maximum size of the cell maxChildSize[i] = widgetGetMaxSize(vectorAt(WIDGET(self)->children, i)); // If the row has a rowspan of 1; see if it is the highest thus far if (pos->rowspan == 1) { maxRowHeight[row] = MAX(maxRowHeight[row], maxChildSize[i].y); } // If the column has a colspan of 1; see if it is the widest thus far if (pos->colspan == 1) { maxColumnWidth[col] = MAX(maxColumnWidth[col], maxChildSize[i].x); } } // Handle spanning rows while ((spanningIndex = tableGetMostOversizedRow(self, maxRowHeight, maxChildSize)) != -1) { int i; const childPositionInfo *pos = vectorAt(self->childPositions, spanningIndex); // Calculate how much larger we need to make the spanned rows int delta = maxChildSize[spanningIndex].y - tablePartialSum(maxRowHeight, pos->row - 1, pos->rowspan); // Loop over the rows spanned increasing their height by 1 for (i = pos->row; delta; i = pos->row + (i + 1) % pos->rowspan, delta--) { maxRowHeight[i]++; } } // Handle spanning columns while ((spanningIndex = tableGetMostOversizedColumn(self, maxColumnWidth, maxChildSize)) != -1) { int i; const childPositionInfo *pos = vectorAt(self->childPositions, spanningIndex); // Calculate how much larger we need to make the spanned columns int delta = maxChildSize[spanningIndex].x - tablePartialSum(maxColumnWidth, pos->column - 1, pos->colspan); for (i = pos->column; delta; i = pos->column + (i + 1) % pos->colspan, delta--) { maxColumnWidth[i]++; } } }