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/slider/slider_tip_modifier.h"
17 
18 #include "base/geometry/ng/offset_t.h"
19 #include "base/i18n/localization.h"
20 #include "bridge/common/utils/utils.h"
21 #include "core/common/font_manager.h"
22 #include "core/components/common/layout/grid_system_manager.h"
23 #include "core/components/slider/slider_theme.h"
24 #include "core/components_ng/pattern/text/text_styles.h"
25 #include "core/components_ng/render/drawing_prop_convertor.h"
26 
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr Dimension CIRCULAR_HORIZON_OFFSET = 13.86_vp;
30 constexpr Dimension ARROW_HORIZON_OFFSET = 1.5_vp;
31 constexpr Dimension ARROW_VERTICAL_OFFSET = 0.68_vp;
32 constexpr Dimension ARROW_RADIUS = 2.0_vp;
33 constexpr Dimension ARROW_HEIGHT = 8.0_vp;
34 constexpr Dimension ARROW_WIDTH = 16.0_vp;
35 constexpr float HALF = 0.5f;
36 
37 constexpr float BUBBLE_SIZE_MIN_SCALE = 0.6f;
38 constexpr float BUBBLE_SIZE_MAX_SCALE = 1.0f;
39 constexpr float BUBBLE_OPACITY_MIN_SCALE = 0.0f;
40 constexpr float BUBBLE_OPACITY_MAX_SCALE = 1.0f;
41 constexpr int32_t BUBBLE_DISPLAY_SIZE_CHANGE_TIMER = 250;
42 constexpr int32_t BUBBLE_DISPLAY_OPACITY_CHANGE_TIMER = 150;
43 constexpr int32_t BUBBLE_DISAPPEAR_SIZE_CHANGE_TIMER = 250;
44 constexpr int32_t BUBBLE_DISAPPEAR_OPACITY_CHANGE_TIMER = 250;
45 constexpr int32_t BUBBLE_DISAPPEAR_DELAY_TIMER = 2000;
46 constexpr Dimension BUBBLE_VERTICAL_WIDTH = 62.0_vp;
47 constexpr Dimension BUBBLE_VERTICAL_HEIGHT = 32.0_vp;
48 constexpr Dimension BUBBLE_HORIZONTAL_WIDTH = 48.0_vp;
49 constexpr Dimension BUBBLE_HORIZONTAL_HEIGHT = 40.0_vp;
50 constexpr Dimension BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_1_WIDTH = 92.0_vp;
51 constexpr Dimension BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_1_HEIGHT = 52.0_vp;
52 constexpr Dimension BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_1_WIDTH = 48.0_vp;
53 constexpr Dimension BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_1_HEIGHT = 60.0_vp;
54 constexpr Dimension BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_2_WIDTH = 96.0_vp;
55 constexpr Dimension BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_2_HEIGHT = 56.0_vp;
56 constexpr Dimension BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_2_WIDTH = 48.0_vp;
57 constexpr Dimension BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_2_HEIGHT = 64.0_vp;
58 constexpr Dimension TEXT_MAX = 36.0_vp;
59 constexpr Dimension TEXT_AGING_MAX = 72.0_vp;
60 constexpr int32_t MAX_LENGTH = 1;
61 constexpr float SUITABLEAGING_LEVEL_1_SCALE = 1.75f;
62 constexpr float SUITABLEAGING_LEVEL_2_SCALE = 2.0f;
63 constexpr Dimension SUITABLEAGING_LEVEL_1_TEXT_FONT_SIZE = 25.0_vp;
64 constexpr Dimension SUITABLEAGING_LEVEL_2_TEXT_FONT_SIZE = 28.0_vp;
65 
66 } // namespace
67 
SliderTipModifier(std::function<std::pair<OffsetF,float> ()> getBubbleVertexFunc)68 SliderTipModifier::SliderTipModifier(std::function<std::pair<OffsetF, float>()> getBubbleVertexFunc)
69     : tipFlag_(AceType::MakeRefPtr<PropertyBool>(false)),
70       contentOffset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
71       contentSize_(AceType::MakeRefPtr<PropertySizeF>(SizeF())),
72       sizeScale_(AceType::MakeRefPtr<AnimatablePropertyFloat>(BUBBLE_SIZE_MIN_SCALE)),
73       opacityScale_(AceType::MakeRefPtr<AnimatablePropertyFloat>(BUBBLE_OPACITY_MIN_SCALE)),
74       content_(AceType::MakeRefPtr<PropertyString>("")), bubbleVertex_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
75       sliderGlobalOffset_(AceType::MakeRefPtr<PropertyOffsetF>(OffsetF())),
76       getBubbleVertexFunc_(std::move(getBubbleVertexFunc))
77 {
78     AttachProperty(tipFlag_);
79     AttachProperty(contentOffset_);
80     AttachProperty(sizeScale_);
81     AttachProperty(opacityScale_);
82     AttachProperty(content_);
83     AttachProperty(bubbleVertex_);
84     AttachProperty(sliderGlobalOffset_);
85 }
86 
~SliderTipModifier()87 SliderTipModifier::~SliderTipModifier() {}
88 
PaintTip(DrawingContext & context)89 void SliderTipModifier::PaintTip(DrawingContext& context)
90 {
91     PaintBubble(context);
92     CHECK_NULL_VOID(paragraph_);
93     PaintText(context);
94     context.canvas.Restore();
95 }
96 
PaintText(DrawingContext & context)97 void SliderTipModifier::PaintText(DrawingContext& context)
98 {
99     auto arrowSizeWidth = static_cast<float>(ARROW_WIDTH.ConvertToPx());
100     auto arrowSizeHeight = static_cast<float>(ARROW_HEIGHT.ConvertToPx());
101     auto circularOffset = static_cast<float>(CIRCULAR_HORIZON_OFFSET.ConvertToPx());
102     auto pipeLine = PipelineBase::GetCurrentContextSafely();
103     CHECK_NULL_VOID(pipeLine);
104     auto fontScale = pipeLine->GetFontScale();
105     SizeF textSize = { 0, 0 };
106     if (paragraph_) {
107         auto width = static_cast<float>(TEXT_MAX.ConvertToPx());
108         if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) ||
109             (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE))) {
110             width = static_cast<float>(TEXT_AGING_MAX.ConvertToPx());
111         }
112         textSize = SizeF(std::min(paragraph_->GetLongestLine(), width), paragraph_->GetHeight());
113     }
114     if (axis_ == Axis::HORIZONTAL) {
115         textOffset_.SetX(vertex_.GetX() - textSize.Width() * HALF);
116         if (isMask_) {
117             textOffset_.SetY(vertex_.GetY() + (bubbleSize_.Height() - textSize.Height() + arrowSizeHeight) * HALF);
118         } else {
119             textOffset_.SetY(vertex_.GetY() - (bubbleSize_.Height() + textSize.Height() + arrowSizeHeight) * HALF);
120         }
121     } else {
122         textOffset_.SetY(vertex_.GetY() - textSize.Height() * HALF);
123         if (isMask_) {
124             textOffset_.SetX(
125                 vertex_.GetX() +
126                 (bubbleSize_.Width() - textSize.Width() + arrowSizeHeight + circularOffset - arrowSizeWidth) * HALF);
127         } else {
128             textOffset_.SetX(
129                 vertex_.GetX() -
130                 (bubbleSize_.Width() + textSize.Width() + arrowSizeHeight + circularOffset - arrowSizeWidth) * HALF);
131         }
132     }
133     paragraph_->Paint(context.canvas, textOffset_.GetX(), textOffset_.GetY());
134 }
135 
PaintHorizontalBubble(float vertexOffsetFromBlock,RSPath & path)136 void SliderTipModifier::PaintHorizontalBubble(float vertexOffsetFromBlock, RSPath& path)
137 {
138     float arrowRadius = static_cast<float>(ARROW_RADIUS.ConvertToPx());
139     auto arrowSizeWidth = static_cast<float>(ARROW_WIDTH.ConvertToPx());
140     auto arrowSizeHeight = static_cast<float>(ARROW_HEIGHT.ConvertToPx());
141     auto arrowHorizonOffset = static_cast<float>(ARROW_HORIZON_OFFSET.ConvertToPx());
142     auto arrowVerticalOffset = static_cast<float>(ARROW_VERTICAL_OFFSET.ConvertToPx());
143     float circularRadius = (bubbleSize_.Height() - arrowSizeHeight) * HALF;
144     if (sliderGlobalOffset_->Get().GetY() + vertex_.GetY() < bubbleSize_.Height()) {
145         vertex_.AddY(vertexOffsetFromBlock / HALF);
146         isMask_ = true;
147         path.MoveTo(vertex_.GetX(), vertex_.GetY());
148         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() + arrowHorizonOffset,
149             vertex_.GetY() + arrowVerticalOffset);
150         path.LineTo(vertex_.GetX() + arrowSizeWidth * HALF, vertex_.GetY() + arrowSizeHeight);
151         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
152             vertex_.GetX() + arrowSizeWidth * HALF, vertex_.GetY() + bubbleSize_.Height());
153         path.LineTo(vertex_.GetX() - arrowSizeWidth * HALF, vertex_.GetY() + bubbleSize_.Height());
154         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
155             vertex_.GetX() - arrowSizeWidth * HALF, vertex_.GetY() + arrowSizeHeight);
156         path.LineTo(vertex_.GetX() - arrowHorizonOffset * HALF, vertex_.GetY() + arrowVerticalOffset);
157     } else {
158         isMask_ = false;
159         path.MoveTo(vertex_.GetX(), vertex_.GetY());
160         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() - arrowHorizonOffset,
161             vertex_.GetY() - arrowVerticalOffset);
162         path.LineTo(vertex_.GetX() - arrowSizeWidth * HALF, vertex_.GetY() - arrowSizeHeight);
163         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
164             vertex_.GetX() - arrowSizeWidth * HALF, vertex_.GetY() - bubbleSize_.Height());
165         path.LineTo(vertex_.GetX() + arrowSizeWidth * HALF, vertex_.GetY() - bubbleSize_.Height());
166         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
167             vertex_.GetX() + arrowSizeWidth * HALF, vertex_.GetY() - arrowSizeHeight);
168         path.LineTo(vertex_.GetX() + arrowHorizonOffset * HALF, vertex_.GetY() - arrowVerticalOffset);
169     }
170     path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX(), vertex_.GetY());
171 }
172 
PaintVerticalBubble(float vertexOffsetFromBlock,RSPath & path)173 void SliderTipModifier::PaintVerticalBubble(float vertexOffsetFromBlock, RSPath& path)
174 {
175     auto arrowWidth = static_cast<float>(ARROW_WIDTH.ConvertToPx());
176     auto arrowHeight = static_cast<float>(ARROW_HEIGHT.ConvertToPx());
177     auto circularOffset = static_cast<float>(CIRCULAR_HORIZON_OFFSET.ConvertToPx());
178     auto arrowHorizonOffset = static_cast<float>(ARROW_HORIZON_OFFSET.ConvertToPx());
179     auto arrowVerticalOffset = static_cast<float>(ARROW_VERTICAL_OFFSET.ConvertToPx());
180     float arrowRadius = static_cast<float>(ARROW_RADIUS.ConvertToPx());
181     float circularRadius = bubbleSize_.Height() * HALF;
182     if (sliderGlobalOffset_->Get().GetX() + vertex_.GetX() < bubbleSize_.Width() ||
183         AceApplicationInfo::GetInstance().IsRightToLeft()) {
184         vertex_.AddX(vertexOffsetFromBlock / HALF);
185         isMask_ = true;
186         path.MoveTo(vertex_.GetX(), vertex_.GetY());
187         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() + arrowVerticalOffset,
188             vertex_.GetY() - arrowHorizonOffset);
189         path.LineTo(vertex_.GetX() + arrowHeight, vertex_.GetY() - arrowWidth * HALF);
190         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
191             vertex_.GetX() + arrowHeight + circularOffset, vertex_.GetY() - bubbleSize_.Height() * HALF);
192         path.LineTo(vertex_.GetX() + bubbleSize_.Width() - bubbleSize_.Height() * HALF,
193             vertex_.GetY() - bubbleSize_.Height() * HALF);
194         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
195             vertex_.GetX() + bubbleSize_.Width() - bubbleSize_.Height() * HALF,
196             vertex_.GetY() + bubbleSize_.Height() * HALF);
197         path.LineTo(vertex_.GetX() + arrowHeight + circularOffset, vertex_.GetY() + bubbleSize_.Height() * HALF);
198         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() + arrowHeight,
199             vertex_.GetY() + arrowWidth * HALF);
200         path.LineTo(vertex_.GetX() + arrowVerticalOffset, vertex_.GetY() + arrowHorizonOffset);
201     } else {
202         isMask_ = false;
203         path.MoveTo(vertex_.GetX(), vertex_.GetY());
204         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() - arrowVerticalOffset,
205             vertex_.GetY() + arrowHorizonOffset);
206         path.LineTo(vertex_.GetX() - arrowHeight, vertex_.GetY() + arrowWidth * HALF);
207         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
208             vertex_.GetX() - arrowHeight - circularOffset, vertex_.GetY() + bubbleSize_.Height() * HALF);
209         path.LineTo(vertex_.GetX() - bubbleSize_.Width() + bubbleSize_.Height() * HALF,
210             vertex_.GetY() + bubbleSize_.Height() * HALF);
211         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
212             vertex_.GetX() - bubbleSize_.Width() + bubbleSize_.Height() * HALF,
213             vertex_.GetY() - bubbleSize_.Height() * HALF);
214         path.LineTo(vertex_.GetX() - arrowHeight - circularOffset, vertex_.GetY() - bubbleSize_.Height() * HALF);
215         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() - arrowHeight,
216             vertex_.GetY() - arrowWidth * HALF);
217         path.LineTo(vertex_.GetX() - arrowVerticalOffset, vertex_.GetY() - arrowHorizonOffset);
218     }
219     path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX(), vertex_.GetY());
220 }
221 
PaintHorizontalBubbleSuitableAging(float vertexOffsetFromBlock,RSPath & path)222 void SliderTipModifier::PaintHorizontalBubbleSuitableAging(float vertexOffsetFromBlock, RSPath& path)
223 {
224     float arrowRadius = static_cast<float>(ARROW_RADIUS.ConvertToPx());
225     auto arrowSizeWidth = static_cast<float>(ARROW_WIDTH.ConvertToPx());
226     auto arrowSizeHeight = static_cast<float>(ARROW_HEIGHT.ConvertToPx());
227     auto arrowHorizonOffset = static_cast<float>(ARROW_HORIZON_OFFSET.ConvertToPx());
228     auto arrowVerticalOffset = static_cast<float>(ARROW_VERTICAL_OFFSET.ConvertToPx());
229     float circularRadius = (bubbleSize_.Height() - arrowSizeHeight) * (1.0f / 3.0f);
230     if (sliderGlobalOffset_->Get().GetY() + vertex_.GetY() < bubbleSize_.Height()) {
231         vertex_.AddY(vertexOffsetFromBlock / HALF);
232         isMask_ = true;
233         path.MoveTo(vertex_.GetX(), vertex_.GetY());
234         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() + arrowHorizonOffset,
235             vertex_.GetY() + arrowVerticalOffset);
236         path.LineTo(vertex_.GetX() + arrowSizeWidth * HALF, vertex_.GetY() + arrowSizeHeight);
237         path.LineTo(vertex_.GetX() + bubbleSize_.Width() * HALF, vertex_.GetY() + arrowSizeHeight);
238         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
239             vertex_.GetX() + bubbleSize_.Width() * HALF + circularRadius,
240             vertex_.GetY() + arrowSizeHeight + (bubbleSize_.Height() - arrowSizeHeight) * (1.0f / 3.0f));
241         path.LineTo(vertex_.GetX() + bubbleSize_.Width() * HALF + circularRadius,
242             vertex_.GetY() + arrowSizeHeight + (bubbleSize_.Height() - arrowSizeHeight) * (2.0f / 3.0f));
243         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
244             vertex_.GetX() + bubbleSize_.Width() * HALF, vertex_.GetY() + bubbleSize_.Height());
245         path.LineTo(vertex_.GetX() - bubbleSize_.Width() * HALF, vertex_.GetY() + bubbleSize_.Height());
246         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
247             vertex_.GetX() - bubbleSize_.Width() * HALF - circularRadius,
248             vertex_.GetY() + arrowSizeHeight + (bubbleSize_.Height() - arrowSizeHeight) * (2.0f / 3.0f));
249         path.LineTo(vertex_.GetX() - bubbleSize_.Width() * HALF - circularRadius,
250             vertex_.GetY() + arrowSizeHeight + (bubbleSize_.Height() - arrowSizeHeight) * (1.0f / 3.0f));
251         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
252             vertex_.GetX() - bubbleSize_.Width() * HALF, vertex_.GetY() + arrowSizeHeight);
253         path.LineTo(vertex_.GetX() - arrowSizeWidth * HALF, vertex_.GetY() + arrowSizeHeight);
254         path.LineTo(vertex_.GetX() - arrowHorizonOffset * HALF, vertex_.GetY() + arrowVerticalOffset);
255     } else {
256         isMask_ = false;
257         path.MoveTo(vertex_.GetX(), vertex_.GetY());
258         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() - arrowHorizonOffset,
259             vertex_.GetY() - arrowVerticalOffset);
260         path.LineTo(vertex_.GetX() - arrowSizeWidth * HALF, vertex_.GetY() - arrowSizeHeight);
261         path.LineTo(vertex_.GetX() - bubbleSize_.Width() * HALF, vertex_.GetY() - arrowSizeHeight);
262         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
263             vertex_.GetX() - bubbleSize_.Width() * HALF - circularRadius,
264             vertex_.GetY() - arrowSizeHeight - (bubbleSize_.Height() - arrowSizeHeight) * (1.0f / 3.0f));
265         path.LineTo(vertex_.GetX() - bubbleSize_.Width() * HALF - circularRadius,
266             vertex_.GetY() - arrowSizeHeight - (bubbleSize_.Height() - arrowSizeHeight) * (2.0f / 3.0f));
267         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
268             vertex_.GetX() - bubbleSize_.Width() * HALF, vertex_.GetY() - bubbleSize_.Height());
269         path.LineTo(vertex_.GetX() + bubbleSize_.Width() * HALF, vertex_.GetY() - bubbleSize_.Height());
270         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
271             vertex_.GetX() + bubbleSize_.Width() * HALF + circularRadius,
272             vertex_.GetY() - arrowSizeHeight - (bubbleSize_.Height() - arrowSizeHeight) * (2.0f / 3.0f));
273         path.LineTo(vertex_.GetX() + bubbleSize_.Width() * HALF + circularRadius,
274             vertex_.GetY() - arrowSizeHeight - (bubbleSize_.Height() - arrowSizeHeight) * (1.0f / 3.0f));
275         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
276             vertex_.GetX() + bubbleSize_.Width() * HALF, vertex_.GetY() - arrowSizeHeight);
277         path.LineTo(vertex_.GetX() + arrowSizeWidth * HALF, vertex_.GetY() - arrowSizeHeight);
278         path.LineTo(vertex_.GetX() + arrowHorizonOffset * HALF, vertex_.GetY() - arrowVerticalOffset);
279     }
280     path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX(), vertex_.GetY());
281 }
282 
PaintVerticalBubbleSuitableAging(float vertexOffsetFromBlock,RSPath & path)283 void SliderTipModifier::PaintVerticalBubbleSuitableAging(float vertexOffsetFromBlock, RSPath& path)
284 {
285     auto arrowWidth = static_cast<float>(ARROW_WIDTH.ConvertToPx());
286     auto arrowHeight = static_cast<float>(ARROW_HEIGHT.ConvertToPx());
287     auto arrowHorizonOffset = static_cast<float>(ARROW_HORIZON_OFFSET.ConvertToPx());
288     auto arrowVerticalOffset = static_cast<float>(ARROW_VERTICAL_OFFSET.ConvertToPx());
289     float arrowRadius = static_cast<float>(ARROW_RADIUS.ConvertToPx());
290     float circularRadius = bubbleSize_.Height() * (1.0f / 3.0f);
291     if (sliderGlobalOffset_->Get().GetX() + vertex_.GetX() < bubbleSize_.Width() ||
292         AceApplicationInfo::GetInstance().IsRightToLeft()) {
293         vertex_.AddX(vertexOffsetFromBlock / HALF);
294         isMask_ = true;
295         path.MoveTo(vertex_.GetX(), vertex_.GetY());
296         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() + arrowVerticalOffset,
297             vertex_.GetY() - arrowHorizonOffset);
298         path.LineTo(vertex_.GetX() + arrowHeight, vertex_.GetY() - arrowWidth * HALF);
299         path.LineTo(vertex_.GetX() + arrowHeight, vertex_.GetY() - bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
300         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
301             vertex_.GetX() + arrowHeight + circularRadius, vertex_.GetY() - bubbleSize_.Height() * HALF);
302         path.LineTo(
303             vertex_.GetX() + bubbleSize_.Width() - circularRadius, vertex_.GetY() - bubbleSize_.Height() * HALF);
304         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
305             vertex_.GetX() + bubbleSize_.Width(), vertex_.GetY() - bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
306         path.LineTo(vertex_.GetX() + bubbleSize_.Width(), vertex_.GetY() + bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
307         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
308             vertex_.GetX() + bubbleSize_.Width() - circularRadius, vertex_.GetY() + bubbleSize_.Height() * HALF);
309         path.LineTo(vertex_.GetX() + arrowHeight + circularRadius, vertex_.GetY() + bubbleSize_.Height() * HALF);
310         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() + arrowHeight,
311             vertex_.GetY() + bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
312         path.LineTo(vertex_.GetX() + arrowHeight, vertex_.GetY() + arrowWidth * HALF);
313         path.LineTo(vertex_.GetX() + arrowVerticalOffset, vertex_.GetY() + arrowHorizonOffset);
314     } else {
315         isMask_ = false;
316         path.MoveTo(vertex_.GetX(), vertex_.GetY());
317         path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() - arrowVerticalOffset,
318             vertex_.GetY() + arrowHorizonOffset);
319         path.LineTo(vertex_.GetX() - arrowHeight, vertex_.GetY() + arrowWidth * HALF);
320         path.LineTo(vertex_.GetX() - arrowHeight, vertex_.GetY() + bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
321         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
322             vertex_.GetX() - arrowHeight - circularRadius, vertex_.GetY() + bubbleSize_.Height() * HALF);
323         path.LineTo(
324             vertex_.GetX() - bubbleSize_.Width() + circularRadius, vertex_.GetY() + bubbleSize_.Height() * HALF);
325         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
326             vertex_.GetX() - bubbleSize_.Width(), vertex_.GetY() + bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
327         path.LineTo(vertex_.GetX() - bubbleSize_.Width(), vertex_.GetY() - bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
328         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION,
329             vertex_.GetX() - bubbleSize_.Width() + circularRadius, vertex_.GetY() - bubbleSize_.Height() * HALF);
330         path.LineTo(vertex_.GetX() - arrowHeight - circularRadius, vertex_.GetY() - bubbleSize_.Height() * HALF);
331         path.ArcTo(circularRadius, circularRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX() - arrowHeight,
332             vertex_.GetY() - bubbleSize_.Height() * (1.0f / 3.0f) * HALF);
333         path.LineTo(vertex_.GetX() - arrowHeight, vertex_.GetY() - arrowWidth * HALF);
334         path.LineTo(vertex_.GetX() - arrowVerticalOffset, vertex_.GetY() - arrowHorizonOffset);
335     }
336     path.ArcTo(arrowRadius, arrowRadius, 0.0f, RSPathDirection::CW_DIRECTION, vertex_.GetX(), vertex_.GetY());
337 }
338 
PaintBubble(DrawingContext & context)339 void SliderTipModifier::PaintBubble(DrawingContext& context)
340 {
341     auto sizeScale = sizeScale_->Get();
342     auto opacityScale = opacityScale_->Get();
343     RSPath path;
344     auto vertexPair = GetBubbleVertex();
345     vertex_ = vertexPair.first;
346     auto vertexOffsetFromBlock = vertexPair.second;
347     auto pipeline = PipelineBase::GetCurrentContext();
348     CHECK_NULL_VOID(pipeline);
349     auto fontScale = pipeline->GetFontScale();
350     if (axis_ == Axis::HORIZONTAL) {
351         if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE)) {
352             PaintHorizontalBubbleSuitableAging(vertexOffsetFromBlock, path);
353         } else {
354             PaintHorizontalBubble(vertexOffsetFromBlock, path);
355         }
356     } else {
357         if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE)) {
358             PaintVerticalBubbleSuitableAging(vertexOffsetFromBlock, path);
359         } else {
360             PaintVerticalBubble(vertexOffsetFromBlock, path);
361         }
362     }
363     context.canvas.Save();
364     context.canvas.Translate(vertex_.GetX(), vertex_.GetY());
365     context.canvas.Scale(sizeScale, sizeScale);
366     context.canvas.Translate(vertex_.GetX() * -1.0, vertex_.GetY() * -1.0);
367     RSPen pen;
368     pen.SetColor(ToRSColor(tipColor_.ChangeAlpha(std::round(tipColor_.GetAlpha() * opacityScale))));
369     pen.SetAntiAlias(true);
370     RSBrush brush;
371     brush.SetColor(ToRSColor(tipColor_.ChangeAlpha(std::round(tipColor_.GetAlpha() * opacityScale))));
372     auto& canvas = context.canvas;
373     canvas.AttachPen(pen);
374     canvas.AttachBrush(brush);
375     canvas.ClipPath(path, RSClipOp::INTERSECT, true);
376     canvas.DrawPath(path);
377     canvas.DetachBrush();
378     canvas.DetachPen();
379 }
380 
onDraw(DrawingContext & context)381 void SliderTipModifier::onDraw(DrawingContext& context)
382 {
383     if (tipFlag_->Get() || GreatNotEqual(sizeScale_->Get(), BUBBLE_SIZE_MIN_SCALE)) {
384         BuildParagraph();
385         UpdateBubbleSize();
386         PaintTip(context);
387     }
388 }
389 
SetBubbleDisplayAnimation()390 void SliderTipModifier::SetBubbleDisplayAnimation()
391 {
392     auto weak = AceType::WeakClaim(this);
393     AnimationOption option = AnimationOption();
394     option.SetDuration(BUBBLE_DISPLAY_SIZE_CHANGE_TIMER);
395     option.SetCurve(Curves::FRICTION);
396     AnimationUtils::Animate(option, [weak]() {
397         auto self = weak.Upgrade();
398         CHECK_NULL_VOID(self);
399         self->sizeScale_->Set(BUBBLE_SIZE_MAX_SCALE);
400     });
401 
402     option.SetDuration(BUBBLE_DISPLAY_OPACITY_CHANGE_TIMER);
403     option.SetCurve(Curves::SHARP);
404     AnimationUtils::Animate(option, [weak]() {
405         auto self = weak.Upgrade();
406         CHECK_NULL_VOID(self);
407         self->opacityScale_->Set(BUBBLE_OPACITY_MAX_SCALE);
408     });
409 }
410 
SetBubbleDisappearAnimation()411 void SliderTipModifier::SetBubbleDisappearAnimation()
412 {
413     auto weak = AceType::WeakClaim(this);
414     AnimationOption option = AnimationOption();
415     option.SetDuration(BUBBLE_DISAPPEAR_SIZE_CHANGE_TIMER);
416     option.SetCurve(Curves::FRICTION);
417     AnimationUtils::Animate(option, [weak]() {
418         auto self = weak.Upgrade();
419         CHECK_NULL_VOID(self);
420         self->sizeScale_->Set(BUBBLE_SIZE_MIN_SCALE);
421     });
422 
423     option.SetDuration(BUBBLE_DISAPPEAR_OPACITY_CHANGE_TIMER);
424     option.SetCurve(Curves::SHARP);
425     AnimationUtils::Animate(option, [weak]() {
426         auto self = weak.Upgrade();
427         CHECK_NULL_VOID(self);
428         self->opacityScale_->Set(BUBBLE_OPACITY_MIN_SCALE);
429     });
430 }
431 
SetTipFlag(bool flag)432 void SliderTipModifier::SetTipFlag(bool flag)
433 {
434     CHECK_NULL_VOID(tipFlag_);
435     if (tipFlag_->Get() == flag) {
436         return;
437     }
438     taskId_++;
439     if (flag) {
440         SetBubbleDisplayAnimation();
441     } else {
442         auto pipeline = PipelineBase::GetCurrentContext();
443         CHECK_NULL_VOID(pipeline);
444         auto taskExecutor = pipeline->GetTaskExecutor();
445         CHECK_NULL_VOID(taskExecutor);
446         taskExecutor->PostDelayedTask(
447             [weak = WeakClaim(this), taskId = taskId_]() {
448                 auto modifier = weak.Upgrade();
449                 CHECK_NULL_VOID(modifier);
450                 if (modifier->taskId_ != taskId) {
451                     return;
452                 }
453                 modifier->SetBubbleDisappearAnimation();
454                 auto pipeline = PipelineBase::GetCurrentContext();
455                 CHECK_NULL_VOID(pipeline);
456                 pipeline->RequestFrame();
457             },
458             TaskExecutor::TaskType::UI, BUBBLE_DISAPPEAR_DELAY_TIMER, "ArkUISliderSetBubbleDisappearAnimation");
459     }
460     tipFlag_->Set(flag);
461 }
462 
BuildParagraph()463 void SliderTipModifier::BuildParagraph()
464 {
465     auto pipeline = PipelineBase::GetCurrentContext();
466     CHECK_NULL_VOID(pipeline);
467     auto fontStyle = std::make_unique<NG::FontStyle>();
468     CHECK_NULL_VOID(fontStyle);
469     fontStyle->UpdateTextColor(textColor_.ChangeAlpha(std::round(textColor_.GetAlpha() * opacityScale_->Get())));
470     auto fontScale = pipeline->GetFontScale();
471     if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) && LessNotEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
472         textFontSize_ = SUITABLEAGING_LEVEL_1_TEXT_FONT_SIZE;
473     } else if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
474         textFontSize_ = SUITABLEAGING_LEVEL_2_TEXT_FONT_SIZE;
475     }
476     fontStyle->UpdateFontSize(textFontSize_);
477     TextStyle textStyle = CreateTextStyleUsingTheme(fontStyle, nullptr, pipeline->GetTheme<TextTheme>());
478     auto content = content_->Get();
479     auto fontManager = pipeline->GetFontManager();
480     if (fontManager && fontManager->IsUseAppCustomFont()) {
481         textStyle.SetFontFamilies(Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont()));
482     }
483     CreateParagraphAndLayout(textStyle, content);
484 }
485 
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content)486 void SliderTipModifier::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content)
487 {
488     if (!CreateParagraph(textStyle, content)) {
489         return;
490     }
491     CHECK_NULL_VOID(paragraph_);
492     auto gridColumnType = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::BUBBLE_TYPE);
493     CHECK_NULL_VOID(gridColumnType);
494     auto parent = gridColumnType->GetParent();
495     if (parent) {
496         parent->BuildColumnWidth();
497     }
498 
499     auto pipeLine = PipelineBase::GetCurrentContextSafely();
500     CHECK_NULL_VOID(pipeLine);
501     auto fontScale = pipeLine->GetFontScale();
502     auto width = static_cast<float>(TEXT_MAX.ConvertToPx());
503     if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) ||
504         (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE))) {
505         width = static_cast<float>(TEXT_AGING_MAX.ConvertToPx());
506     }
507     paragraph_->Layout(width);
508 }
509 
CreateParagraph(const TextStyle & textStyle,std::string content)510 bool SliderTipModifier::CreateParagraph(const TextStyle& textStyle, std::string content)
511 {
512     ParagraphStyle paraStyle = { .direction = TextDirection::LTR,
513         .align = textStyle.GetTextAlign(),
514         .maxLines = MAX_LENGTH,
515         .fontLocale = Localization::GetInstance()->GetFontLocale(),
516         .wordBreak = WordBreak::BREAK_ALL,
517         .textOverflow = TextOverflow::ELLIPSIS };
518     paragraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
519     CHECK_NULL_RETURN(paragraph_, false);
520     paragraph_->PushStyle(textStyle);
521     StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
522     paragraph_->AddText(StringUtils::Str8ToStr16(content));
523     paragraph_->Build();
524     return true;
525 }
526 
GetBubbleVertex()527 std::pair<OffsetF, float> SliderTipModifier::GetBubbleVertex()
528 {
529     if (!getBubbleVertexFunc_) {
530         return std::pair<OffsetF, float>();
531     }
532     auto bubbleVertexInBlock = getBubbleVertexFunc_();
533     bubbleVertexInBlock.first += contentOffset_->Get();
534     return bubbleVertexInBlock;
535 }
536 
UpdateBubbleSize()537 void SliderTipModifier::UpdateBubbleSize()
538 {
539     auto pipeline = PipelineBase::GetCurrentContext();
540     CHECK_NULL_VOID(pipeline);
541     auto theme = pipeline->GetTheme<SliderTheme>();
542     CHECK_NULL_VOID(theme);
543 
544     float bubbleSizeHeight = static_cast<float>(BUBBLE_HORIZONTAL_WIDTH.ConvertToPx());
545     float bubbleSizeWidth = static_cast<float>(BUBBLE_HORIZONTAL_HEIGHT.ConvertToPx());
546     if (axis_ != Axis::HORIZONTAL) {
547         bubbleSizeHeight = static_cast<float>(BUBBLE_VERTICAL_WIDTH.ConvertToPx());
548         bubbleSizeWidth = static_cast<float>(BUBBLE_VERTICAL_HEIGHT.ConvertToPx());
549     }
550 
551     auto fontScale = pipeline->GetFontScale();
552     if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) && LessNotEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
553         bubbleSizeHeight = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_1_WIDTH.ConvertToPx());
554         bubbleSizeWidth = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_1_HEIGHT.ConvertToPx());
555         if (axis_ != Axis::HORIZONTAL) {
556             bubbleSizeHeight = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_1_WIDTH.ConvertToPx());
557             bubbleSizeWidth = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_1_HEIGHT.ConvertToPx());
558         }
559     } else if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
560         bubbleSizeHeight = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_2_WIDTH.ConvertToPx());
561         bubbleSizeWidth = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_2_HEIGHT.ConvertToPx());
562         if (axis_ != Axis::HORIZONTAL) {
563             bubbleSizeHeight = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_2_WIDTH.ConvertToPx());
564             bubbleSizeWidth = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_2_HEIGHT.ConvertToPx());
565         }
566     }
567     bubbleSize_ = SizeF(bubbleSizeHeight, bubbleSizeWidth);
568 }
569 
UpdateOverlayRect(const SizeF & frameSize)570 bool SliderTipModifier::UpdateOverlayRect(const SizeF& frameSize)
571 {
572     auto contentSize = contentSize_->Get();
573     auto pipeline = PipelineBase::GetCurrentContext();
574     CHECK_NULL_RETURN(pipeline, false);
575     auto theme = pipeline->GetTheme<SliderTheme>();
576     CHECK_NULL_RETURN(theme, false);
577     auto vertexPair = GetBubbleVertex();
578     auto vertex = vertexPair.first;
579     auto distance = static_cast<float>(theme->GetBubbleToCircleCenterDistance().ConvertToPx());
580     auto hotShadowWidth = sliderMode_ == SliderModel::SliderMode::OUTSET
581                               ? theme->GetOutsetHotBlockShadowWidth().ConvertToPx()
582                               : theme->GetInsetHotBlockShadowWidth().ConvertToPx();
583     auto circleSize = SizeF(blockSize_.Width() + hotShadowWidth / HALF, blockSize_.Height() + hotShadowWidth / HALF);
584     RectF rect;
585     if (axis_ == Axis::HORIZONTAL) {
586         auto maxWidth = std::max(circleSize.Height(), frameSize.Height());
587         if (sliderGlobalOffset_->Get().GetY() + vertex.GetY() < bubbleSize_.Height()) {
588             rect.SetOffset(OffsetF(-bubbleSize_.Width(), bubbleSize_.Height() + distance));
589         } else {
590             rect.SetOffset(OffsetF(-bubbleSize_.Width(), -bubbleSize_.Height() - distance));
591         }
592         rect.SetSize(
593             SizeF(contentSize.Width() + bubbleSize_.Width() / HALF, maxWidth + bubbleSize_.Height() + distance));
594     } else {
595         float bubbleCenterX = rect.GetOffset().GetX() + bubbleSize_.Width() * HALF;
596         float sliderOffsetX = sliderGlobalOffset_->Get().GetX() - bubbleCenterX;
597         auto maxWidth = std::max(circleSize.Width(), frameSize.Width());
598         if (sliderGlobalOffset_->Get().GetX() + vertex.GetX() < bubbleSize_.Width()) {
599             rect.SetOffset(OffsetF(AceApplicationInfo::GetInstance().IsRightToLeft()
600                 ? (sliderOffsetX + bubbleSize_.Width() + distance)
601                 : (bubbleSize_.Width() + distance), -bubbleSize_.Height()));
602         } else {
603             rect.SetOffset(OffsetF(AceApplicationInfo::GetInstance().IsRightToLeft()
604                 ? (sliderOffsetX - bubbleSize_.Width() - distance)
605                 : (-bubbleSize_.Width() - distance), -bubbleSize_.Height()));
606         }
607         rect.SetSize(
608             SizeF(maxWidth + bubbleSize_.Width() + distance, contentSize.Height() + bubbleSize_.Height() / HALF));
609     }
610     auto origin = GetBoundsRect();
611     if (origin.IsValid() && rect.IsValid()) {
612         rect = rect.CombineRectT(origin);
613     }
614     if (rect != origin) {
615         SetBoundsRect(rect);
616         return true;
617     }
618     return false;
619 }
620 } // namespace OHOS::Ace::NG
621