[Bf-blender-cvs] [2de2789] ui_layout_gridflow: Initial (and theorical) code for grid flow layout.

Bastien Montagne noreply at git.blender.org
Sun Dec 4 12:45:54 CET 2016


Commit: 2de27897f3c017920e73bdd10adee3ec0bf4bef5
Author: Bastien Montagne
Date:   Fri Nov 18 11:53:35 2016 +0100
Branches: ui_layout_gridflow
https://developer.blender.org/rB2de27897f3c017920e73bdd10adee3ec0bf4bef5

Initial (and theorical) code for grid flow layout.

Needs RNA API, and... testing! :P

===================================================================

M	source/blender/editors/include/UI_interface.h
M	source/blender/editors/interface/interface_layout.c

===================================================================

diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 9fbce7d..515ad49 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -893,6 +893,8 @@ float uiLayoutGetScaleY(uiLayout *layout);
 uiLayout *uiLayoutRow(uiLayout *layout, int align);
 uiLayout *uiLayoutColumn(uiLayout *layout, int align);
 uiLayout *uiLayoutColumnFlow(uiLayout *layout, int number, int align);
+uiLayout *uiLayoutGridFlow(uiLayout *layout, const bool row_major, const int num_columns, const int align,
+                           const bool even_columns, const bool even_rows);
 uiLayout *uiLayoutBox(uiLayout *layout);
 uiLayout *uiLayoutListBox(uiLayout *layout, struct uiList *ui_list, struct PointerRNA *ptr, struct PropertyRNA *prop,
                           struct PointerRNA *actptr, struct PropertyRNA *actprop);
diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c
index b128bf4..dfc03dc 100644
--- a/source/blender/editors/interface/interface_layout.c
+++ b/source/blender/editors/interface/interface_layout.c
@@ -102,6 +102,7 @@ typedef enum uiItemType {
 	ITEM_LAYOUT_COLUMN,
 	ITEM_LAYOUT_COLUMN_FLOW,
 	ITEM_LAYOUT_ROW_FLOW,
+	ITEM_LAYOUT_GRID_FLOW,
 	ITEM_LAYOUT_BOX,
 	ITEM_LAYOUT_ABSOLUTE,
 	ITEM_LAYOUT_SPLIT,
@@ -154,6 +155,17 @@ typedef struct uiLayoutItemFlow {
 	int totcol;
 } uiLayoutItemFlow;
 
+typedef struct uiLayoutItemGridFlow {
+	uiLayout litem;
+
+	/* Extra parameters */
+	bool row_major, even_columns, even_rows;
+	int num_columns;
+
+	/* Pure internal runtime storage. */
+	int tot_items, tot_columns, tot_rows;
+} uiLayoutItemGridFlow;
+
 typedef struct uiLayoutItemBx {
 	uiLayout litem;
 	uiBut *roundbox;
@@ -307,6 +319,7 @@ static int ui_layout_local_dir(uiLayout *layout)
 			return UI_LAYOUT_HORIZONTAL;
 		case ITEM_LAYOUT_COLUMN:
 		case ITEM_LAYOUT_COLUMN_FLOW:
+		case ITEM_LAYOUT_GRID_FLOW:
 		case ITEM_LAYOUT_SPLIT:
 		case ITEM_LAYOUT_ABSOLUTE:
 		case ITEM_LAYOUT_BOX:
@@ -2517,6 +2530,286 @@ static void ui_litem_layout_column_flow(uiLayout *litem)
 	litem->y = miny;
 }
 
+/* multi-column layout, automatically flowing to the next */
+static void ui_litem_estimate_grid_flow(uiLayout *litem)
+{
+	uiStyle *style = litem->root->style;
+	uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem;
+	uiItem *item;
+	int i;
+
+	/* Estimate average needed width and height per item.
+	 * To do so, we do a weighted average where dimension of an item is its own weight.
+	 * This allows us to give more importance to big items, while not using actual biggest size as reference
+	 * (e.g. if we have a fair amount of very wide items, computed average width will be very close to their width,
+	 * but if we only have one very wide one, and many narrower ones, then we'll contraint the widest into reasonably
+	 * smaller space, to avoid too much emptyness in other cells). */
+	{
+		gflow->tot_items = 0;
+		float avg_w = 0.0f;
+		float avg_h = 0.0f;
+		float totweight_w = 0.0f;
+		float totweight_h = 0.0f;
+		for (item = litem->items.first, gflow->tot_items = 0; item; item = item->next, gflow->tot_items++) {
+			int item_w, item_h;
+			ui_item_size(item, &item_w, &item_h);
+
+			avg_w += (float)(item_w * item_w);
+			totweight_w += (float)item_w;
+
+			avg_h += (float)(item_h * item_h);
+			totweight_h += (float)item_h;
+		}
+		avg_w /= totweight_w;
+		avg_h /= totweight_h;
+
+		/* Even in varying column width case, we fix our columns number from weighted average width of items,
+		 * a proper solving of required width would be too costly, and this should give reasonably good results
+		 * in all resonable cases... */
+		if (gflow->num_columns > 0) {
+			gflow->tot_columns = gflow->num_columns;
+		}
+		else {
+			if (avg_w == 0.0f) {
+				gflow->tot_columns = 1;
+			}
+			else {
+				gflow->tot_columns = min_ii(max_ii((int)(litem->root->emw / avg_w), 1), gflow->tot_items);
+			}
+		}
+		gflow->tot_rows = (int)ceilf((float)gflow->tot_items / gflow->tot_columns);
+
+		/* Set evenly-spaced axes size. */
+		if (gflow->even_columns) {
+			litem->w = (int)(gflow->tot_columns * avg_w) + style->columnspace * (gflow->tot_columns - 1);
+		}
+		if (gflow->even_rows) {
+			litem->h = (int)(gflow->tot_rows * avg_h) + style->buttonspacey * (gflow->tot_rows - 1);
+
+			/* If columns' width and rows' height are even, we are done. */
+			if (gflow->even_columns) {
+				return;
+			}
+		}
+	}
+
+	/* Compute actual needed space for non-evenly spaced axes. */
+	{
+		const bool use_dim1 = gflow->row_major ? !gflow->even_rows : !gflow->even_columns;
+		const bool use_dim2 = gflow->row_major ? !gflow->even_columns : !gflow->even_rows;
+		const int num_dim1 = gflow->row_major ? gflow->tot_rows : gflow->tot_columns;
+		const int num_dim2 = gflow->row_major ? gflow->tot_columns : gflow->tot_rows;
+
+		float avg_dim1 = 0.0f;
+		float totweight_dim1 = 0.0f;
+		float *avg_dim2 = NULL;
+		float *totweight_dim2 = NULL;
+		if (use_dim2) {
+			avg_dim2 = alloca(sizeof(*avg_dim2) * num_dim2);
+			totweight_dim2 = alloca(sizeof(*totweight_dim2) * num_dim2);
+			memset(avg_dim2, 0, sizeof(*avg_dim2) * num_dim2);
+			memset(totweight_dim2, 0, sizeof(*totweight_dim2) * num_dim2);
+		}
+
+		int tot_dim1 = 0, tot_dim2 = 0;;
+		const int space_dim1 = gflow->row_major ? style->buttonspacey : style->columnspace;
+		const int space_dim2 = gflow->row_major ? style->columnspace : style->buttonspacey;
+
+		for (i = 0, item = litem->items.first; item; item = item->next, i++) {
+			const int index_dim2 = i % num_dim2;
+			int item_w, item_h;
+			ui_item_size(item, &item_w, &item_h);
+
+			if (use_dim1) {
+				const int item_dim1 = gflow->row_major ? item_h : item_w;
+				avg_dim1 += (float)(item_dim1 * item_dim1);
+				totweight_dim1 += (float)item_dim1;
+
+				if (index_dim2 == num_dim2 - 1) {
+					/* End of a set in first dimension (i.e. end of a row if row_major, of a column otherwise). */
+					tot_dim1 += (int)(avg_dim1 / totweight_dim1);
+					avg_dim1 = totweight_dim1 = 0.0f;
+				}
+			}
+
+			if (use_dim2) {
+				const int item_dim2 = gflow->row_major ? item_w : item_h;
+
+				avg_dim2[index_dim2] += (float)(item_dim2 * item_dim2);
+				totweight_dim2[index_dim2] += (float)item_dim2;
+			}
+		}
+
+		if (use_dim1) {
+			tot_dim1 += (num_dim1 - 1) * space_dim1;
+		}
+		if (use_dim2) {
+			/* And now, finalize computing of second dimension sizes */
+			for (i = 0; i < num_dim2; i++) {
+				tot_dim2 += (int)(avg_dim2[i] / totweight_dim2[i]);
+			}
+			tot_dim2 += (num_dim2 - 1) * space_dim2;
+		}
+
+		if (!gflow->even_columns) {
+			litem->w = gflow->row_major ? tot_dim2 : tot_dim1;
+		}
+		if (!gflow->even_rows) {
+			litem->h = gflow->row_major ? tot_dim1 : tot_dim2;
+		}
+	}
+}
+
+static void ui_litem_layout_grid_flow(uiLayout *litem)
+{
+	int i;
+	uiStyle *style = litem->root->style;
+	uiLayoutItemGridFlow *gflow = (uiLayoutItemGridFlow *)litem;
+	uiItem *item;
+
+	int *widths = alloca(sizeof(*widths) * gflow->tot_columns);
+	int *heights = alloca(sizeof(*heights) * gflow->tot_rows);
+	int *x_cos = alloca(sizeof(*x_cos) * gflow->tot_columns);
+	int *y_cos = alloca(sizeof(*y_cos) * gflow->tot_rows);
+
+	int even_h;
+
+	/* Precompute even columns' width and xpos, and rows' height and ypos. */
+	if (gflow->even_columns) {
+		for (int col = 0; col < gflow->tot_columns; col++) {
+			x_cos[col] = col ? x_cos[col - 1] + widths[col - 1] + style->columnspace : litem->x;
+			/*            (<     remaining width              > - <         space between remaining columns         >) /
+			 *            <   remamining columns   > */
+			widths[col] = ((litem->w - (x_cos[col] - litem->x)) - (gflow->tot_columns - col - 1) * style->columnspace) /
+			              (gflow->tot_columns - col);
+		}
+	}
+	if (gflow->even_rows) {
+		float avg_h = 0.0f;
+		float totweight_h = 0.0f;
+		for (item = litem->items.first, gflow->tot_items = 0; item; item = item->next, gflow->tot_items++) {
+			int item_h;
+			ui_item_size(item, NULL, &item_h);
+
+			avg_h += (float)(item_h * item_h);
+			totweight_h += (float)item_h;
+		}
+		avg_h /= totweight_h;
+
+		/* Items' height. */
+		even_h = (int)ceilf(avg_h);
+
+		for (int row = 0; row < gflow->tot_rows; row++) {
+			heights[row] = even_h * row;
+			y_cos[row] = litem->y - (even_h + style->buttonspacey) * row;
+		}
+	}
+
+	/* Compute actual needed space for non-evenly spaced axes. */
+	if (!(gflow->even_columns && gflow->even_rows)) {
+		const bool use_dim1 = gflow->row_major ? !gflow->even_rows : !gflow->even_columns;
+		const bool use_dim2 = gflow->row_major ? !gflow->even_columns : !gflow->even_rows;
+		const int num_dim1 = gflow->row_major ? gflow->tot_rows : gflow->tot_columns;
+		const int num_dim2 = gflow->row_major ? gflow->tot_columns : gflow->tot_rows;
+
+		float tot_dim1 = 0.0f, tot_dim2 = 0.0f;
+		float *avg_dim1 = NULL;
+		float *totweight_dim1 = NULL;
+		float *avg_dim2 = NULL;
+		float *totweight_dim2 = NULL;
+		if (use_dim1) {
+			avg_dim1 = alloca(sizeof(*avg_dim1) * num_dim1);
+			totweight_dim1 = alloca(sizeof(*totweight_dim1) * num_dim1);
+			memset(avg_dim1, 0, sizeof(*avg_dim1) * num_dim1);
+			memset(totweight_dim1, 0, sizeof(*totweight_dim1) * num_dim1);
+		}
+		if (use_dim2) {
+			avg_dim2 = alloca(sizeof(*avg_dim2) * num_dim2);
+			totweight_dim2 = alloca(sizeof(*totweight_dim2) * num_dim2);
+			memset(avg_dim2, 0, sizeof(*avg_dim2) * num_dim2);
+			memset(totweight_dim2, 0, sizeof(*totweight_dim2) * num_dim2);
+		}
+
+		const int space_dim1 = gflow->row_major ? style->buttonspacey : litem->space;
+		const int space_dim2 = gflow->row_major ? litem->space : style->buttonspacey;
+
+		for (i = 0, item = litem->items.first; item; item = item->next, i++) {
+			const int index_dim1 = i / num_dim2;
+			const int index_dim2 = i % num_dim2;
+			int item_w, item_h;
+			ui_item_size(item, &item_w, &item_h);
+
+			if (use_dim1) {
+				const int item_dim1 = gflow->row_major ? item_h : item_w;
+				avg_dim1[index_dim1] += (float)(item_dim1 * item_dim1);
+				totweight_dim1[index_dim1] += (float)item_dim1;
+			}
+			if (use_dim2) {
+				const int item_dim2 = gflow->row_major ? item_w : item_h;
+				avg_

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list