2 * Copyright (c) 2019 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 using Tizen.NUI.BaseComponents;
23 public partial class GridLayout
25 private Node[] hEdgeList;
26 private Node[] vEdgeList;
28 private int maxRowCount;
29 private int maxColumnConut;
30 private float[] hLocations;
31 private float[] vLocations;
32 private int totalHorizontalExpand = 0;
33 private int totalVerticalExpand = 0;
35 private GridChild[] gridChildren;
38 /// The nested class to represent a node of DAG.
42 /// <summary>The start vertex with the same value as <c>Column/Row</c> child property.</summary>
43 public int Start { get; }
45 /// <summary>The end vertex with the same value as <c>Column+ColumnSpan/Row+RowSpan</c>.</summary>
46 public int End { get; }
48 /// <summary>The edge with the same value as measured width/height of child.</summary>
49 public float Edge { get; }
51 /// <summary>The stretch with the same value as <c>HorizontalStretch/VerticalStretch</c>.</summary>
52 public StretchFlags Stretch { get; }
54 /// <summary>The expanded size. It can be updated by expand calculation.</summary>
55 public float ExpandedSize { get; set; }
57 public Node(int vertex, int span, float edge, StretchFlags stretch)
66 private class GridChild
68 public LayoutItem LayoutItem { get; }
69 public Node Column { get; }
70 public Node Row { get; }
71 public GridChild(LayoutItem layoutItem, Node column, Node row)
73 LayoutItem = layoutItem;
79 private void InitChildren(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
81 InitChildrenData(widthMeasureSpec, heightMeasureSpec);
83 InitEdgeList(ref hEdgeList);
84 InitLocations(ref hLocations);
86 InitEdgeList(ref vEdgeList);
87 InitLocations(ref vLocations);
90 private void InitChildrenWithExpand(LayoutLength width, LayoutLength height)
92 if (totalHorizontalExpand > 0)
94 ReInitLocationsWithExpand(ref hLocations, width);
96 if (totalVerticalExpand > 0)
98 ReInitLocationsWithExpand(ref vLocations, height);
102 private void ReInitLocationsWithExpand(ref float[] locations, LayoutLength parentSize)
104 bool isHorizontal = (locations == hLocations);
105 Node[] edgeList = isHorizontal ? hEdgeList : vEdgeList;
106 int maxIndex = isHorizontal ? maxColumnConut : maxRowCount;
107 float space = isHorizontal ? ColumnSpacing : RowSpacing;
108 float totalExpand = isHorizontal ? totalHorizontalExpand : totalVerticalExpand;
110 float parentDecimalSize = parentSize.AsDecimal();
111 float maxExpandedSize = parentDecimalSize * LayoutChildren.Count;
112 float minExpandedSize = 0;
113 float newChildrenSize = locations[maxIndex] - locations[0] - space;
115 // No available sapce
116 if (newChildrenSize > parentDecimalSize)
119 // binary search for finding maximum expanded size.
120 while ((int)(newChildrenSize + 0.5) != (int)parentDecimalSize)
122 float curExpandedSize = (maxExpandedSize + minExpandedSize) / 2;
123 for (int i = 0; i < edgeList.Length; i++)
125 Node node = edgeList[i];
126 // update expanded size.
127 if (node.Stretch.HasFlag(StretchFlags.Expand))
128 node.ExpandedSize = curExpandedSize / totalExpand;
131 // re-init locations based on updated expanded size.
132 InitLocations(ref locations);
133 newChildrenSize = locations[maxIndex] - locations[0] - space;
135 // internal child size cannot exceed the Gridlayout size.
136 if (newChildrenSize > parentDecimalSize)
138 maxExpandedSize = curExpandedSize;
142 minExpandedSize = curExpandedSize;
147 private void InitChildrenData(MeasureSpecification widthMeasureSpec, MeasureSpecification heightMeasureSpec)
149 int childCount = LayoutChildren.Count;
150 bool isHorizontal = (GridOrientation == Orientation.Horizontal);
151 int mainPivot = 0, subPivot = 0;
152 int[] pivotStack = new int[isHorizontal ? Columns : Rows];
154 vLocations = hLocations = null;
155 vEdgeList = hEdgeList = null;
156 gridChildren = new GridChild[childCount];
157 maxColumnConut = Columns;
160 totalVerticalExpand = 0;
161 totalHorizontalExpand = 0;
163 for (int i = 0; i < childCount; i++)
165 LayoutItem item = LayoutChildren[i];
166 View view = item?.Owner;
167 if (view == null) continue;
169 int column, columnSpan, row, rowSpan;
170 StretchFlags verticalStretch, horizontalStretch;
172 column = GetColumn(view);
173 columnSpan = GetColumnSpan(view);
175 rowSpan = GetRowSpan(view);
176 verticalStretch = GetVerticalStretch(view);
177 horizontalStretch = GetHorizontalStretch(view);
179 if (column + columnSpan > maxColumnConut || row + rowSpan > maxRowCount)
181 if (column + columnSpan > maxColumnConut)
182 Tizen.Log.Error("NUI", "Column + ColumnSapn exceeds Grid Columns. Column + ColumnSpan (" + column + " + " + columnSpan + ") > Grid Columns(" + maxColumnConut + ")");
184 Tizen.Log.Error("NUI", "Row + RowSapn exceeds Grid Rows. Row + RowSapn (" + row + " + " + rowSpan + ") > Grid Rows(" + maxRowCount + ")");
186 gridChildren[i] = new GridChild(null, new Node(0, 1, 0, 0), new Node(0, 1, 0, 0));
191 if (horizontalStretch.HasFlag(StretchFlags.Expand))
192 totalHorizontalExpand++;
194 if (verticalStretch.HasFlag(StretchFlags.Expand))
195 totalVerticalExpand++;
197 // assign column/row depending on GridOrientation. The main axis count(Columns on Horizontal, Rows otherwise) won't be exceeded
198 // explicit column(row) count which is assigned by Columns(Rows). but, cross axis count(Rows(Columns)) can be increased by sub axis count.
199 if (column == CellUndefined || row == CellUndefined)
201 (int point, int span) mainAxis = isHorizontal ? (column, columnSpan) : (row, rowSpan);
202 (int point, int span) subAxis = isHorizontal ? (row, rowSpan) : (column, columnSpan);
204 if (subAxis.point != CellUndefined)
205 subPivot = subAxis.point;
206 if (mainAxis.point != CellUndefined)
207 mainPivot = mainAxis.point;
209 if (mainPivot + mainAxis.span > pivotStack.Length)
215 for (int n = mainPivot + mainAxis.span - 1; n >= mainPivot; n--)
217 if (pivotStack[n] > subPivot)
220 n = mainPivot + mainAxis.span;
222 if (n > pivotStack.Length)
224 if (mainAxis.point != CellUndefined)
225 mainPivot = mainAxis.point;
229 n = mainPivot + mainAxis.span;
246 for (int start = mainPivot, end = mainPivot + mainAxis.span; start < end; start++)
248 pivotStack[start] = subPivot + subAxis.span;
251 mainPivot += mainAxis.span;
254 if (maxColumnConut < column + columnSpan)
255 maxColumnConut = column + columnSpan;
256 if (maxRowCount < row + rowSpan)
257 maxRowCount = row + rowSpan;
259 MeasureChildWithMargins(item, widthMeasureSpec, new LayoutLength(0), heightMeasureSpec, new LayoutLength(0));
260 gridChildren[i] = new GridChild(item,
261 new Node(column, columnSpan, item.MeasuredWidth.Size.AsDecimal() + item.Owner.Margin.Start + item.Owner.Margin.End, horizontalStretch),
262 new Node(row, rowSpan, item.MeasuredHeight.Size.AsDecimal() + item.Owner.Margin.Top + item.Owner.Margin.Bottom, verticalStretch));
266 /// <summary> Initialize the edge list sorted by start vetex. </summary>
267 private void InitEdgeList(ref Node[] edgeList)
269 bool isHorizontal = (edgeList == hEdgeList);
270 int axisCount = isHorizontal ? Columns : Rows;
272 edgeList = new Node[gridChildren.Length + axisCount];
274 for (int i = 0; i < gridChildren.Length; i++)
275 edgeList[i] = isHorizontal ? gridChildren[i].Column : gridChildren[i].Row;
277 // Add virtual edge that have no edge for connecting adjacent cells.
278 for (int i = LayoutChildren.Count, end = LayoutChildren.Count + axisCount, v = 0; i < end; i++, v++)
279 edgeList[i] = new Node(v, 1, 0, 0);
281 Array.Sort(edgeList, (a, b) => a.Start.CompareTo(b.Start));
285 /// Locations are longest path from zero-vertex. that means 'locations[MAX] - locations[0]' is maximun size of children.
286 /// Since GridLayout is Directed Acyclic Graph(DAG) which have no negative cycles, longest path can be found in linear time.
288 private void InitLocations(ref float[] locations)
290 bool isHorizontal = (locations == hLocations);
291 int maxAxisCount = isHorizontal ? maxColumnConut : maxRowCount;
292 Node[] edgeList = isHorizontal ? hEdgeList : vEdgeList;
293 float space = isHorizontal ? ColumnSpacing : RowSpacing;
295 locations = new float[maxAxisCount + 1];
297 for (int i = 0; i < edgeList.Length; i++)
299 float newLocation = locations[edgeList[i].Start] + edgeList[i].Edge + edgeList[i].ExpandedSize;
300 if (edgeList[i].Edge + edgeList[i].ExpandedSize > 0)
301 newLocation += space;
303 if (locations[edgeList[i].End] < newLocation)
305 locations[edgeList[i].End] = newLocation;