1 /*
2  * Copyright (c) 2022-2023 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 "frameworks/core/components_ng/pattern/refresh/refresh_layout_algorithm.h"
17 
18 #include "frameworks/base/utils/utils.h"
19 #include "frameworks/core/common/container.h"
20 #include "frameworks/core/components_ng/base/frame_node.h"
21 #include "frameworks/core/components_ng/pattern/refresh/refresh_layout_property.h"
22 #include "frameworks/core/components_ng/pattern/refresh/refresh_pattern.h"
23 #include "frameworks/core/components_ng/property/measure_property.h"
24 #include "frameworks/core/components_ng/property/measure_utils.h"
25 #include "frameworks/core/components_ng/property/property.h"
26 
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr Dimension TRIGGER_REFRESH_DISTANCE = 64.0_vp;
30 } // namespace
31 
32 RefreshLayoutAlgorithm::RefreshLayoutAlgorithm() = default;
33 
Measure(LayoutWrapper * layoutWrapper)34 void RefreshLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
35 {
36     CHECK_NULL_VOID(layoutWrapper);
37     auto layoutProperty = AceType::DynamicCast<NG::RefreshLayoutProperty>(layoutWrapper->GetLayoutProperty());
38     CHECK_NULL_VOID(layoutProperty);
39     auto layoutConstraint = layoutProperty->CreateChildConstraint();
40     int32_t index = 0;
41     for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
42         if (!Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
43             child->Measure(layoutConstraint);
44             ++index;
45             continue;
46         }
47         if (HasCustomBuilderIndex() && index == customBuilderIndex_.value_or(0)) {
48             auto builderLayoutConstraint = layoutConstraint;
49             bool isCustomBuilderExist = layoutProperty->GetIsCustomBuilderExistValue(true);
50             if (isCustomBuilderExist) {
51                 builderLayoutConstraint.UpdateIllegalSelfIdealSizeWithCheck(
52                     CalculateBuilderSize(child, builderLayoutConstraint, builderBaseHeight_));
53             } else {
54                 builderLayoutConstraint.minSize.SetHeight(builderBaseHeight_);
55                 builderLayoutConstraint.maxSize.SetHeight(builderBaseHeight_);
56                 builderLayoutConstraint.percentReference.SetHeight(builderBaseHeight_);
57             }
58             child->Measure(builderLayoutConstraint);
59             ++index;
60             continue;
61         }
62         child->Measure(layoutConstraint);
63         ++index;
64     }
65     PerformMeasureSelf(layoutWrapper);
66 }
67 
CalculateBuilderSize(RefPtr<LayoutWrapper> childLayoutWrapper,LayoutConstraintF & constraint,float customBaseHeight)68 OptionalSizeF RefreshLayoutAlgorithm::CalculateBuilderSize(
69     RefPtr<LayoutWrapper> childLayoutWrapper, LayoutConstraintF& constraint, float customBaseHeight)
70 {
71     OptionalSizeF defaultSize;
72     CHECK_NULL_RETURN(childLayoutWrapper, defaultSize);
73     auto layoutProperty = childLayoutWrapper->GetLayoutProperty();
74     CHECK_NULL_RETURN(layoutProperty, defaultSize);
75     std::optional<CalcLength> width = std::nullopt;
76     auto&& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
77     if (!calcLayoutConstraint) {
78         return defaultSize;
79     }
80     std::optional<float> reSetHeight = customBaseHeight;
81     if (calcLayoutConstraint->selfIdealSize.has_value() &&
82         calcLayoutConstraint->selfIdealSize.value().Height().has_value()) {
83         reSetHeight = ConvertToPx(
84             calcLayoutConstraint->selfIdealSize.value().Height().value(), constraint.scaleProperty, customBaseHeight)
85                           .value_or(-1.0f);
86     }
87     if (calcLayoutConstraint->selfIdealSize.has_value()) {
88         width = calcLayoutConstraint->selfIdealSize->Width();
89     }
90     auto reSetWidth = ConvertToPx(width, constraint.scaleProperty, constraint.percentReference.Width());
91     return { reSetWidth, reSetHeight };
92 }
93 
Layout(LayoutWrapper * layoutWrapper)94 void RefreshLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
95 {
96     PerformLayout(layoutWrapper);
97     auto layoutProperty = AceType::DynamicCast<NG::RefreshLayoutProperty>(layoutWrapper->GetLayoutProperty());
98     CHECK_NULL_VOID(layoutProperty);
99 
100     for (auto&& child : layoutWrapper->GetAllChildrenWithBuild()) {
101         if (!child) {
102             continue;
103         }
104         child->Layout();
105     }
106 }
107 // Called to perform layout render node and child.
PerformLayout(LayoutWrapper * layoutWrapper)108 void RefreshLayoutAlgorithm::PerformLayout(LayoutWrapper* layoutWrapper)
109 {
110     CHECK_NULL_VOID(layoutWrapper);
111     auto layoutProperty = AceType::DynamicCast<NG::RefreshLayoutProperty>(layoutWrapper->GetLayoutProperty());
112     CHECK_NULL_VOID(layoutProperty);
113     // update child position.
114     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
115     const auto& padding = layoutProperty->CreatePaddingAndBorder();
116     MinusPaddingToSize(padding, size);
117     auto left = padding.left.value_or(0);
118     auto top = padding.top.value_or(0);
119     auto paddingOffset = OffsetF(left, top);
120     auto align = Alignment::TOP_CENTER;
121     auto host = layoutWrapper->GetHostNode();
122     CHECK_NULL_VOID(host);
123     auto pattern = host->GetPattern<RefreshPattern>();
124     CHECK_NULL_VOID(pattern);
125     // Update child position.
126     // if customBuilder exist, customBuilder is first child
127     int32_t index = 0;
128     float customBuilderHeight = 0.0f;
129     for (const auto& child : layoutWrapper->GetAllChildrenWithBuild()) {
130         if (!child) {
131             index++;
132             continue;
133         }
134         auto paddingOffsetChild = paddingOffset;
135         auto alignChild = align;
136         if (!HasCustomBuilderIndex()) {
137             if (index == layoutWrapper->GetTotalChildCount() - 1) {
138                 alignChild = Alignment::TOP_CENTER;
139             }
140         } else {
141             if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
142                 auto builderChild = layoutWrapper->GetOrCreateChildByIndex(customBuilderIndex_.value_or(0));
143                 CHECK_NULL_VOID(builderChild);
144                 auto geometryNode = builderChild->GetGeometryNode();
145                 CHECK_NULL_VOID(geometryNode);
146                 auto builderHeight = geometryNode->GetMarginFrameSize().Height();
147                 alignChild = Alignment::TOP_CENTER;
148                 if (index == customBuilderIndex_.value_or(0)) {
149                     auto builderOffset =
150                         NearEqual(builderHeight, builderBaseHeight_) ? 0.0f : (builderBaseHeight_ - builderHeight);
151                     paddingOffsetChild += OffsetF(0.0f, builderOffset);
152                 } else {
153                     auto scrollOffset =
154                         NearEqual(builderHeight, builderBaseHeight_) ? builderHeight : builderBaseHeight_;
155                     paddingOffsetChild += OffsetF(0.0f, scrollOffset);
156                 }
157                 auto translate =
158                     Alignment::GetAlignPosition(size, child->GetGeometryNode()->GetMarginFrameSize(), alignChild) +
159                     paddingOffsetChild;
160                 child->GetGeometryNode()->SetMarginFrameOffset(translate);
161                 index++;
162                 continue;
163             }
164             if (index == customBuilderIndex_.value_or(0)) {
165                 alignChild = Alignment::TOP_CENTER;
166                 paddingOffsetChild += OffsetF(0.0f, customBuilderOffset_);
167                 auto geometryNode = child->GetGeometryNode();
168                 CHECK_NULL_VOID(geometryNode);
169                 customBuilderHeight = geometryNode->GetMarginFrameSize().Height();
170             } else {
171                 auto refreshingProp = layoutProperty->GetIsRefreshing().value_or(false);
172                 if (refreshingProp) {
173                     auto distance = static_cast<float>(TRIGGER_REFRESH_DISTANCE.ConvertToPx());
174                     auto refreshingPosition = Positive(customBuilderHeight) ? distance + customBuilderHeight : 0.0f;
175                     paddingOffsetChild += OffsetF(0.0f, refreshingPosition);
176                 } else {
177                     paddingOffsetChild += OffsetF(0.0f, scrollOffset_);
178                 }
179             }
180         }
181         auto translate = Alignment::GetAlignPosition(size, child->GetGeometryNode()->GetMarginFrameSize(), alignChild) +
182                          paddingOffsetChild;
183         child->GetGeometryNode()->SetMarginFrameOffset(translate);
184         index++;
185     }
186 }
187 
188 } // namespace OHOS::Ace::NG
189