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