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_ng/pattern/grid/grid_adaptive/grid_adaptive_layout_algorithm.h"
17
18 #include <algorithm>
19 #include <cmath>
20 #include <optional>
21 #include <type_traits>
22
23 #include "base/geometry/dimension.h"
24 #include "base/utils/utils.h"
25 #include "core/components/common/layout/constants.h"
26 #include "core/components_ng/pattern/grid/grid_item_layout_property.h"
27 #include "core/components_ng/pattern/grid/grid_layout_property.h"
28 #include "core/components_ng/pattern/image/image_layout_property.h"
29 #include "core/components_ng/property/measure_utils.h"
30
31 namespace OHOS::Ace::NG {
32
Measure(LayoutWrapper * layoutWrapper)33 void GridAdaptiveLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
34 {
35 gridLayoutInfo_.gridMatrix_.clear();
36 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
37 CHECK_NULL_VOID(gridLayoutProperty);
38 auto layoutDirection = gridLayoutProperty->GetGridDirection().value_or(FlexDirection::ROW);
39 auto axis = (layoutDirection == FlexDirection::ROW || layoutDirection == FlexDirection::ROW_REVERSE)
40 ? Axis::HORIZONTAL
41 : Axis::VERTICAL;
42 auto idealSize =
43 CreateIdealSize(gridLayoutProperty->GetLayoutConstraint().value(), axis, MeasureType::MATCH_CONTENT);
44 auto padding = gridLayoutProperty->CreatePaddingAndBorder();
45 MinusPaddingToSize(padding, idealSize);
46
47 auto firstChildWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
48 CHECK_NULL_VOID(firstChildWrapper);
49 auto layoutConstraint = gridLayoutProperty->CreateChildConstraint();
50 firstChildWrapper->Measure(layoutConstraint);
51 auto firstChildSize = firstChildWrapper->GetGeometryNode()->GetMarginFrameSize();
52
53 // Calculate grid cell size.
54 gridCellSize_ = firstChildSize;
55 auto cellLength = gridLayoutProperty->GetCellLength();
56 if (cellLength.has_value() && cellLength > 0) {
57 auto cellLengthInPx = static_cast<float>(Dimension(cellLength.value(), DimensionUnit::VP).ConvertToPx());
58 if (axis == Axis::HORIZONTAL) {
59 gridCellSize_.SetHeight(cellLengthInPx);
60 } else {
61 gridCellSize_.SetWidth(cellLengthInPx);
62 }
63 }
64
65 // Calculate cell count and display count.
66 auto minCount = std::max(gridLayoutProperty->GetMinCount().value_or(1), 1);
67 auto maxCount = std::max(gridLayoutProperty->GetMaxCount().value_or(Infinity<int32_t>()), 1);
68 if (minCount > maxCount) {
69 minCount = 1;
70 maxCount = Infinity<int32_t>();
71 }
72 auto maxSize = gridLayoutProperty->GetLayoutConstraint()->maxSize;
73 auto refWidth = idealSize.Width().has_value() ? idealSize.Width().value() : maxSize.Width();
74 auto refHeight = idealSize.Height().has_value() ? idealSize.Height().value() : maxSize.Height();
75 auto scale = gridLayoutProperty->GetLayoutConstraint()->scaleProperty;
76 auto rowsGap = ConvertToPx(gridLayoutProperty->GetRowsGap().value_or(0.0_vp), scale, refHeight).value_or(0);
77 auto columnsGap = ConvertToPx(gridLayoutProperty->GetColumnsGap().value_or(0.0_vp), scale, refWidth).value_or(0);
78 auto childrenCount = layoutWrapper->GetTotalChildCount();
79 auto mainGap = (axis == Axis::HORIZONTAL) ? columnsGap : rowsGap;
80 auto crossGap = (axis == Axis::HORIZONTAL) ? rowsGap : columnsGap;
81 mainCount_ = std::floor((idealSize.MainSize(axis).value_or(maxSize.MainSize(axis)) + mainGap) /
82 (gridCellSize_.MainSize(axis) + mainGap));
83 mainCount_ = std::clamp(mainCount_, minCount, maxCount);
84 crossCount_ = std::floor((idealSize.CrossSize(axis).value_or(Infinity<float>()) + crossGap) /
85 (gridCellSize_.CrossSize(axis) + crossGap));
86 auto maxCrossCount = std::max(static_cast<int32_t>(std::ceil(static_cast<float>(childrenCount) / mainCount_)), 1);
87 crossCount_ = std::clamp(crossCount_, 1, maxCrossCount);
88 displayCount_ = std::min(childrenCount, mainCount_ * crossCount_);
89
90 // Update frame size.
91 auto rowCount = axis == Axis::HORIZONTAL ? crossCount_ : mainCount_;
92 auto columnCount = axis == Axis::HORIZONTAL ? mainCount_ : crossCount_;
93 rowCount_ = rowCount;
94 columnCount_ = columnCount;
95 idealSize.UpdateIllegalSizeWithCheck(
96 OptionalSizeF(columnCount * gridCellSize_.Width() + (columnCount - 1) * columnsGap,
97 rowCount * gridCellSize_.Height() + (rowCount - 1) * rowsGap));
98 AddPaddingToSize(padding, idealSize);
99 layoutWrapper->GetGeometryNode()->SetFrameSize(idealSize.ConvertToSizeT());
100
101 // Create child constraint.
102 auto childLayoutConstraint = gridLayoutProperty->CreateChildConstraint();
103 childLayoutConstraint.maxSize.UpdateSizeWithCheck(gridCellSize_);
104
105 // Measure children.
106 for (int32_t index = 0; index < displayCount_; ++index) {
107 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
108 if (!childWrapper) {
109 continue;
110 }
111 childWrapper->Measure(childLayoutConstraint);
112 }
113 }
114
Layout(LayoutWrapper * layoutWrapper)115 void GridAdaptiveLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
116 {
117 CHECK_NULL_VOID(layoutWrapper);
118 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
119 CHECK_NULL_VOID(gridLayoutProperty);
120 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
121 auto padding = gridLayoutProperty->CreatePaddingAndBorder();
122 MinusPaddingToSize(padding, frameSize);
123 auto direction = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
124
125 int32_t total = layoutWrapper->GetTotalChildCount();
126 for (int32_t index = 0; index < total; ++index) {
127 if (index < displayCount_) {
128 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
129 if (!childWrapper) {
130 continue;
131 }
132 auto childOffset = CalculateChildOffset(index, layoutWrapper);
133 if (direction == TextDirection::RTL) {
134 childOffset.SetX(frameSize.CrossSize(Axis::VERTICAL) - childOffset.GetX() -
135 childWrapper->GetGeometryNode()->GetMarginFrameSize().Width() +
136 padding.left.value_or(.0f) + padding.right.value_or(.0f));
137 }
138 childWrapper->GetGeometryNode()->SetMarginFrameOffset(childOffset);
139 childWrapper->Layout();
140 } else {
141 layoutWrapper->RemoveChildInRenderTree(index);
142 }
143 }
144
145 for (const auto& mainLine : gridLayoutInfo_.gridMatrix_) {
146 int32_t itemIdex = -1;
147 for (const auto& crossLine : mainLine.second) {
148 // If item index is the same, must be the same GridItem, need't layout again.
149 if (itemIdex == crossLine.second) {
150 continue;
151 }
152 itemIdex = crossLine.second;
153 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(itemIdex);
154 if (!wrapper) {
155 continue;
156 }
157 auto layoutProperty = wrapper->GetLayoutProperty();
158 CHECK_NULL_VOID(layoutProperty);
159 auto gridItemLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutProperty);
160 CHECK_NULL_VOID(gridItemLayoutProperty);
161 gridItemLayoutProperty->UpdateMainIndex(mainLine.first);
162 gridItemLayoutProperty->UpdateCrossIndex(crossLine.first);
163 }
164 }
165 gridLayoutInfo_.crossCount_ = columnCount_;
166 gridLayoutInfo_.endIndex_ = displayCount_ - 1;
167 gridLayoutInfo_.startMainLineIndex_ = 0;
168 gridLayoutInfo_.endMainLineIndex_ = rowCount_ - 1;
169 }
170
CalculateChildOffset(int32_t index,LayoutWrapper * layoutWrapper)171 OffsetF GridAdaptiveLayoutAlgorithm::CalculateChildOffset(int32_t index, LayoutWrapper* layoutWrapper)
172 {
173 auto frameSize = layoutWrapper->GetGeometryNode()->GetMarginFrameSize();
174 auto layoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
175 CHECK_NULL_RETURN(layoutProperty, OffsetF());
176 auto padding = layoutProperty->CreatePaddingAndBorder();
177 auto layoutDirection = layoutProperty->GetGridDirection().value_or(FlexDirection::ROW);
178 auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
179 auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
180 auto columnsGap =
181 ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
182
183 int32_t rowIndex = 0;
184 int32_t columnIndex = 0;
185 switch (layoutDirection) {
186 case FlexDirection::ROW:
187 rowIndex = index / mainCount_;
188 columnIndex = index % mainCount_;
189 break;
190 case FlexDirection::ROW_REVERSE:
191 rowIndex = index / mainCount_;
192 columnIndex = mainCount_ - index % mainCount_ - 1;
193 break;
194 case FlexDirection::COLUMN:
195 columnIndex = index / mainCount_;
196 rowIndex = index % mainCount_;
197 break;
198 case FlexDirection::COLUMN_REVERSE:
199 columnIndex = index / mainCount_;
200 rowIndex = mainCount_ - index % mainCount_ - 1;
201 break;
202 default:
203 TAG_LOGI(AceLogTag::ACE_GRID, "%{public}d is not support", layoutDirection);
204 break;
205 }
206 gridLayoutInfo_.gridMatrix_[rowIndex][columnIndex] = index;
207
208 auto positionX = columnIndex * (gridCellSize_.Width() + columnsGap);
209 auto positionY = rowIndex * (gridCellSize_.Height() + rowsGap);
210 return padding.Offset() + OffsetF(positionX, positionY);
211 }
212
213 } // namespace OHOS::Ace::NG