#include "grid.h"
#include "../../ucx/cx/array_list.h"
#include "../common/context.h"
#include <stdio.h>
#include <stdlib.h>
UiGridLayout* ui_grid_layout_create(
const CxAllocator *a,
short columnspacing,
short rowspacing) {
UiGridLayout *grid = cxZalloc(a,
sizeof(UiGridLayout));
grid->widgets = cxArrayListCreate(a,
NULL,
sizeof(GridElm),
32);
grid->columnspacing = columnspacing;
grid->rowspacing = rowspacing;
return grid;
}
void ui_grid_add_widget(
UiGridLayout *grid,
short x,
short y,
W32Widget *widget,
GridLayoutInfo *layout)
{
GridElm elm;
elm.widget = widget;
elm.gridx = x;
elm.gridy = y;
elm.layout = *layout;
cxListAdd(grid->widgets, &elm);
if (x > grid->max_column) {
grid->max_column = x;
}
if (y > grid->max_row) {
grid->max_row = y;
}
}
void ui_grid_layout(UiGridLayout *grid,
int width,
int height) {
if (width ==
0 || height ==
0) {
return;
}
int ncols = grid->max_column
+1;
int nrows = grid->max_row
+1;
GridDef *cols = calloc(ncols,
sizeof(GridDef));
GridDef *rows = calloc(nrows,
sizeof(GridDef));
int colspacing = grid->columnspacing;
int rowspacing = grid->rowspacing;
int span_max =
1;
for(
int r=
0;r<
2;r++) {
CxIterator i = cxListIterator(grid->widgets);
cx_foreach(GridElm *, elm, i) {
int x = elm->gridx;
int y = elm->gridy;
GridDef *col = &cols[x];
GridDef *row = &rows[y];
W32Size size = w32_widget_get_preferred_size(elm->widget);
elm->layout.preferred_width = size.width;
elm->layout.preferred_height = size.height;
int elm_width = size.width + elm->layout.margin.left + elm->layout.margin.right;
if(elm_width > cols[elm->gridx].preferred_size && elm->layout.colspan <=
1 && span_max ==
1) {
cols[elm->gridx].preferred_size = elm_width;
}
int elm_height = size.height + elm->layout.margin.top + elm->layout.margin.bottom;
if(elm_height > rows[elm->gridy].preferred_size && elm->layout.rowspan <=
1 && span_max ==
1) {
rows[elm->gridy].preferred_size = elm_height;
}
if(elm->layout.rowspan > span_max || elm->layout.colspan > span_max) {
continue;
}
int end_col = x+elm->layout.colspan;
if(end_col > ncols) {
end_col = ncols;
}
int end_row = y+elm->layout.rowspan;
if(end_row > nrows) {
end_row = nrows;
}
if(elm->layout.colspan >
1) {
int span_width =
0;
GridDef *last_col = col;
for(
int c=x;c<end_col;c++) {
span_width += cols[c].size;
last_col = &cols[c];
}
if(span_width < elm->layout.preferred_width) {
last_col->size += elm->layout.preferred_width - span_width;
}
}
if(elm->layout.rowspan >
1) {
int span_height =
0;
GridDef *last_row = row;
for(
int c=x;c<end_row;c++) {
span_height += rows[c].size;
last_row = &rows[c];
}
if(span_height < elm->layout.preferred_height) {
last_row->size += elm->layout.preferred_height - span_height;
}
}
if(elm->layout.hexpand) {
if(elm->layout.colspan >
1) {
GridDef *last_col = col;
for(
int c=x;c<end_col;c++) {
last_col = &cols[c];
if(last_col->expand) {
break;
}
}
last_col->expand =
TRUE;
}
else {
col->expand =
TRUE;
}
}
if(elm->layout.vexpand) {
if(elm->layout.rowspan >
1) {
GridDef *last_row = row;
for(
int c=x;c<nrows;c++) {
last_row = &rows[c];
if(last_row->expand) {
break;
}
}
last_row->expand =
TRUE;
}
else {
row->expand =
TRUE;
}
}
}
span_max =
50000;
}
int col_ext =
0;
int row_ext =
0;
int preferred_width =
0;
int preferred_height =
0;
for(
int j=
0;j<ncols;j++) {
preferred_width += cols[j].preferred_size;
if(cols[j].expand) {
col_ext++;
}
}
for(
int j=
0;j<nrows;j++) {
preferred_height += rows[j].preferred_size;
if(rows[j].expand) {
row_ext++;
}
}
if(ncols >
0) {
preferred_width += (ncols
-1) * colspacing;
}
if(nrows >
0) {
preferred_height += (nrows
-1) * rowspacing;
}
grid->preferred_width = preferred_width;
grid->preferred_height = preferred_height;
int hremaining = width - preferred_width;
int vremaining = height - preferred_height;
int hext = col_ext >
0 ? hremaining/col_ext :
0;
int vext = row_ext >
0 ? vremaining/row_ext :
0;
for(
int j=
0;j<ncols;j++) {
GridDef *col = &cols[j];
if(col->expand) {
col->size = col->preferred_size + hext;
}
else {
col->size = col->preferred_size;
}
}
for(
int j=
0;j<nrows;j++) {
GridDef *row = &rows[j];
if(row->expand) {
row->size = row->preferred_size + vext;
}
else {
row->size = row->preferred_size;
}
}
int pos =
0;
for(
int j=
0;j<ncols;j++) {
cols[j].pos = pos;
pos += cols[j].size + colspacing;
}
pos =
0;
for(
int j=
0;j<nrows;j++) {
rows[j].pos = pos;
pos += rows[j].size + rowspacing;
}
CxIterator i = cxListIterator(grid->widgets);
cx_foreach(GridElm *, elm, i) {
GridDef *col = &cols[elm->gridx];
GridDef *row = &rows[elm->gridy];
int child_width =
0;
int child_height =
0;
int child_x =
0;
int child_y =
0;
if(elm->layout.hfill) {
if(elm->layout.colspan >
1) {
int cwidth =
0;
int end_col = elm->gridx + elm->layout.colspan;
if(end_col > ncols) {
end_col = ncols;
}
int real_span =
0;
for(
int c=elm->gridx;c<end_col;c++) {
cwidth += cols[c].size;
real_span++;
}
if(real_span >
0) {
cwidth += (real_span
-1) * colspacing;
}
child_width = cwidth;
}
else {
child_width = col->size;
}
child_width -= elm->layout.margin.left + elm->layout.margin.right;
}
else {
child_width = elm->layout.preferred_width;
}
if(elm->layout.vfill) {
if(elm->layout.rowspan >
1) {
int rheight =
0;
int end_row = elm->gridy + elm->layout.rowspan;
if(end_row > nrows) {
end_row = nrows;
}
int real_span =
0;
for(
int r=elm->gridy;r<end_row;r++) {
rheight += rows[r].size;
real_span++;
}
if(real_span >
0) {
rheight += (real_span
-1) * rowspacing;
}
child_height = rheight;
}
child_height = row->size - elm->layout.margin.top - elm->layout.margin.bottom;
}
else {
child_height = elm->layout.preferred_height;
}
child_x = col->pos + elm->layout.margin.left;
child_y = row->pos + elm->layout.margin.top;
SetWindowPos(elm->widget->hwnd,
NULL, child_x, child_y, child_width, child_height,
SWP_NOZORDER |
SWP_NOACTIVATE);
InvalidateRect(elm->widget->hwnd,
NULL,
TRUE);
if (elm->widget->layout) {
elm->widget->layout(elm->widget->layoutmanager, child_width, child_height);
}
}
free(cols);
free(rows);
}