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