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