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