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