• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "core/components_ng/pattern/select_overlay/select_overlay_modifier.h"
17 
18 #include "core/components/text_overlay/text_overlay_theme.h"
19 #include "core/components_ng/render/drawing.h"
20 #include "core/pipeline_ng/pipeline_context.h"
21 
22 namespace OHOS::Ace::NG {
23 namespace {
24 constexpr Dimension COORDINATE_X = 8.13_vp;
25 constexpr Dimension COORDINATE_Y = 8.13_vp;
26 constexpr Dimension MORE_ANIMATION_LINEEND_X = -8.9_vp;
27 constexpr Dimension MORE_ANIMATION_LINEEND_Y = 0.6_vp;
28 constexpr Dimension MORE_ANIMATION_OTHER_CIRCLE_X = 1.25_vp;
29 constexpr Dimension MORE_ANIMATION_OTHER_CIRCLE_Y = 8.25_vp;
30 constexpr Dimension MORE_ANIMATION_END_CIRCLE_X = 9.0_vp;
31 constexpr Dimension MORE_ANIMATION_TOP_CIRCLE_Y = -0.25_vp;
32 constexpr Dimension MASK_OFFSET_Y = 1.75_vp;
33 constexpr Dimension MASK_WIDTH = 24.0_vp;
34 constexpr Dimension MASK_HEIGHT = 10.25_vp;
35 
36 constexpr int32_t ICON_MICRO_ANIMATION_DURATION1 = 300;
37 constexpr int32_t ICON_MICRO_ANIMATION_DURATION2 = 200;
38 constexpr int32_t ROUND_NUMBER = 4;
39 constexpr int32_t FIRST_INDEX = 0;
40 constexpr int32_t SECOND_INDEX = 1;
41 constexpr int32_t THIRD_INDEX = 2;
42 constexpr int32_t FOURTH_INDEX = 3;
43 
44 constexpr float ROTATION_ANGLE = 45.0f;
45 
46 std::vector<int32_t> circle_x { -1, 0, 1, 0 };
47 std::vector<int32_t> circle_Y { 0, -1, 0, 1 };
48 } // namespace
49 
SelectOverlayModifier(const OffsetF & menuOptionOffset,bool isReverse,const WeakPtr<Pattern> & pattern)50 SelectOverlayModifier::SelectOverlayModifier(
51     const OffsetF& menuOptionOffset, bool isReverse, const WeakPtr<Pattern>& pattern)
52     : isReverse_(isReverse), pattern_(pattern)
53 {
54     pointRadius_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(Dimension(1.75_vp).ConvertToPx());
55     AttachProperty(pointRadius_);
56 
57     headPointRadius_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(Dimension(1.75_vp).ConvertToPx());
58     AttachProperty(headPointRadius_);
59 
60     menuOptionOffset_ = AceType::MakeRefPtr<PropertyOffsetF>(OffsetF());
61     AttachProperty(menuOptionOffset_);
62 
63     rotationAngle_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(isReverse_ ? -ROTATION_ANGLE : ROTATION_ANGLE);
64     AttachProperty(rotationAngle_);
65 
66     circlesAndBackArrowOpacity_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(1.0);
67     AttachProperty(circlesAndBackArrowOpacity_);
68 
69     firstHandleIsShow_ = AceType::MakeRefPtr<PropertyBool>(false);
70     AttachProperty(firstHandleIsShow_);
71 
72     secondHandleIsShow_ = AceType::MakeRefPtr<PropertyBool>(false);
73     AttachProperty(secondHandleIsShow_);
74 
75     hasExtensionMenu_ = AceType::MakeRefPtr<PropertyBool>(false);
76     AttachProperty(hasExtensionMenu_);
77 
78     SetDefaultCircleAndLineEndOffset();
79 }
80 
SetDefaultCircleAndLineEndOffset()81 void SelectOverlayModifier::SetDefaultCircleAndLineEndOffset()
82 {
83     for (int32_t i = 0; i < ROUND_NUMBER; i++) {
84         auto coordinate = OffsetF(COORDINATE_X.ConvertToPx() * circle_x[i], COORDINATE_Y.ConvertToPx() * circle_Y[i]);
85         auto circleOffset = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(coordinate);
86         auto lineEndCoordinate = coordinate;
87         auto lineEndOffset = AceType::MakeRefPtr<AnimatablePropertyOffsetF>(lineEndCoordinate);
88         circleOffset_.emplace_back(circleOffset);
89         if (i > FIRST_INDEX) {
90             if (i == THIRD_INDEX && isReverse_ && !circleOffset_.empty()) {
91                 lineEndOffset->Set(circleOffset_.front()->Get());
92             }
93             lineEndOffset_.emplace_back(lineEndOffset);
94             if (static_cast<int32_t>(lineEndOffset_.size()) > i - 1) {
95                 AttachProperty(lineEndOffset_[i - 1]);
96             }
97         }
98         if (static_cast<int32_t>(circleOffset_.size()) > i) {
99             AttachProperty(circleOffset_[i]);
100         }
101     }
102 }
103 
SetOtherPointRadius(const Dimension & radius,bool noAnimation)104 void SelectOverlayModifier::SetOtherPointRadius(const Dimension& radius, bool noAnimation)
105 {
106     if (pointRadius_) {
107         AnimationOption option = AnimationOption();
108         option.SetDuration(ICON_MICRO_ANIMATION_DURATION2);
109         option.SetCurve(Curves::FRICTION);
110         auto pattern = pattern_.Upgrade();
111         auto host = pattern ? pattern->GetHost() : nullptr;
112         auto contextPtr = host ? host->GetContextRefPtr() : nullptr;
113         AnimationUtils::Animate(
114             option, [weakPointRadius = AceType::WeakClaim(AceType::RawPtr(pointRadius_)), radius]() {
115                 auto pointRadius = weakPointRadius.Upgrade();
116                 CHECK_NULL_VOID(pointRadius);
117                 pointRadius->Set(radius.ConvertToPx());
118             }, nullptr, nullptr, contextPtr);
119     }
120 }
121 
SetHeadPointRadius(const Dimension & radius,bool noAnimation)122 void SelectOverlayModifier::SetHeadPointRadius(const Dimension& radius, bool noAnimation)
123 {
124     if (headPointRadius_) {
125         AnimationOption option = AnimationOption();
126         option.SetDuration(ICON_MICRO_ANIMATION_DURATION2);
127         option.SetCurve(Curves::FRICTION);
128         auto pattern = pattern_.Upgrade();
129         auto host = pattern ? pattern->GetHost() : nullptr;
130         auto contextPtr = host ? host->GetContextRefPtr() : nullptr;
131         AnimationUtils::Animate(
132             option, [weakHeadPointRadius = AceType::WeakClaim(AceType::RawPtr(headPointRadius_)), radius]() {
133                 auto headPointRadius = weakHeadPointRadius.Upgrade();
134                 CHECK_NULL_VOID(headPointRadius);
135                 headPointRadius->Set(radius.ConvertToPx());
136             }, nullptr, nullptr, contextPtr);
137     }
138 }
139 
SetLineEndOffset(bool isMore,bool noAnimation)140 void SelectOverlayModifier::SetLineEndOffset(bool isMore, bool noAnimation)
141 {
142     if (circleOffset_.size() < ROUND_NUMBER || lineEndOffset_.size() < ROUND_NUMBER - 1) {
143         return;
144     }
145     for (int32_t i = 0; i < ROUND_NUMBER; i++) {
146         CHECK_NULL_VOID(circleOffset_[i]);
147         if (i < ROUND_NUMBER - 1) {
148             CHECK_NULL_VOID(lineEndOffset_[i]);
149         }
150     }
151     LineEndOffsetWithAnimation(isMore, noAnimation);
152 }
153 
ChangeCircle()154 void SelectOverlayModifier::ChangeCircle()
155 {
156     CHECK_NULL_VOID(rotationAngle_);
157     if (circleOffset_.size() < ROUND_NUMBER || lineEndOffset_.size() < ROUND_NUMBER - 1) {
158         return;
159     }
160     if (!isReverse_) {
161         circleOffset_[FIRST_INDEX]->Set(
162             OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), MORE_ANIMATION_TOP_CIRCLE_Y.ConvertToPx()));
163         circleOffset_[SECOND_INDEX]->Set(
164             OffsetF(-MORE_ANIMATION_OTHER_CIRCLE_X.ConvertToPx(), -MORE_ANIMATION_OTHER_CIRCLE_Y.ConvertToPx()));
165         circleOffset_[THIRD_INDEX]->Set(OffsetF(MORE_ANIMATION_END_CIRCLE_X.ConvertToPx(), 0));
166         circleOffset_[FOURTH_INDEX]->Set(
167             OffsetF(-MORE_ANIMATION_OTHER_CIRCLE_X.ConvertToPx(), MORE_ANIMATION_OTHER_CIRCLE_Y.ConvertToPx()));
168         lineEndOffset_[FIRST_INDEX]->Set(
169             OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), -MORE_ANIMATION_LINEEND_Y.ConvertToPx()));
170         lineEndOffset_[SECOND_INDEX]->Set(
171             OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), Dimension(0, DimensionUnit::VP).ConvertToPx()));
172         lineEndOffset_[THIRD_INDEX]->Set(
173             OffsetF(MORE_ANIMATION_LINEEND_X.ConvertToPx(), MORE_ANIMATION_LINEEND_Y.ConvertToPx()));
174     } else {
175         circleOffset_[FIRST_INDEX]->Set(
176             OffsetF(-MORE_ANIMATION_END_CIRCLE_X.ConvertToPx(), Dimension(0, DimensionUnit::VP).ConvertToPx()));
177         circleOffset_[SECOND_INDEX]->Set(
178             OffsetF(MORE_ANIMATION_OTHER_CIRCLE_X.ConvertToPx(), -MORE_ANIMATION_OTHER_CIRCLE_Y.ConvertToPx()));
179         circleOffset_[THIRD_INDEX]->Set(
180             OffsetF(-MORE_ANIMATION_LINEEND_X.ConvertToPx(), MORE_ANIMATION_TOP_CIRCLE_Y.ConvertToPx()));
181         circleOffset_[FOURTH_INDEX]->Set(
182             OffsetF(MORE_ANIMATION_OTHER_CIRCLE_X.ConvertToPx(), MORE_ANIMATION_OTHER_CIRCLE_Y.ConvertToPx()));
183         // Adjust the direction of back arrow when reverse layout.
184         lineEndOffset_[FIRST_INDEX]->Set(
185             OffsetF(-MORE_ANIMATION_LINEEND_X.ConvertToPx(), -MORE_ANIMATION_LINEEND_Y.ConvertToPx()));
186         lineEndOffset_[SECOND_INDEX]->Set(
187             OffsetF(-MORE_ANIMATION_LINEEND_X.ConvertToPx(), Dimension(0, DimensionUnit::VP).ConvertToPx()));
188         lineEndOffset_[THIRD_INDEX]->Set(
189             OffsetF(-MORE_ANIMATION_LINEEND_X.ConvertToPx(), MORE_ANIMATION_LINEEND_Y.ConvertToPx()));
190     }
191     rotationAngle_->Set(0);
192 }
193 
LineEndOffsetWithAnimation(bool isMore,bool noAnimation)194 void SelectOverlayModifier::LineEndOffsetWithAnimation(bool isMore, bool noAnimation)
195 {
196     CHECK_NULL_VOID(rotationAngle_);
197     if (isMore) {
198         if (!noAnimation) {
199             AnimationOption option = AnimationOption();
200             option.SetDuration(ICON_MICRO_ANIMATION_DURATION1);
201             option.SetCurve(Curves::FRICTION);
202             auto pattern = pattern_.Upgrade();
203             auto host = pattern ? pattern->GetHost() : nullptr;
204             auto contextPtr = host ? host->GetContextRefPtr() : nullptr;
205             AnimationUtils::Animate(option, [weak = AceType::WeakClaim(this)]() {
206                 auto overlayModifier = weak.Upgrade();
207                 CHECK_NULL_VOID(overlayModifier);
208                 overlayModifier->ChangeCircle();
209             }, nullptr, nullptr, contextPtr);
210         } else {
211             ChangeCircle();
212         }
213     } else {
214         BackArrowTransitionAnimation(noAnimation);
215     }
216 }
217 
BackArrowTransitionChange(const OffsetF & coordinate,int32_t i)218 void SelectOverlayModifier::BackArrowTransitionChange(const OffsetF& coordinate, int32_t i)
219 {
220     if (static_cast<int32_t>(circleOffset_.size()) < i || static_cast<int32_t>(lineEndOffset_.size()) < i - 1) {
221         return;
222     }
223     circleOffset_[i]->Set(coordinate);
224     rotationAngle_->Set(isReverse_ ? -ROTATION_ANGLE : ROTATION_ANGLE);
225     if (i > FIRST_INDEX) {
226         if (i == THIRD_INDEX && isReverse_) {
227             auto endCircleOffset = OffsetF(COORDINATE_X.ConvertToPx() * circle_x[FIRST_INDEX],
228                 COORDINATE_Y.ConvertToPx() * circle_Y[FIRST_INDEX]);
229             lineEndOffset_[i - 1]->Set(endCircleOffset);
230             circleOffset_[FIRST_INDEX]->Set(endCircleOffset);
231             return;
232         }
233         lineEndOffset_[i - 1]->Set(coordinate);
234     };
235 }
236 
BackArrowTransitionAnimation(bool noAnimation)237 void SelectOverlayModifier::BackArrowTransitionAnimation(bool noAnimation)
238 {
239     CHECK_NULL_VOID(rotationAngle_);
240     if (!noAnimation) {
241         AnimationOption option = AnimationOption();
242         option.SetDuration(ICON_MICRO_ANIMATION_DURATION1);
243         option.SetCurve(Curves::FRICTION);
244         auto pattern = pattern_.Upgrade();
245         auto host = pattern ? pattern->GetHost() : nullptr;
246         auto contextPtr = host ? host->GetContextRefPtr() : nullptr;
247 
248         for (int32_t i = 0; i < ROUND_NUMBER; i++) {
249             auto coordinate =
250                 OffsetF(COORDINATE_X.ConvertToPx() * circle_x[i], COORDINATE_Y.ConvertToPx() * circle_Y[i]);
251             AnimationUtils::Animate(
252                 option, [weak = AceType::WeakClaim(this),
253                             weakRotationAngle = AceType::WeakClaim(AceType::RawPtr(rotationAngle_)), i, coordinate]() {
254                     auto overlayModifier = weak.Upgrade();
255                     CHECK_NULL_VOID(overlayModifier);
256                     overlayModifier->BackArrowTransitionChange(coordinate, i);
257                 }, nullptr, nullptr, contextPtr);
258         }
259     } else {
260         for (int32_t i = 0; i < ROUND_NUMBER; i++) {
261             auto coordinate =
262                 OffsetF(COORDINATE_X.ConvertToPx() * circle_x[i], COORDINATE_Y.ConvertToPx() * circle_Y[i]);
263             BackArrowTransitionChange(coordinate, i);
264         }
265     }
266 }
267 
onDraw(DrawingContext & drawingContext)268 void SelectOverlayModifier::onDraw(DrawingContext& drawingContext)
269 {
270     CHECK_NULL_VOID(hasExtensionMenu_);
271     CHECK_NULL_VOID(hasExtensionMenu_->Get());
272     for (int32_t i = 0; i < ROUND_NUMBER; i++) {
273         CHECK_NULL_VOID(circleOffset_[i]);
274         if (i < ROUND_NUMBER - 1) {
275             CHECK_NULL_VOID(lineEndOffset_[i]);
276         }
277     }
278     CHECK_NULL_VOID(rotationAngle_);
279     CHECK_NULL_VOID(menuOptionOffset_);
280     CHECK_NULL_VOID(pointRadius_);
281     CHECK_NULL_VOID(headPointRadius_);
282     CHECK_NULL_VOID(firstHandleIsShow_);
283     CHECK_NULL_VOID(secondHandleIsShow_);
284 
285     if (!isNewAvoid_ && !firstHandleIsShow_->Get() && !secondHandleIsShow_->Get()) {
286         return;
287     }
288 
289     if (Container::GreatOrEqualAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
290         return;
291     }
292 
293     auto pipeline = PipelineContext::GetCurrentContextSafelyWithCheck();
294     CHECK_NULL_VOID(pipeline);
295     auto textOverlayTheme = pipeline->GetTheme<TextOverlayTheme>();
296     CHECK_NULL_VOID(textOverlayTheme);
297     iconColor_ = textOverlayTheme->GetMoreOrBackIconColor();
298     DrawbBackArrow(drawingContext);
299     DrawbCircles(drawingContext);
300 }
301 
DrawbBackArrow(DrawingContext & drawingContext)302 void SelectOverlayModifier::DrawbBackArrow(DrawingContext& drawingContext)
303 {
304     auto& canvas = drawingContext.canvas;
305     // Draw a back arrow.
306     canvas.Save();
307     canvas.Rotate(rotationAngle_->Get(), menuOptionOffset_->Get().GetX(), menuOptionOffset_->Get().GetY());
308 
309     Color iconColor = iconColor_;
310     iconColor = iconColor.BlendOpacity(circlesAndBackArrowOpacity_->Get());
311     int32_t headPointIndex = isReverse_ ? THIRD_INDEX : FIRST_INDEX;
312     if (circleOffset_.size() < ROUND_NUMBER || lineEndOffset_.size() < ROUND_NUMBER - 1) {
313         return;
314     }
315     for (int32_t i = 0; i < ROUND_NUMBER - 2; i++) {
316         RSPen pen;
317         pen.SetAntiAlias(true);
318         pen.SetColor(iconColor.GetValue());
319         pen.SetWidth(pointRadius_->Get() * 2);
320         pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
321         canvas.AttachPen(pen);
322         int32_t targetIndex = (i + 1 == headPointIndex ? FIRST_INDEX : i + 1);
323         auto coordinate = menuOptionOffset_->Get() + circleOffset_[targetIndex]->Get();
324         auto endOffset = menuOptionOffset_->Get() + lineEndOffset_[i]->Get();
325         canvas.DrawLine({ coordinate.GetX(), coordinate.GetY() }, { endOffset.GetX(), endOffset.GetY() });
326         canvas.DetachPen();
327     }
328 
329     auto sideWidth = MASK_WIDTH.ConvertToPx();
330     auto maskOffset = menuOptionOffset_->Get() + OffsetF(-sideWidth / 2.0, MASK_OFFSET_Y.ConvertToPx());
331     RSRect clipInnerRect = RSRect(maskOffset.GetX(), maskOffset.GetY(), sideWidth + maskOffset.GetX(),
332         maskOffset.GetY() + MASK_HEIGHT.ConvertToPx());
333     canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
334     RSPen pen;
335     pen.SetAntiAlias(true);
336     pen.SetColor(iconColor.GetValue());
337     pen.SetWidth(pointRadius_->Get() * 2);
338     pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
339     canvas.AttachPen(pen);
340     auto coordinate = menuOptionOffset_->Get() + circleOffset_[3]->Get();
341     auto endOffset = menuOptionOffset_->Get() + lineEndOffset_[2]->Get();
342     canvas.DrawLine({ coordinate.GetX(), coordinate.GetY() }, { endOffset.GetX(), endOffset.GetY() });
343     canvas.DetachPen();
344     canvas.Restore();
345 }
346 
DrawbCircles(DrawingContext & drawingContext)347 void SelectOverlayModifier::DrawbCircles(DrawingContext& drawingContext)
348 {
349     auto& canvas = drawingContext.canvas;
350     // Paint other circles.
351     Color iconColor = iconColor_;
352     iconColor = iconColor.BlendOpacity(circlesAndBackArrowOpacity_->Get());
353     if (circleOffset_.size() < ROUND_NUMBER) {
354         return;
355     }
356     for (int32_t i = 0; i < ROUND_NUMBER; i++) {
357         canvas.Save();
358         canvas.Rotate(rotationAngle_->Get(), menuOptionOffset_->Get().GetX(), menuOptionOffset_->Get().GetY());
359         auto coordinate = menuOptionOffset_->Get() + circleOffset_[i]->Get();
360         canvas.Translate(coordinate.GetX(), coordinate.GetY());
361         RSBrush brush;
362         brush.SetAntiAlias(true);
363         brush.SetColor(iconColor.GetValue());
364         canvas.AttachBrush(brush);
365         // The radius UX effect of the top circle is different from other circles.
366         // the top circle is the third index when reverse layout.
367         if ((!isReverse_ && (i == FIRST_INDEX)) || (isReverse_ && (i == THIRD_INDEX))) {
368             canvas.DrawCircle({ 0.0, 0.0 }, headPointRadius_->Get());
369         } else {
370             canvas.DrawCircle({ 0.0, 0.0 }, pointRadius_->Get());
371         }
372         canvas.DetachBrush();
373         canvas.Restore();
374     }
375 }
376 } // namespace OHOS::Ace::NG
377