1 /*
2  * Copyright (c) 2022 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 "core/components_v2/grid_layout/render_grid_row.h"
17 
18 #include <utility>
19 
20 #include "core/components_v2/grid_layout/grid_container_utils.h"
21 #include "core/components_v2/grid_layout/grid_row_component.h"
22 #include "core/components_v2/grid_layout/render_grid_col.h"
23 
24 namespace OHOS::Ace::V2 {
25 namespace {
26 std::map<RefPtr<RenderGridCol>, RefPtr<RenderNode>> gridColToNodeMap;
27 
OrderComparator(const RefPtr<RenderNode> & left,const RefPtr<RenderNode> & right)28 bool OrderComparator(const RefPtr<RenderNode>& left, const RefPtr<RenderNode>& right)
29 {
30     auto leftGrid = AceType::DynamicCast<RenderGridCol>(left);
31     auto rightGrid = AceType::DynamicCast<RenderGridCol>(right);
32     if (leftGrid && rightGrid) {
33         return (leftGrid->GetOrder(leftGrid->GetSizeType()) < rightGrid->GetOrder(rightGrid->GetSizeType()));
34     }
35     return false;
36 }
37 
SortChildrenByOrder(const std::list<RefPtr<RenderNode>> & children)38 inline std::list<RefPtr<RenderNode>> SortChildrenByOrder(const std::list<RefPtr<RenderNode>>& children)
39 {
40     auto sortChildren = children;
41     sortChildren.sort(OrderComparator);
42     return sortChildren;
43 }
44 
ConvertSizeTypeToString(GridSizeType sizeType)45 std::string ConvertSizeTypeToString(GridSizeType sizeType)
46 {
47     switch (sizeType) {
48         case GridSizeType::XS:
49             return "xs";
50         case GridSizeType::SM:
51             return "sm";
52         case GridSizeType::LG:
53             return "lg";
54         case GridSizeType::MD:
55             return "md";
56         case GridSizeType::XL:
57             return "xl";
58         case GridSizeType::XXL:
59             return "xxl";
60         case GridSizeType::UNDEFINED:
61             return "undefined";
62     }
63 }
64 
65 } // namespace
66 
Create()67 RefPtr<RenderNode> RenderGridRow::Create()
68 {
69     return AceType::MakeRefPtr<RenderGridRow>();
70 }
71 
Update(const RefPtr<Component> & component)72 void RenderGridRow::Update(const RefPtr<Component>& component)
73 {
74     auto gridRowComponent = AceType::DynamicCast<GridRowComponent>(component);
75     if (!gridRowComponent) {
76         LOGE("update grid row component fail, because type unmatch");
77         return;
78     }
79     gridRowComponent_ = component;
80 }
81 
GetDirection() const82 GridRowDirection RenderGridRow::GetDirection() const
83 {
84     auto component = AceType::DynamicCast<GridRowComponent>(gridRowComponent_);
85     if (!component) {
86         return GridRowDirection::Row;
87     }
88     return component->GetDirection();
89 }
90 
GetBreakPoints() const91 RefPtr<BreakPoints> RenderGridRow::GetBreakPoints() const
92 {
93     auto component = AceType::DynamicCast<GridRowComponent>(gridRowComponent_);
94     if (!component) {
95         return nullptr;
96     }
97     return component->GetBreakPoints();
98 }
99 
GetTotalCol() const100 int32_t RenderGridRow::GetTotalCol() const
101 {
102     auto component = AceType::DynamicCast<GridRowComponent>(gridRowComponent_);
103     if (!component) {
104         return 0;
105     }
106     auto totalColumn = GridContainerUtils::ProcessColumn(currentSizeType_, component->GetTotalCol());
107     return totalColumn;
108 }
109 
GetGutter() const110 std::pair<double, double> RenderGridRow::GetGutter() const
111 {
112     auto component = AceType::DynamicCast<GridRowComponent>(gridRowComponent_);
113     if (!component) {
114         return std::make_pair<double, double>(0.0, 0.0);
115     }
116     auto gutter = GridContainerUtils::ProcessGutter(currentSizeType_, component->GetGutter());
117     return std::make_pair<double, double>(NormalizeToPx(gutter.first), NormalizeToPx(gutter.second));
118 }
119 
PerformLayout()120 void RenderGridRow::PerformLayout()
121 {
122     auto maxSize = GetLayoutParam().GetMaxSize();
123     auto component = AceType::DynamicCast<GridRowComponent>(gridRowComponent_);
124     if (!component) {
125         LOGI("Grid row fail to perform build");
126         return;
127     }
128     auto context = context_.Upgrade();
129     if (!context) {
130         LOGI("context upgrade fail perform layout fail");
131         return;
132     }
133     auto sizeType = GridContainerUtils::ProcessGridSizeType(component->GetBreakPoints(), maxSize, context);
134     if (currentSizeType_ != sizeType) {
135         auto sizeTypeString = ConvertSizeTypeToString(sizeType);
136         component->FirebreakPointEvent(sizeTypeString);
137         currentSizeType_ = sizeType;
138     }
139     auto gutter = GridContainerUtils::ProcessGutter(sizeType, component->GetGutter());
140     auto gutterInDouble = std::make_pair<double, double>(NormalizeToPx(gutter.first), NormalizeToPx(gutter.second));
141     int32_t columnNum = GridContainerUtils::ProcessColumn(sizeType, component->GetTotalCol());
142     double columnUnitWidth = GridContainerUtils::ProcessColumnWidth(gutterInDouble, columnNum, maxSize.Width());
143     LayoutEachChild(columnUnitWidth, maxSize.Height(), gutterInDouble.first, sizeType, columnNum);
144     int32_t offset = 0;
145     Offset rowPosition;
146     double currentRowHeight = 0.0;
147     double totalHeight = 0.0;
148     double lastLength = maxSize.Width();
149     for (const auto& child : gridColChildren_) {
150         auto gridColChild = AceType::DynamicCast<RenderGridCol>(child);
151         if (!gridColChild) {
152             continue;
153         }
154         auto currentChildSpan = GetGridColSpan(gridColChild, sizeType);
155         if (currentChildSpan == 0) {
156             continue;
157         }
158         currentChildSpan = std::min(columnNum, currentChildSpan);
159         NewLineOffset newLineOffset;
160         CalculateOffsetOfNewline(
161             gridColChild, currentChildSpan, columnNum - offset, columnNum, sizeType, newLineOffset);
162         if (!newLineOffset.isValid) {
163             continue;
164         }
165         if (newLineOffset.newLineCount > 0) {
166             totalHeight += currentRowHeight * newLineOffset.newLineCount + gutterInDouble.second;
167             rowPosition.SetY(
168                 rowPosition.GetY() + currentRowHeight * newLineOffset.newLineCount + gutterInDouble.second);
169             offset = newLineOffset.offset;
170             currentRowHeight = gridColToNodeMap[gridColChild]->GetLayoutSize().Height();
171             lastLength = maxSize.Width();
172         } else {
173             currentRowHeight = std::max(currentRowHeight, gridColToNodeMap[gridColChild]->GetLayoutSize().Height());
174         }
175         Offset position;
176         if (component->GetDirection() == V2::GridRowDirection::RowReverse) {
177             double childSpanPlusOffsetWidth = 0.0;
178             if (newLineOffset.newLineCount > 0) {
179                 childSpanPlusOffsetWidth = ((currentChildSpan + newLineOffset.offset) * columnUnitWidth +
180                                             ((currentChildSpan + newLineOffset.offset) - 1) * gutterInDouble.first);
181                 position.SetX(lastLength - childSpanPlusOffsetWidth);
182             } else {
183                 childSpanPlusOffsetWidth =
184                     ((currentChildSpan + GetRelativeOffset(gridColChild, sizeType)) * columnUnitWidth +
185                         ((currentChildSpan + GetRelativeOffset(gridColChild, sizeType)) - 1) * gutterInDouble.first);
186                 offset += GetRelativeOffset(gridColChild, sizeType);
187             }
188             position.SetX(lastLength - childSpanPlusOffsetWidth);
189             lastLength -= childSpanPlusOffsetWidth + gutterInDouble.first;
190         } else if (component->GetDirection() == V2::GridRowDirection::Row) {
191             if (newLineOffset.newLineCount > 0) {
192                 position.SetX(offset > 0 ? offset * columnUnitWidth + offset * gutterInDouble.first : 0);
193             } else {
194                 offset += GetRelativeOffset(gridColChild, sizeType);
195                 position.SetX(offset * (columnUnitWidth + gutterInDouble.first));
196             }
197         }
198         position.SetY(rowPosition.GetY());
199         gridColToNodeMap[gridColChild]->SetPosition(position);
200         offset += currentChildSpan;
201     }
202     totalHeight += currentRowHeight;
203     if (component->HasContainerHeight()) {
204         SetLayoutSize(Size(maxSize.Width(), maxSize.Height()));
205     } else {
206         SetLayoutSize(Size(maxSize.Width(), totalHeight));
207     }
208     gridColToNodeMap.clear();
209     gridColChildren_.clear();
210 }
211 
LayoutEachChild(double columnUnitWidth,double childHeightLimit,double xGutter,GridSizeType sizeType,int32_t columnNum)212 void RenderGridRow::LayoutEachChild(
213     double columnUnitWidth, double childHeightLimit, double xGutter, GridSizeType sizeType, int32_t columnNum)
214 {
215     auto children = GetChildren();
216     std::list<RefPtr<RenderNode>> gridColChildren;
217     for (const auto& child : children) {
218         RefPtr<RenderNode> childPtr = child;
219         FindGridColChild(childPtr);
220         auto gridCol = AceType::DynamicCast<RenderGridCol>(childPtr);
221         if (!gridCol) {
222             LOGE("Not a grid_col component");
223             continue;
224         }
225         auto span = std::min(gridCol->GetSpan(sizeType), columnNum);
226         gridCol->SetSizeType(sizeType);
227         LayoutParam childLayout(Size(columnUnitWidth * span + (span - 1) * xGutter, childHeightLimit),
228             Size(columnUnitWidth * span + (span - 1) * xGutter, 0));
229         child->Layout(childLayout);
230         gridColChildren.emplace_back(gridCol);
231         gridColToNodeMap[gridCol] = child;
232     }
233     gridColChildren_ = SortChildrenByOrder(gridColChildren);
234 }
235 
ParseNewLineForLargeOffset(int32_t childSpan,int32_t childOffset,int32_t restColumnNum,int32_t totalColumnNum,NewLineOffset & newLineOffset)236 void RenderGridRow::ParseNewLineForLargeOffset(
237     int32_t childSpan, int32_t childOffset, int32_t restColumnNum, int32_t totalColumnNum, NewLineOffset& newLineOffset)
238 {
239     int32_t totalOffsetStartFromNewLine = childOffset - restColumnNum;
240     if (totalColumnNum <= 0) {
241         return;
242     }
243     newLineOffset.newLineCount = totalOffsetStartFromNewLine / totalColumnNum + 1;
244     // ex. totalColumn 12, restColumn is 4, child offset 26, span 6. Offsets of next 2 lines are 12 and 10
245     // then the child will be placed at the third new line with offset 0.
246     if ((totalOffsetStartFromNewLine % totalColumnNum) + childSpan > totalColumnNum) {
247         newLineOffset.offset = 0;
248         ++newLineOffset.newLineCount;
249     } else {
250         // ex. totalColumn 12, restColumn is 4, child offset 20, span 6. Offsets of next 2 lines are 12 and 4
251         // then the child will be placed at the second new line with offset 4.
252         newLineOffset.offset = totalOffsetStartFromNewLine % totalColumnNum;
253     }
254 }
255 
CalculateOffsetOfNewline(const RefPtr<RenderNode> & node,int32_t currentChildSpan,int32_t restColumnNum,int32_t totalColumnNum,GridSizeType sizeType,NewLineOffset & newLineOffset) const256 void RenderGridRow::CalculateOffsetOfNewline(const RefPtr<RenderNode>& node, int32_t currentChildSpan,
257     int32_t restColumnNum, int32_t totalColumnNum, GridSizeType sizeType, NewLineOffset& newLineOffset) const
258 {
259     auto gridCol = AceType::DynamicCast<RenderGridCol>(node);
260     if (!gridCol) {
261         LOGE("Not a grid_col component");
262         return;
263     }
264     newLineOffset.isValid = true;
265     auto offset = gridCol->GetOffset(sizeType);
266     if (restColumnNum >= offset + currentChildSpan) {
267         // in this case, child can be place at current row
268         newLineOffset.offset = gridCol->GetOffset(sizeType);
269         newLineOffset.newLineCount = 0;
270         return;
271     }
272     // ex. if there are 7 columns left and chile span is 4 or 8(< or > than restColumnNum), offset is 5,
273     // child will be set on a new row with offset 0
274     if (restColumnNum >= offset) {
275         newLineOffset.offset = 0;
276         newLineOffset.newLineCount = 1;
277     } else {
278         ParseNewLineForLargeOffset(currentChildSpan, offset, restColumnNum, totalColumnNum, newLineOffset);
279     }
280 }
281 
GetRelativeOffset(const RefPtr<RenderNode> & node,GridSizeType sizeType) const282 int32_t RenderGridRow::GetRelativeOffset(const RefPtr<RenderNode>& node, GridSizeType sizeType) const
283 {
284     auto gridCol = AceType::DynamicCast<RenderGridCol>(node);
285     if (!gridCol) {
286         LOGE("Not a grid_col component");
287         return false;
288     }
289     return gridCol->GetOffset(sizeType);
290 }
291 
GetGridColSpan(const RefPtr<RenderNode> & node,GridSizeType sizeType) const292 int32_t RenderGridRow::GetGridColSpan(const RefPtr<RenderNode>& node, GridSizeType sizeType) const
293 {
294     auto gridCol = AceType::DynamicCast<RenderGridCol>(node);
295     if (!gridCol) {
296         LOGE("Not a grid_col component");
297         return 0;
298     }
299     return gridCol->GetSpan(sizeType);
300 }
301 
FindGridColChild(RefPtr<RenderNode> & gridColChild) const302 void RenderGridRow::FindGridColChild(RefPtr<RenderNode>& gridColChild) const
303 {
304     while (gridColChild) {
305         if (AceType::InstanceOf<RenderGridCol>(gridColChild)) {
306             return;
307         }
308         gridColChild = gridColChild->GetChildren().front();
309     }
310 }
311 } // namespace OHOS::Ace::V2