1 /*
2  * Copyright (c) 2022-2024 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/scroll/scroll_layout_algorithm.h"
17 
18 #include <algorithm>
19 
20 #include "base/geometry/axis.h"
21 #include "base/geometry/ng/offset_t.h"
22 #include "base/geometry/ng/size_t.h"
23 #include "base/log/ace_trace.h"
24 #include "base/utils/utils.h"
25 #include "core/components/common/properties/alignment.h"
26 #include "core/components_ng/pattern/scroll/scroll_layout_property.h"
27 #include "core/components_ng/pattern/scroll/scroll_pattern.h"
28 #include "core/components_ng/property/layout_constraint.h"
29 #include "core/components_ng/property/measure_property.h"
30 #include "core/components_ng/property/measure_utils.h"
31 
32 namespace OHOS::Ace::NG {
33 namespace {
34 
UpdateChildConstraint(Axis axis,const OptionalSizeF & selfIdealSize,LayoutConstraintF & contentConstraint)35 void UpdateChildConstraint(Axis axis, const OptionalSizeF& selfIdealSize, LayoutConstraintF& contentConstraint)
36 {
37     contentConstraint.parentIdealSize = selfIdealSize;
38     if (axis == Axis::VERTICAL) {
39         contentConstraint.maxSize.SetHeight(Infinity<float>());
40     } else {
41         contentConstraint.maxSize.SetWidth(Infinity<float>());
42     }
43 }
44 
45 } // namespace
46 
Measure(LayoutWrapper * layoutWrapper)47 void ScrollLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
48 {
49     auto layoutProperty = AceType::DynamicCast<ScrollLayoutProperty>(layoutWrapper->GetLayoutProperty());
50     CHECK_NULL_VOID(layoutProperty);
51     auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL);
52     auto constraint = layoutProperty->GetLayoutConstraint();
53     auto idealSize = CreateIdealSize(constraint.value(), axis, MeasureType::MATCH_CONTENT);
54     auto padding = layoutProperty->CreatePaddingAndBorder();
55     MinusPaddingToSize(padding, idealSize);
56     // Calculate child layout constraint.
57     auto childLayoutConstraint = layoutProperty->CreateChildConstraint();
58     UpdateChildConstraint(axis, idealSize, childLayoutConstraint);
59     // Measure child.
60     auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
61     auto childSize = SizeF(0.f, 0.f);
62     if (childWrapper) {
63         childWrapper->Measure(childLayoutConstraint);
64         // Use child size when self idea size of scroll is not setted.
65         childSize = childWrapper->GetGeometryNode()->GetMarginFrameSize();
66         if (!idealSize.Width()) {
67             idealSize.SetWidth(childSize.Width());
68         }
69         if (!idealSize.Height()) {
70             idealSize.SetHeight(childSize.Height());
71         }
72     }
73     AddPaddingToSize(padding, idealSize);
74     auto selfSize = idealSize.ConvertToSizeT();
75     selfSize.Constrain(constraint->minSize, constraint->maxSize);
76     auto scrollNode = layoutWrapper->GetHostNode();
77     CHECK_NULL_VOID(scrollNode);
78     auto scrollPattern = scrollNode->GetPattern<ScrollPattern>();
79     CHECK_NULL_VOID(scrollPattern);
80     if (scrollPattern->IsSelectScroll() && scrollPattern->GetHasOptionWidth()) {
81         auto selectScrollWidth = scrollPattern->GetSelectScrollWidth();
82         selfSize.SetWidth(selectScrollWidth);
83     }
84     auto lastViewSize = scrollPattern->GetViewSize();
85     auto lastViewPortExtent = scrollPattern->GetViewPortExtent();
86     if (!layoutWrapper->IsConstraintNoChanged() || lastViewSize != selfSize || lastViewPortExtent != childSize) {
87         scrollPattern->AddScrollMeasureInfo(constraint, childLayoutConstraint, selfSize, childSize);
88     }
89     layoutWrapper->GetGeometryNode()->SetFrameSize(selfSize);
90     //set initial offset
91     if (scrollPattern->NeedSetInitialOffset()) {
92         auto initialOffset = scrollPattern->GetInitialOffset();
93         if (axis == Axis::VERTICAL) {
94             auto offset = initialOffset.GetY();
95             currentOffset_ = offset.Unit() == DimensionUnit::PERCENT ?
96                              -offset.Value() * selfSize.Height() : -offset.ConvertToPx();
97         } else {
98             auto offset = initialOffset.GetX();
99             currentOffset_ = offset.Unit() == DimensionUnit::PERCENT ?
100                              -offset.Value() * selfSize.Width() : -offset.ConvertToPx();
101         }
102     }
103 }
104 
Layout(LayoutWrapper * layoutWrapper)105 void ScrollLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
106 {
107     CHECK_NULL_VOID(layoutWrapper);
108     auto layoutProperty = AceType::DynamicCast<ScrollLayoutProperty>(layoutWrapper->GetLayoutProperty());
109     CHECK_NULL_VOID(layoutProperty);
110     auto axis = layoutProperty->GetAxis().value_or(Axis::VERTICAL);
111     auto scrollNode = layoutWrapper->GetHostNode();
112     CHECK_NULL_VOID(scrollNode);
113     auto scrollPattern = AceType::DynamicCast<ScrollPattern>(scrollNode->GetPattern());
114     auto geometryNode = layoutWrapper->GetGeometryNode();
115     CHECK_NULL_VOID(geometryNode);
116     auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
117     CHECK_NULL_VOID(childWrapper);
118     auto childGeometryNode = childWrapper->GetGeometryNode();
119     CHECK_NULL_VOID(childGeometryNode);
120     auto size = geometryNode->GetFrameSize();
121     auto layoutDirection = layoutWrapper->GetLayoutProperty()->GetNonAutoLayoutDirection();
122     auto padding = layoutProperty->CreatePaddingAndBorder();
123     viewSize_ = size;
124     MinusPaddingToSize(padding, size);
125     viewPort_ = size;
126     auto childSize = childGeometryNode->GetMarginFrameSize();
127     auto contentEndOffset = layoutProperty->GetScrollContentEndOffsetValue(.0f);
128     scrollableDistance_ = GetMainAxisSize(childSize, axis) - GetMainAxisSize(viewPort_, axis) + contentEndOffset;
129     if (!scrollPattern->CanOverScroll(scrollPattern->GetScrollSource())) {
130         if (scrollableDistance_ > 0.0f) {
131             currentOffset_ = std::clamp(currentOffset_, -scrollableDistance_, 0.0f);
132         } else {
133             currentOffset_ = std::clamp(currentOffset_, 0.0f, -scrollableDistance_);
134         }
135     }
136     viewPortExtent_ = childSize;
137     viewPortLength_ = GetMainAxisSize(viewPort_, axis);
138     auto currentOffset = axis == Axis::VERTICAL ? OffsetF(0.0f, currentOffset_) : OffsetF(currentOffset_, 0.0f);
139     if (layoutDirection == TextDirection::RTL && axis == Axis::HORIZONTAL) {
140         currentOffset = OffsetF(std::min(size.Width() - childSize.Width(), 0.f) - currentOffset_, 0.0f);
141     }
142     auto scrollAlignment = Alignment::CENTER;
143     if (layoutProperty->GetPositionProperty() && layoutProperty->GetPositionProperty()->HasAlignment()) {
144         scrollAlignment = layoutProperty->GetPositionProperty()->GetAlignment().value();
145     }
146     if (layoutDirection == TextDirection::RTL) {
147         UpdateScrollAlignment(scrollAlignment);
148     }
149     auto alignmentPosition = Alignment::GetAlignPosition(size, viewPortExtent_, scrollAlignment);
150     if (GreatNotEqual(viewPortExtent_.Width(), size.Width()) && layoutDirection == TextDirection::RTL &&
151         axis == Axis::VERTICAL) {
152         alignmentPosition.SetX(size.Width() - viewPortExtent_.Width());
153     }
154     childGeometryNode->SetMarginFrameOffset(padding.Offset() + currentOffset + alignmentPosition);
155     childWrapper->Layout();
156     UpdateOverlay(layoutWrapper);
157 }
158 
UpdateOverlay(LayoutWrapper * layoutWrapper)159 void ScrollLayoutAlgorithm::UpdateOverlay(LayoutWrapper* layoutWrapper)
160 {
161     auto frameNode = layoutWrapper->GetHostNode();
162     CHECK_NULL_VOID(frameNode);
163     auto paintProperty = frameNode->GetPaintProperty<ScrollablePaintProperty>();
164     CHECK_NULL_VOID(paintProperty);
165     if (!paintProperty->GetFadingEdge().value_or(false)) {
166         return;
167     }
168     auto overlayNode = frameNode->GetOverlayNode();
169     CHECK_NULL_VOID(overlayNode);
170     auto geometryNode = frameNode->GetGeometryNode();
171     CHECK_NULL_VOID(geometryNode);
172     auto scrollFrameSize = geometryNode->GetMarginFrameSize();
173     auto overlayGeometryNode = overlayNode->GetGeometryNode();
174     CHECK_NULL_VOID(overlayGeometryNode);
175     overlayGeometryNode->SetFrameSize(scrollFrameSize);
176 }
177 
UpdateScrollAlignment(Alignment & scrollAlignment)178 void ScrollLayoutAlgorithm::UpdateScrollAlignment(Alignment& scrollAlignment)
179 {
180     if (scrollAlignment == Alignment::TOP_LEFT) {
181         scrollAlignment = Alignment::TOP_RIGHT;
182     } else if (scrollAlignment == Alignment::TOP_RIGHT) {
183         scrollAlignment = Alignment::TOP_LEFT;
184     } else if (scrollAlignment == Alignment::BOTTOM_LEFT) {
185         scrollAlignment = Alignment::BOTTOM_RIGHT;
186     } else if (scrollAlignment == Alignment::BOTTOM_RIGHT) {
187         scrollAlignment = Alignment::BOTTOM_LEFT;
188     } else if (scrollAlignment == Alignment::CENTER_RIGHT) {
189         scrollAlignment = Alignment::CENTER_LEFT;
190     } else if (scrollAlignment == Alignment::CENTER_LEFT) {
191         scrollAlignment = Alignment::CENTER_RIGHT;
192     }
193 }
194 
195 } // namespace OHOS::Ace::NG
196