• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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