1 /*
2  * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "layout/flex_layout.h"
17 
18 namespace OHOS {
LayoutChildren(bool needInvalidate)19 void FlexLayout::LayoutChildren(bool needInvalidate)
20 {
21     if (childrenHead_ == nullptr) {
22         return;
23     }
24 
25     if ((direction_ == LAYOUT_HOR) || (direction_ == LAYOUT_HOR_R)) {
26         LayoutHorizontal();
27     } else {
28         LayoutVertical();
29     }
30     if (needInvalidate) {
31         Invalidate();
32     }
33 }
34 
GetStartPos(const int16_t & length,int16_t & pos,int16_t & interval,int16_t count,uint16_t * validLengths,uint16_t * childsNum)35 void FlexLayout::GetStartPos(const int16_t& length,
36                              int16_t& pos,
37                              int16_t& interval,
38                              int16_t count,
39                              uint16_t* validLengths,
40                              uint16_t* childsNum)
41 {
42     if (!validLengths || !childsNum) {
43         return;
44     }
45     pos = 0;
46     interval = 0;
47 
48     if (majorAlign_ == ALIGN_START) {
49         pos = 0;
50     } else if (majorAlign_ == ALIGN_END) {
51         pos = length - validLengths[count];
52         /* if total length of children is too long or only one child, layout them centerly no matter what key word set.
53          */
54     } else if ((majorAlign_ == ALIGN_CENTER) || (validLengths[count] >= length) || (childsNum[count] == 1)) {
55         pos = (length - validLengths[count]) / 2; // 2: half
56     } else if (majorAlign_ == ALIGN_AROUND) {
57         if (childsNum[count] == 0) {
58             return;
59         }
60         interval = (length - validLengths[count]) / childsNum[count];
61         pos = interval / 2; // 2: half
62     } else if (majorAlign_ == ALIGN_EVENLY) {
63         interval = (length - validLengths[count]) / (childsNum[count] + 1);
64         pos = interval;
65     } else {
66         interval = (length - validLengths[count]) / (childsNum[count] - 1);
67         pos = 0;
68     }
69 }
70 
GetNoWrapStartPos(const int16_t & length,int16_t & majorPos,int16_t & interval)71 void FlexLayout::GetNoWrapStartPos(const int16_t& length, int16_t& majorPos, int16_t& interval)
72 {
73     uint16_t childrenNum = 0;
74     uint16_t totalValidLength = 0;
75 
76     CalValidLength(totalValidLength, childrenNum);
77     GetStartPos(length, majorPos, interval, 0, &totalValidLength, &childrenNum);
78 }
79 
GetRowStartPos(int16_t & pos,int16_t & interval,int16_t count,uint16_t * rowsWidth,uint16_t * rowsChildNum)80 void FlexLayout::GetRowStartPos(int16_t& pos,
81                                 int16_t& interval,
82                                 int16_t count,
83                                 uint16_t* rowsWidth,
84                                 uint16_t* rowsChildNum)
85 {
86     GetStartPos(GetWidth(), pos, interval, count, rowsWidth, rowsChildNum);
87 }
88 
GetColumnStartPos(int16_t & pos,int16_t & interval,int16_t count,uint16_t * columnsHeight,uint16_t * columnsChildNum)89 void FlexLayout::GetColumnStartPos(int16_t& pos,
90                                    int16_t& interval,
91                                    int16_t count,
92                                    uint16_t* columnsHeight,
93                                    uint16_t* columnsChildNum)
94 {
95     GetStartPos(GetHeight(), pos, interval, count, columnsHeight, columnsChildNum);
96 }
97 
CalValidLength(uint16_t & totalValidLength,uint16_t & allChildNum)98 void FlexLayout::CalValidLength(uint16_t& totalValidLength, uint16_t& allChildNum)
99 {
100     UIView* child = childrenHead_;
101     int16_t left;
102     int16_t right;
103     int16_t top;
104     int16_t bottom;
105 
106     /* calculate valid length of all children views */
107     while (child != nullptr) {
108         if (child->IsVisible()) {
109             child->ReMeasure();
110             if ((direction_ == LAYOUT_HOR) || (direction_ == LAYOUT_HOR_R)) {
111                 left = child->GetStyle(STYLE_MARGIN_LEFT);
112                 right = child->GetStyle(STYLE_MARGIN_RIGHT);
113                 totalValidLength += (child->GetRelativeRect().GetWidth() + left + right);
114             } else {
115                 top = child->GetStyle(STYLE_MARGIN_TOP);
116                 bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
117                 totalValidLength += (child->GetRelativeRect().GetHeight() + top + bottom);
118             }
119             allChildNum++;
120         }
121         child = child->GetNextSibling();
122     }
123 }
124 
CalRowCount()125 void FlexLayout::CalRowCount()
126 {
127     UIView* child = childrenHead_;
128     int16_t pos = 0;
129     int16_t left;
130     int16_t right;
131 
132     rowCount_ = 1;
133     while (child != nullptr) {
134         if (child->IsVisible()) {
135             child->ReMeasure();
136             left = child->GetStyle(STYLE_MARGIN_LEFT);
137             right = child->GetStyle(STYLE_MARGIN_RIGHT);
138             pos += left;
139             if ((pos + child->GetRelativeRect().GetWidth() + right) > GetWidth()) {
140                 pos = left;
141                 rowCount_++;
142             }
143             pos += child->GetRelativeRect().GetWidth() + right;
144         }
145         child = child->GetNextSibling();
146     }
147 }
148 
GetRowMaxHeight(uint16_t size,uint16_t * maxRosHegiht)149 void FlexLayout::GetRowMaxHeight(uint16_t size, uint16_t* maxRosHegiht)
150 {
151     UIView* child = childrenHead_;
152     int16_t pos = 0;
153     int16_t left;
154     int16_t right;
155     int16_t top;
156     int16_t bottom;
157     uint16_t i = 0;
158     uint16_t height = 0;
159 
160     if ((maxRosHegiht == nullptr) || (size > rowCount_)) {
161         return;
162     }
163 
164     while (child != nullptr) {
165         if (child->IsVisible()) {
166             left = child->GetStyle(STYLE_MARGIN_LEFT);
167             right = child->GetStyle(STYLE_MARGIN_RIGHT);
168             top = child->GetStyle(STYLE_MARGIN_TOP);
169             bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
170             pos += left;
171             if ((pos + child->GetRelativeRect().GetWidth() + right) > GetWidth()) {
172                 pos = left;
173                 maxRosHegiht[i] = height;
174                 height = 0;
175                 i++;
176             }
177             height = MATH_MAX(height, child->GetRelativeRect().GetHeight() + top + bottom);
178             maxRosHegiht[i] = height;
179             pos += child->GetRelativeRect().GetWidth() + right;
180         }
181         child = child->GetNextSibling();
182     }
183 }
184 
GetRowsWidth(uint16_t rowNum,uint16_t * rowsWidth,uint16_t * rowsChildNum)185 void FlexLayout::GetRowsWidth(uint16_t rowNum, uint16_t* rowsWidth, uint16_t* rowsChildNum)
186 {
187     UIView* child = childrenHead_;
188     int16_t pos = 0;
189     int16_t left;
190     int16_t right;
191     uint16_t rowChildNum = 0;
192     uint16_t rowCount = 0;
193     uint16_t width = 0;
194 
195     if ((rowsWidth == nullptr) || (rowsChildNum == nullptr) || (rowNum > rowCount_)) {
196         return;
197     }
198 
199     while (child != nullptr) {
200         if (child->IsVisible()) {
201             left = child->GetStyle(STYLE_MARGIN_LEFT);
202             right = child->GetStyle(STYLE_MARGIN_RIGHT);
203             pos += left;
204             if ((pos + child->GetRelativeRect().GetWidth() + right) > GetWidth()) {
205                 pos = left;
206                 rowsWidth[rowCount] = width;
207                 width = 0;
208                 rowsChildNum[rowCount] = rowChildNum;
209                 rowChildNum = 0;
210                 rowCount++;
211             }
212             width += child->GetRelativeRect().GetWidth() + right + left;
213             rowsWidth[rowCount] = width;
214             rowChildNum++;
215             rowsChildNum[rowCount] = rowChildNum;
216             pos += child->GetRelativeRect().GetWidth() + right;
217         }
218         child = child->GetNextSibling();
219     }
220 }
221 
GetCrossAxisPosY(int16_t & posY,uint16_t & count,uint16_t * rowsMaxHeight,UIView * child)222 void FlexLayout::GetCrossAxisPosY(int16_t& posY, uint16_t& count, uint16_t* rowsMaxHeight, UIView* child)
223 {
224     if ((rowsMaxHeight == nullptr) || (child == nullptr)) {
225         return;
226     }
227 
228     uint16_t i = 0;
229     uint16_t offset = 0;
230     int16_t top = child->GetStyle(STYLE_MARGIN_TOP);
231     int16_t bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
232 
233     if (secondaryAlign_ == ALIGN_START) {
234         for (i = 0; i < count; i++) {
235             offset += rowsMaxHeight[i];
236         }
237         posY = top + offset;
238     } else if (secondaryAlign_ == ALIGN_END) {
239         for (i = rowCount_ - 1; i > count; i--) {
240             offset += rowsMaxHeight[i];
241         }
242         posY = GetHeight() - child->GetRelativeRect().GetHeight() - bottom - offset;
243     } else {
244         for (i = 0; i < rowCount_; i++) {
245             offset += rowsMaxHeight[i];
246         }
247         offset = (rowsMaxHeight[0] - offset) / 2; // 2: half
248         for (i = 1; i <= count; i++) {
249             offset += (rowsMaxHeight[i - 1] + rowsMaxHeight[i]) / 2; // 2: half
250         }
251         posY = (GetHeight() - child->GetRelativeRect().GetHeight() - top - bottom) / 2 + top + offset; // 2: half
252     }
253 }
254 
LayoutHorizontal()255 void FlexLayout::LayoutHorizontal()
256 {
257     UIView* child = childrenHead_;
258     int16_t interval = 0;
259     int16_t posX = 0;
260     int16_t posY = 0;
261     uint16_t count = 0;
262     uint16_t widthsBuf[MAX_COUNT_DEFAULT] = {0};
263     uint16_t maxHeightsBuf[MAX_COUNT_DEFAULT] = {0};
264     uint16_t childsNumBuf[MAX_COUNT_DEFAULT] = {0};
265     uint16_t* rowsWidth = widthsBuf;
266     uint16_t* rowsMaxHeight = maxHeightsBuf;
267     uint16_t* rowsChildNum = childsNumBuf;
268     bool allocFlag = false;
269 
270     if (wrap_ == WRAP) {
271         CalRowCount();
272         if (rowCount_ > MAX_COUNT_DEFAULT) {
273             rowsWidth = new uint16_t[rowCount_]();
274             rowsMaxHeight = new uint16_t[rowCount_]();
275             rowsChildNum = new uint16_t[rowCount_]();
276             allocFlag = true;
277         }
278         GetRowMaxHeight(rowCount_, rowsMaxHeight);
279         GetRowsWidth(rowCount_, rowsWidth, rowsChildNum);
280         GetRowStartPos(posX, interval, count, rowsWidth, rowsChildNum);
281     } else {
282         GetNoWrapStartPos(GetWidth(), posX, interval);
283     }
284 
285     while (child != nullptr) {
286         if (child->IsVisible()) {
287             int16_t left = child->GetStyle(STYLE_MARGIN_LEFT);
288             int16_t right = child->GetStyle(STYLE_MARGIN_RIGHT);
289             posX += left;
290             if (((posX + child->GetRelativeRect().GetWidth() + right) > GetWidth()) && (wrap_ == WRAP)) {
291                 GetRowStartPos(posX, interval, ++count, rowsWidth, rowsChildNum);
292                 posX += left;
293             }
294 
295             GetCrossAxisPosY(posY, count, rowsMaxHeight, child);
296             if (direction_ == LAYOUT_HOR_R) {
297                 child->SetPosition(GetWidth() - posX - child->GetRelativeRect().GetWidth() - right,
298                                    posY - child->GetStyle(STYLE_MARGIN_TOP));
299             } else {
300                 child->SetPosition(posX - left, posY - child->GetStyle(STYLE_MARGIN_TOP));
301             }
302             posX += child->GetRelativeRect().GetWidth() + right + interval;
303             child->LayoutChildren();
304         }
305         child = child->GetNextSibling();
306     }
307 
308     if (allocFlag) {
309         delete[] rowsWidth;
310         delete[] rowsMaxHeight;
311         delete[] rowsChildNum;
312     }
313 }
314 
CalColumnCount()315 void FlexLayout::CalColumnCount()
316 {
317     UIView* child = childrenHead_;
318     int16_t pos = 0;
319     int16_t top;
320     int16_t bottom;
321 
322     columnCount_ = 1;
323     while (child != nullptr) {
324         if (child->IsVisible()) {
325             child->ReMeasure();
326             top = child->GetStyle(STYLE_MARGIN_TOP);
327             bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
328             pos += top;
329             if ((pos + child->GetRelativeRect().GetHeight() + bottom) > GetHeight()) {
330                 pos = top;
331                 columnCount_++;
332             }
333             pos += child->GetRelativeRect().GetHeight() + bottom;
334         }
335         child = child->GetNextSibling();
336     }
337 }
338 
GetColumnMaxWidth(uint16_t size,uint16_t * maxColumnsWidth)339 void FlexLayout::GetColumnMaxWidth(uint16_t size, uint16_t* maxColumnsWidth)
340 {
341     UIView* child = childrenHead_;
342     int16_t pos = 0;
343     int16_t left;
344     int16_t right;
345     int16_t bottom;
346     uint16_t i = 0;
347     uint16_t width = 0;
348 
349     if ((maxColumnsWidth == nullptr) || (size > columnCount_)) {
350         return;
351     }
352 
353     while (child != nullptr) {
354         if (child->IsVisible()) {
355             left = child->GetStyle(STYLE_MARGIN_LEFT);
356             right = child->GetStyle(STYLE_MARGIN_RIGHT);
357             bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
358             pos += left;
359             if ((pos + child->GetRelativeRect().GetHeight() + bottom) > GetHeight()) {
360                 pos = left;
361                 maxColumnsWidth[i] = width;
362                 width = 0;
363                 i++;
364             }
365             width = MATH_MAX(width, child->GetRelativeRect().GetWidth() + left + right);
366             maxColumnsWidth[i] = width;
367             pos += child->GetRelativeRect().GetHeight() + bottom;
368         }
369         child = child->GetNextSibling();
370     }
371 }
372 
GetColumnsHeight(uint16_t columnNum,uint16_t * columnsHeight,uint16_t * columnsChildNum)373 void FlexLayout::GetColumnsHeight(uint16_t columnNum, uint16_t* columnsHeight, uint16_t* columnsChildNum)
374 {
375     UIView* child = childrenHead_;
376     int16_t pos = 0;
377     int16_t top;
378     int16_t bottom;
379     uint16_t columnChildNum = 0;
380     uint16_t columnCount = 0;
381     uint16_t height = 0;
382 
383     if ((columnsHeight == nullptr) || (columnsChildNum == nullptr) || (columnNum > columnCount_)) {
384         return;
385     }
386 
387     while (child != nullptr) {
388         if (child->IsVisible()) {
389             top = child->GetStyle(STYLE_MARGIN_TOP);
390             bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
391             pos += top;
392             if ((pos + child->GetRelativeRect().GetHeight() + bottom) > GetHeight()) {
393                 pos = top;
394                 columnsHeight[columnCount] = height;
395                 height = 0;
396                 columnsChildNum[columnCount] = columnChildNum;
397                 columnChildNum = 0;
398                 columnCount++;
399             }
400             height += child->GetRelativeRect().GetHeight() + top + bottom;
401             columnsHeight[columnCount] = height;
402             columnChildNum++;
403             columnsChildNum[columnCount] = columnChildNum;
404             pos += child->GetRelativeRect().GetHeight() + bottom;
405         }
406         child = child->GetNextSibling();
407     }
408 }
409 
GetCrossAxisPosX(int16_t & posX,uint16_t & count,uint16_t * columnsMaxWidth,UIView * child)410 void FlexLayout::GetCrossAxisPosX(int16_t& posX, uint16_t& count, uint16_t* columnsMaxWidth, UIView* child)
411 {
412     if ((columnsMaxWidth == nullptr) || (child == nullptr)) {
413         return;
414     }
415 
416     uint16_t i = 0;
417     uint16_t offset = 0;
418     int16_t left = child->GetStyle(STYLE_MARGIN_LEFT);
419     int16_t right = child->GetStyle(STYLE_MARGIN_RIGHT);
420 
421     if (secondaryAlign_ == ALIGN_START) {
422         for (i = 0; i < count; i++) {
423             offset += columnsMaxWidth[i];
424         }
425         posX = left + offset;
426     } else if (secondaryAlign_ == ALIGN_END) {
427         for (i = columnCount_ - 1; i > count; i--) {
428             offset += columnsMaxWidth[i];
429         }
430         posX = GetWidth() - child->GetRelativeRect().GetWidth() - right - offset;
431     } else {
432         for (i = 0; i < columnCount_; i++) {
433             offset += columnsMaxWidth[i];
434         }
435         offset = (columnsMaxWidth[0] - offset) / 2; // 2: half
436         for (i = 1; i <= count; i++) {
437             offset += (columnsMaxWidth[i - 1] + columnsMaxWidth[i]) / 2; // 2: half
438         }
439         posX = (GetWidth() - child->GetRelativeRect().GetWidth() - left - right) / 2 + left + offset; // 2: half
440     }
441 }
442 
LayoutVertical()443 void FlexLayout::LayoutVertical()
444 {
445     UIView* child = childrenHead_;
446     int16_t interval = 0;
447     int16_t posX = 0;
448     int16_t posY = 0;
449     uint16_t count = 0;
450     uint16_t heightsBuf[MAX_COUNT_DEFAULT] = {0};
451     uint16_t maxWidthsBuf[MAX_COUNT_DEFAULT] = {0};
452     uint16_t childsNumBuf[MAX_COUNT_DEFAULT] = {0};
453     uint16_t* columnsHeight = heightsBuf;
454     uint16_t* columnsMaxWidth = maxWidthsBuf;
455     uint16_t* columnsChildNum = childsNumBuf;
456     bool allocFlag = false;
457 
458     if (wrap_ == WRAP) {
459         CalColumnCount();
460         if (columnCount_ > MAX_COUNT_DEFAULT) {
461             columnsHeight = new uint16_t[columnCount_]();
462             columnsMaxWidth = new uint16_t[columnCount_]();
463             columnsChildNum = new uint16_t[columnCount_]();
464             allocFlag = true;
465         }
466         GetColumnMaxWidth(columnCount_, columnsMaxWidth);
467         GetColumnsHeight(columnCount_, columnsHeight, columnsChildNum);
468         GetColumnStartPos(posY, interval, count, columnsHeight, columnsChildNum);
469     } else {
470         GetNoWrapStartPos(GetHeight(), posY, interval);
471     }
472 
473     while (child != nullptr) {
474         if (child->IsVisible()) {
475             int16_t top = child->GetStyle(STYLE_MARGIN_TOP);
476             int16_t bottom = child->GetStyle(STYLE_MARGIN_BOTTOM);
477             posY += top;
478             if (((posY + child->GetRelativeRect().GetHeight() + bottom) > GetHeight()) && (wrap_ == WRAP)) {
479                 GetColumnStartPos(posY, interval, ++count, columnsHeight, columnsChildNum);
480                 posY += top;
481             }
482 
483             GetCrossAxisPosX(posX, count, columnsMaxWidth, child);
484             if (direction_ == LAYOUT_VER_R) {
485                 child->SetPosition(posX - child->GetStyle(STYLE_MARGIN_LEFT),
486                                    GetHeight() - posY - child->GetRelativeRect().GetHeight() - bottom);
487             } else {
488                 child->SetPosition(posX - child->GetStyle(STYLE_MARGIN_LEFT), posY - top);
489             }
490             posY += child->GetRelativeRect().GetHeight() + bottom + interval;
491             child->LayoutChildren();
492         }
493         child = child->GetNextSibling();
494     }
495 
496     if (allocFlag) {
497         delete[] columnsHeight;
498         delete[] columnsMaxWidth;
499         delete[] columnsChildNum;
500     }
501 }
502 } // namespace OHOS
503