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