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