1 /*
2 * Copyright (c) 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/swiper_indicator/indicator_common/arc_swiper_indicator_pattern.h"
17
18 namespace OHOS::Ace::NG {
19 namespace {
20 constexpr double QUARTER_CIRCLE_ANGLE = 90.0;
21 constexpr double HALF_CIRCLE_ANGLE = 180.0;
22 constexpr double THREE_QUARTER_CIRCLE_ANGLE = 270.0;
23 constexpr double FULL_CIRCLE_ANGLE = 360.0;
24 constexpr float ITEM_PADDING = 5.0f;
25 constexpr int32_t OFFSET_ANGLE = 5;
26 constexpr float ACTIVE_ITEM_ANGLE = 4.0f;
27 constexpr float ADD_HOT_REG_ANGLE = 8.0f;
28 constexpr Dimension CIRCLE_DIAMETER_OFFSET = 16.0_vp;
29 constexpr float DIVIDE_NUM = 0.5f; // The number 0.5 represents equal division
30 } // namespace
SetArcIndicatorHotRegion(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)31 bool ArcSwiperIndicatorPattern::SetArcIndicatorHotRegion(
32 const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
33 {
34 if (config.skipMeasure && config.skipLayout) {
35 return false;
36 }
37
38 const auto& indicatorGeometryNode = dirty->GetGeometryNode();
39 CHECK_NULL_RETURN(indicatorGeometryNode, false);
40 const auto& frameRect = indicatorGeometryNode->GetFrameRect();
41 if (NonPositive(frameRect.Width()) || NonPositive(frameRect.Height())) {
42 return false;
43 }
44 return CalculateArcIndicatorHotRegion(frameRect, indicatorGeometryNode->GetContentOffset());
45 }
46
InitAccessibilityFocusEvent()47 void ArcSwiperIndicatorPattern::InitAccessibilityFocusEvent()
48 {
49 auto host = GetHost();
50 CHECK_NULL_VOID(host);
51 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
52 CHECK_NULL_VOID(accessibilityProperty);
53 accessibilityProperty->SetOnAccessibilityFocusCallback([weak = WeakClaim(this)](bool focus) {
54 auto indicator = weak.Upgrade();
55 CHECK_NULL_VOID(indicator);
56 indicator->SetAccessibilityFocusd(focus);
57 });
58 }
59
CalculateArcIndicatorHotRegion(const RectF & frameRect,const OffsetF & contentOffset)60 bool ArcSwiperIndicatorPattern::CalculateArcIndicatorHotRegion(const RectF& frameRect, const OffsetF& contentOffset)
61 {
62 auto host = GetHost();
63 CHECK_NULL_RETURN(host, false);
64 auto gestureHub = host->GetOrCreateGestureEventHub();
65 CHECK_NULL_RETURN(gestureHub, false);
66 auto swiperNode = DynamicCast<FrameNode>(host->GetParent());
67 CHECK_NULL_RETURN(swiperNode, false);
68 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
69 CHECK_NULL_RETURN(swiperPattern, false);
70 int32_t itemCount = swiperPattern->RealTotalCount();
71 if (itemCount <= 0) {
72 return false;
73 }
74 auto allPointArcAngle = (itemCount - 1) * ITEM_PADDING + ACTIVE_ITEM_ANGLE + ADD_HOT_REG_ANGLE;
75 auto centerX = frameRect.Width() * DIVIDE_NUM + contentOffset.GetX();
76 auto centerY = frameRect.Height() * DIVIDE_NUM + contentOffset.GetY();
77 auto minEdgeLength = LessNotEqual(frameRect.Width(), frameRect.Height()) ? frameRect.Width() : frameRect.Height();
78 if (LessOrEqual(minEdgeLength, CIRCLE_DIAMETER_OFFSET.ConvertToPx())) {
79 return false;
80 }
81 auto radius = (minEdgeLength - CIRCLE_DIAMETER_OFFSET.ConvertToPx()) * DIVIDE_NUM;
82 const auto& parameter = swiperPattern->GetSwiperArcDotParameters();
83 CHECK_NULL_RETURN(parameter, false);
84 SwiperArcDirection arcDirection = parameter->arcDirection.value_or(SwiperArcDirection::SIX_CLOCK_DIRECTION);
85 std::vector<DimensionRect> responseRegion;
86 int32_t allAngleCount = static_cast<int32_t>(allPointArcAngle);
87 for (int32_t i = 0; i <= allAngleCount; i++) {
88 double angle = 0.0;
89 if (arcDirection == SwiperArcDirection::THREE_CLOCK_DIRECTION) {
90 angle = - allPointArcAngle * DIVIDE_NUM + i;
91 } else if (arcDirection == SwiperArcDirection::NINE_CLOCK_DIRECTION) {
92 angle = HALF_CIRCLE_ANGLE - allPointArcAngle * DIVIDE_NUM + i;
93 } else {
94 angle = QUARTER_CIRCLE_ANGLE - allPointArcAngle * DIVIDE_NUM + i;
95 }
96 if (LessNotEqual(angle, 0.0)) {
97 angle = FULL_CIRCLE_ANGLE + angle;
98 }
99
100 OffsetF angleOffset = CalculateAngleOffset(centerX, centerY, radius, angle);
101 Dimension width;
102 Dimension height;
103 OffsetF hotRegionOffset = CalculateRectLayout(angle, radius, angleOffset, width, height);
104
105 DimensionRect responseRect(width, height, DimensionOffset(hotRegionOffset));
106 responseRegion.emplace_back(responseRect);
107 }
108
109 gestureHub->SetResponseRegion(responseRegion);
110 return true;
111 }
112
GetCenterPointF()113 PointF ArcSwiperIndicatorPattern::GetCenterPointF()
114 {
115 auto center = PointF(0.0, 0.0);
116 auto swiperNode = GetSwiperNode();
117 CHECK_NULL_RETURN(swiperNode, center);
118 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
119 CHECK_NULL_RETURN(swiperPattern, center);
120
121 const auto& geometryNode = swiperNode->GetGeometryNode();
122 const auto& contentSize = geometryNode->GetFrameSize();
123 float centerY_ = (swiperPattern->GetDirection() == Axis::HORIZONTAL ?
124 contentSize.Height() : contentSize.Width()) * DIVIDE_NUM;
125 float centerX_ = (swiperPattern->GetDirection() == Axis::HORIZONTAL ?
126 contentSize.Width() : contentSize.Height()) * DIVIDE_NUM;
127 center = PointF(centerX_, centerY_);
128 return center;
129 }
130
GetAngleWithPoint(const PointF & conter,const PointF & point)131 float ArcSwiperIndicatorPattern::GetAngleWithPoint(const PointF& conter, const PointF& point)
132 {
133 float angle = 0.0;
134 auto swiperNode = GetSwiperNode();
135 CHECK_NULL_RETURN(swiperNode, angle);
136 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
137 CHECK_NULL_RETURN(swiperPattern, angle);
138
139 float centerX = swiperPattern->GetDirection() == Axis::HORIZONTAL ? conter.GetX() : conter.GetY();
140 float centerY = swiperPattern->GetDirection() == Axis::HORIZONTAL ? conter.GetY() : conter.GetX();
141 float pointX = swiperPattern->GetDirection() == Axis::HORIZONTAL ? point.GetX() : point.GetY();
142 float pointY = swiperPattern->GetDirection() == Axis::HORIZONTAL ? point.GetY() : point.GetX();
143 const auto& arcDotParameters = swiperPattern->GetSwiperArcDotParameters();
144 CHECK_NULL_RETURN(arcDotParameters, angle);
145 std::optional<SwiperArcDirection> swiperArcDirection = arcDotParameters->arcDirection;
146 CHECK_NULL_RETURN(swiperArcDirection.has_value(), angle);
147 auto arcDirection = swiperArcDirection.value();
148 if (NearEqual(centerY, pointY) && LessOrEqual(centerX, pointX)) {
149 angle = -QUARTER_CIRCLE_ANGLE;
150 } else if (NearEqual(centerX, pointX) && pointY > centerY) {
151 angle = 0.0;
152 } else if (NearEqual(centerY, pointY) && pointX < centerX) {
153 angle = QUARTER_CIRCLE_ANGLE;
154 } else if (NearEqual(centerX, pointX) && pointY < centerY) {
155 angle = HALF_CIRCLE_ANGLE;
156 } else if (pointX > centerX && pointY > centerY) {
157 angle = atan((pointY - centerY) / (pointX - centerX)) * HALF_CIRCLE_ANGLE / M_PI - QUARTER_CIRCLE_ANGLE;
158 } else if (pointX < centerX && pointY > centerY) {
159 angle = QUARTER_CIRCLE_ANGLE - atan((pointY - centerY) / (centerX - pointX)) * HALF_CIRCLE_ANGLE / M_PI;
160 } else if (pointX < centerX && pointY < centerY) {
161 angle = QUARTER_CIRCLE_ANGLE + atan((centerY - pointY) / (centerX - pointX)) * HALF_CIRCLE_ANGLE / M_PI;
162 } else {
163 angle = -QUARTER_CIRCLE_ANGLE - atan((centerY - pointY) / (pointX - centerX)) * HALF_CIRCLE_ANGLE / M_PI;
164 }
165
166 return ConvertAngleWithArcDirection(arcDirection, angle);
167 }
168
ConvertAngleWithArcDirection(SwiperArcDirection arcDirection,const float & angle)169 float ArcSwiperIndicatorPattern::ConvertAngleWithArcDirection(SwiperArcDirection arcDirection, const float& angle)
170 {
171 float result = 0.0;
172 if (arcDirection == SwiperArcDirection::SIX_CLOCK_DIRECTION) {
173 result = angle;
174 } else if (arcDirection == SwiperArcDirection::THREE_CLOCK_DIRECTION) {
175 if (angle > QUARTER_CIRCLE_ANGLE) {
176 result = -THREE_QUARTER_CIRCLE_ANGLE + angle;
177 } else {
178 result = angle + QUARTER_CIRCLE_ANGLE;
179 }
180 result = -result;
181 } else {
182 if (angle < -QUARTER_CIRCLE_ANGLE) {
183 result = THREE_QUARTER_CIRCLE_ANGLE + angle;
184 } else {
185 result = angle - QUARTER_CIRCLE_ANGLE;
186 }
187 }
188
189 return result;
190 }
191
CalculateCycle(float angle,float startAngle,const PointF & conter,const PointF & point)192 void ArcSwiperIndicatorPattern::CalculateCycle(float angle, float startAngle, const PointF& conter, const PointF& point)
193 {
194 if (direction_ == SwiperDirection::LEFT) {
195 if (angle < startAngle && !isUpdateCycle_ && CheckPointLocation(conter, point)) {
196 dragCycle_ += static_cast<int32_t>(FULL_CIRCLE_ANGLE);
197 isUpdateCycle_ = true;
198 } else if (angle > startAngle) {
199 isUpdateCycle_ = false;
200 }
201 }
202 if (direction_ == SwiperDirection::RIGHT) {
203 if (angle > startAngle && !isUpdateCycle_ && CheckPointLocation(conter, point)) {
204 dragCycle_ -= static_cast<int32_t>(FULL_CIRCLE_ANGLE);
205 isUpdateCycle_ = true;
206 } else if (angle < startAngle) {
207 isUpdateCycle_ = false;
208 }
209 }
210 }
211
GetEndAngle(const PointF & conter,const PointF & point,float startAngle)212 float ArcSwiperIndicatorPattern::GetEndAngle(const PointF& conter, const PointF& point, float startAngle)
213 {
214 float angle = GetAngleWithPoint(conter, point);
215 if (isUpageStartAngle_) {
216 if (angle > startAngle + OFFSET_ANGLE) {
217 direction_ = SwiperDirection::LEFT;
218 } else if (angle < startAngle - OFFSET_ANGLE) {
219 direction_ = SwiperDirection::RIGHT;
220 }
221 }
222 CalculateCycle(angle, startAngle, conter, point);
223 angle += dragCycle_;
224 if (isUpageStartAngle_ && direction_!= SwiperDirection::UNKNOWN) {
225 oldEndAngle_ = angle;
226 isUpageStartAngle_ = false;
227 }
228 if (((direction_ == SwiperDirection::LEFT) && angle < oldEndAngle_ - OFFSET_ANGLE) ||
229 ((direction_ == SwiperDirection::RIGHT) && angle > oldEndAngle_ + OFFSET_ANGLE)) {
230 float angle = GetAngleWithPoint(conter, point);
231 if ((direction_ == SwiperDirection::LEFT) && angle < startAngle) {
232 UpadateStartAngle();
233 }
234 if ((direction_ == SwiperDirection::RIGHT) && angle > startAngle) {
235 UpadateStartAngle();
236 }
237 }
238 if ((direction_ == SwiperDirection::LEFT) && (angle > oldEndAngle_)) {
239 oldEndAngle_ = angle;
240 }
241 if ((direction_ == SwiperDirection::RIGHT) && (angle < oldEndAngle_)) {
242 oldEndAngle_ = angle;
243 }
244 if (direction_ == SwiperDirection::UNKNOWN) {
245 angle = startAngle;
246 }
247 return angle;
248 }
249
UpadateStartAngle()250 void ArcSwiperIndicatorPattern::UpadateStartAngle()
251 {
252 dragCycle_ = 0;
253 direction_ = SwiperDirection::UNKNOWN;
254 isUpageStartAngle_ = true;
255 isUpdateCycle_ = false;
256 oldEndAngle_ = 0;
257 }
258
CheckPointLocation(const PointF & conter,const PointF & point)259 bool ArcSwiperIndicatorPattern::CheckPointLocation(const PointF& conter, const PointF& point)
260 {
261 auto swiperNode = GetSwiperNode();
262 CHECK_NULL_RETURN(swiperNode, false);
263 auto swiperPattern = swiperNode->GetPattern<SwiperPattern>();
264 CHECK_NULL_RETURN(swiperPattern, false);
265
266 float centerX = swiperPattern->GetDirection() == Axis::HORIZONTAL ? conter.GetX() : conter.GetY();
267 float centerY = swiperPattern->GetDirection() == Axis::HORIZONTAL ? conter.GetY() : conter.GetX();
268 float pointX = swiperPattern->GetDirection() == Axis::HORIZONTAL ? point.GetX() : point.GetY();
269 float pointY = swiperPattern->GetDirection() == Axis::HORIZONTAL ? point.GetY() : point.GetX();
270 if ((direction_ == SwiperDirection::LEFT) && pointX > centerX && pointY < centerY) {
271 return true;
272 }
273 if ((direction_ == SwiperDirection::RIGHT) && pointX < centerX && pointY < centerY) {
274 return true;
275 }
276
277 return false;
278 }
279 } // namespace OHOS::Ace::NG