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