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