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
18namespace OHOS {
19void 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
35void 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
71void 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
80void 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
89void 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
98void 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
125void 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
149void 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
185void 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
222void 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
255void 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
315void 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
339void 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
373void 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
410void 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
443void 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