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 auto gridLayoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
36 CHECK_NULL_VOID(gridLayoutProperty);
37 auto layoutDirection = gridLayoutProperty->GetGridDirection().value_or(FlexDirection::ROW);
38 auto axis = (layoutDirection == FlexDirection::ROW || layoutDirection == FlexDirection::ROW_REVERSE)
39 ? Axis::HORIZONTAL
40 : Axis::VERTICAL;
41 auto idealSize =
42 CreateIdealSize(gridLayoutProperty->GetLayoutConstraint().value(), axis, MeasureType::MATCH_CONTENT);
43 auto padding = gridLayoutProperty->CreatePaddingAndBorder();
44 MinusPaddingToSize(padding, idealSize);
45
46 auto firstChildWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
47 CHECK_NULL_VOID(firstChildWrapper);
48 auto layoutConstraint = gridLayoutProperty->CreateChildConstraint();
49 firstChildWrapper->Measure(layoutConstraint);
50 auto firstChildSize = firstChildWrapper->GetGeometryNode()->GetMarginFrameSize();
51
52 // Calculate grid cell size.
53 gridCellSize_ = firstChildSize;
54 auto cellLength = gridLayoutProperty->GetCellLength();
55 if (cellLength.has_value() && cellLength > 0) {
56 auto cellLengthInPx = static_cast<float>(Dimension(cellLength.value(), DimensionUnit::VP).ConvertToPx());
57 if (axis == Axis::HORIZONTAL) {
58 gridCellSize_.SetHeight(cellLengthInPx);
59 } else {
60 gridCellSize_.SetWidth(cellLengthInPx);
61 }
62 }
63
64 // Calculate cell count and display count.
65 auto minCount = std::max(gridLayoutProperty->GetMinCount().value_or(1), 1);
66 auto maxCount = std::max(gridLayoutProperty->GetMaxCount().value_or(Infinity<int32_t>()), 1);
67 if (minCount > maxCount) {
68 minCount = 1;
69 maxCount = Infinity<int32_t>();
70 }
71 auto maxSize = gridLayoutProperty->GetLayoutConstraint()->maxSize;
72 auto refWidth = idealSize.Width().has_value() ? idealSize.Width().value() : maxSize.Width();
73 auto refHeight = idealSize.Height().has_value() ? idealSize.Height().value() : maxSize.Height();
74 auto scale = gridLayoutProperty->GetLayoutConstraint()->scaleProperty;
75 auto rowsGap = ConvertToPx(gridLayoutProperty->GetRowsGap().value_or(0.0_vp), scale, refHeight).value_or(0);
76 auto columnsGap = ConvertToPx(gridLayoutProperty->GetColumnsGap().value_or(0.0_vp), scale, refWidth).value_or(0);
77 auto childrenCount = layoutWrapper->GetTotalChildCount();
78 auto mainGap = (axis == Axis::HORIZONTAL) ? columnsGap : rowsGap;
79 auto crossGap = (axis == Axis::HORIZONTAL) ? rowsGap : columnsGap;
80 mainCount_ = std::floor((idealSize.MainSize(axis).value_or(maxSize.MainSize(axis)) + mainGap) /
81 (gridCellSize_.MainSize(axis) + mainGap));
82 mainCount_ = std::clamp(mainCount_, minCount, maxCount);
83 crossCount_ = std::floor((idealSize.CrossSize(axis).value_or(Infinity<float>()) + crossGap) /
84 (gridCellSize_.CrossSize(axis) + crossGap));
85 auto maxCrossCount = std::max(static_cast<int32_t>(std::ceil(static_cast<float>(childrenCount) / mainCount_)), 1);
86 crossCount_ = std::clamp(crossCount_, 1, maxCrossCount);
87 displayCount_ = std::min(childrenCount, mainCount_ * crossCount_);
88 LOGI("axis: %{public}d, main count: %{public}d, cross count: %{public}d, displayCount: %{public}d, gridCellSize: "
89 "%{public}s",
90 axis, mainCount_, crossCount_, displayCount_, gridCellSize_.ToString().c_str());
91
92 // Update frame size.
93 auto rowCount = axis == Axis::HORIZONTAL ? crossCount_ : mainCount_;
94 auto columnCount = axis == Axis::HORIZONTAL ? mainCount_ : crossCount_;
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 int32_t total = layoutWrapper->GetTotalChildCount();
119 for (int32_t index = 0; index < total; ++index) {
120 if (index < displayCount_) {
121 auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(index);
122 if (!childWrapper) {
123 continue;
124 }
125 // TODO: add center position when grid item is less than ceil.
126 childWrapper->GetGeometryNode()->SetMarginFrameOffset(CalculateChildOffset(index, layoutWrapper));
127 childWrapper->Layout();
128 } else {
129 layoutWrapper->RemoveChildInRenderTree(index);
130 }
131 }
132
133 for (const auto& mainLine : gridLayoutInfo_.gridMatrix_) {
134 int32_t itemIdex = -1;
135 for (const auto& crossLine : mainLine.second) {
136 // If item index is the same, must be the same GridItem, need't layout again.
137 if (itemIdex == crossLine.second) {
138 continue;
139 }
140 itemIdex = crossLine.second;
141 auto wrapper = layoutWrapper->GetOrCreateChildByIndex(itemIdex);
142 if (!wrapper) {
143 LOGE("Layout item wrapper of index: %{public}d is null, please check.", itemIdex);
144 continue;
145 }
146 auto layoutProperty = wrapper->GetLayoutProperty();
147 CHECK_NULL_VOID(layoutProperty);
148 auto gridItemLayoutProperty = AceType::DynamicCast<GridItemLayoutProperty>(layoutProperty);
149 CHECK_NULL_VOID(gridItemLayoutProperty);
150 gridItemLayoutProperty->UpdateMainIndex(mainLine.first);
151 gridItemLayoutProperty->UpdateCrossIndex(crossLine.first);
152 }
153 }
154 }
155
CalculateChildOffset(int32_t index,LayoutWrapper * layoutWrapper) const156 OffsetF GridAdaptiveLayoutAlgorithm::CalculateChildOffset(int32_t index, LayoutWrapper* layoutWrapper) const
157 {
158 auto frameSize = layoutWrapper->GetGeometryNode()->GetMarginFrameSize();
159 auto layoutProperty = AceType::DynamicCast<GridLayoutProperty>(layoutWrapper->GetLayoutProperty());
160 CHECK_NULL_RETURN(layoutProperty, OffsetF());
161 auto padding = layoutProperty->CreatePaddingAndBorder();
162 auto layoutDirection = layoutProperty->GetGridDirection().value_or(FlexDirection::ROW);
163 auto scale = layoutProperty->GetLayoutConstraint()->scaleProperty;
164 auto rowsGap = ConvertToPx(layoutProperty->GetRowsGap().value_or(0.0_vp), scale, frameSize.Height()).value_or(0);
165 auto columnsGap =
166 ConvertToPx(layoutProperty->GetColumnsGap().value_or(0.0_vp), scale, frameSize.Width()).value_or(0);
167
168 int32_t rowIndex = 0;
169 int32_t columnIndex = 0;
170 switch (layoutDirection) {
171 case FlexDirection::ROW:
172 rowIndex = index / mainCount_;
173 columnIndex = index % mainCount_;
174 break;
175 case FlexDirection::ROW_REVERSE:
176 rowIndex = index / mainCount_;
177 columnIndex = mainCount_ - index % mainCount_ - 1;
178 break;
179 case FlexDirection::COLUMN:
180 columnIndex = index / mainCount_;
181 rowIndex = index % mainCount_;
182 break;
183 case FlexDirection::COLUMN_REVERSE:
184 columnIndex = index / mainCount_;
185 rowIndex = mainCount_ - index % mainCount_ - 1;
186 break;
187 default:
188 LOGI("%{public}d is not support", layoutDirection);
189 break;
190 }
191
192 auto positionX = columnIndex * (gridCellSize_.Width() + columnsGap);
193 auto positionY = rowIndex * (gridCellSize_.Height() + rowsGap);
194 return padding.Offset() + OffsetF(positionX, positionY);
195 }
196
197 } // namespace OHOS::Ace::NG