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(const RefPtr<FrameNode> & host)394 void SliderTipModifier::SetBubbleDisplayAnimation(const RefPtr<FrameNode>& host)
395 {
396 CHECK_NULL_VOID(host);
397 auto weak = AceType::WeakClaim(this);
398 AnimationOption option = AnimationOption();
399 option.SetDuration(BUBBLE_DISPLAY_SIZE_CHANGE_TIMER);
400 option.SetCurve(Curves::FRICTION);
401 AnimationUtils::Animate(option, [weak]() {
402 auto self = weak.Upgrade();
403 CHECK_NULL_VOID(self);
404 self->sizeScale_->Set(BUBBLE_SIZE_MAX_SCALE);
405 }, nullptr, nullptr, host->GetContextRefPtr());
406
407 option.SetDuration(BUBBLE_DISPLAY_OPACITY_CHANGE_TIMER);
408 option.SetCurve(Curves::SHARP);
409 AnimationUtils::Animate(option, [weak]() {
410 auto self = weak.Upgrade();
411 CHECK_NULL_VOID(self);
412 self->opacityScale_->Set(BUBBLE_OPACITY_MAX_SCALE);
413 }, nullptr, nullptr, host->GetContextRefPtr());
414 }
415
SetBubbleDisappearAnimation(const RefPtr<FrameNode> & host)416 void SliderTipModifier::SetBubbleDisappearAnimation(const RefPtr<FrameNode>& host)
417 {
418 CHECK_NULL_VOID(host);
419 auto weak = AceType::WeakClaim(this);
420 AnimationOption option = AnimationOption();
421 option.SetDuration(BUBBLE_DISAPPEAR_SIZE_CHANGE_TIMER);
422 option.SetCurve(Curves::FRICTION);
423 AnimationUtils::Animate(option, [weak]() {
424 auto self = weak.Upgrade();
425 CHECK_NULL_VOID(self);
426 self->sizeScale_->Set(BUBBLE_SIZE_MIN_SCALE);
427 }, nullptr, nullptr, host->GetContextRefPtr());
428
429 option.SetDuration(BUBBLE_DISAPPEAR_OPACITY_CHANGE_TIMER);
430 option.SetCurve(Curves::SHARP);
431 AnimationUtils::Animate(option, [weak]() {
432 auto self = weak.Upgrade();
433 CHECK_NULL_VOID(self);
434 self->opacityScale_->Set(BUBBLE_OPACITY_MIN_SCALE);
435 }, nullptr, nullptr, host->GetContextRefPtr());
436 }
437
SetTipFlag(bool flag,const RefPtr<FrameNode> & host)438 void SliderTipModifier::SetTipFlag(bool flag, const RefPtr<FrameNode>& host)
439 {
440 CHECK_NULL_VOID(tipFlag_);
441 if (tipFlag_->Get() == flag) {
442 return;
443 }
444 taskId_++;
445 if (flag) {
446 SetBubbleDisplayAnimation(host);
447 } else if (tipDelayTime_ > 0) {
448 auto pipeline = PipelineBase::GetCurrentContext();
449 CHECK_NULL_VOID(pipeline);
450 auto taskExecutor = pipeline->GetTaskExecutor();
451 CHECK_NULL_VOID(taskExecutor);
452 taskExecutor->PostDelayedTask(
453 [weak = WeakClaim(this), taskId = taskId_, weakHost = AceType::WeakClaim(AceType::RawPtr(host))]() {
454 auto modifier = weak.Upgrade();
455 CHECK_NULL_VOID(modifier);
456 auto host = weakHost.Upgrade();
457 CHECK_NULL_VOID(host);
458 if (modifier->taskId_ != taskId) {
459 return;
460 }
461 modifier->SetBubbleDisappearAnimation(host);
462 auto pipeline = host->GetContextRefPtr();
463 CHECK_NULL_VOID(pipeline);
464 pipeline->RequestFrame();
465 },
466 TaskExecutor::TaskType::UI, tipDelayTime_, "ArkUISliderSetBubbleDisappearAnimation");
467 } else {
468 SetBubbleDisappearAnimation(host);
469 }
470 tipFlag_->Set(flag);
471 }
472
BuildParagraph()473 void SliderTipModifier::BuildParagraph()
474 {
475 auto pipeline = PipelineBase::GetCurrentContext();
476 CHECK_NULL_VOID(pipeline);
477 auto fontStyle = std::make_unique<NG::FontStyle>();
478 CHECK_NULL_VOID(fontStyle);
479 fontStyle->UpdateTextColor(textColor_.ChangeAlpha(std::round(textColor_.GetAlpha() * opacityScale_->Get())));
480 auto fontScale = pipeline->GetFontScale();
481 if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) && LessNotEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
482 textFontSize_ = SUITABLEAGING_LEVEL_1_TEXT_FONT_SIZE;
483 } else if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
484 textFontSize_ = SUITABLEAGING_LEVEL_2_TEXT_FONT_SIZE;
485 }
486 fontStyle->UpdateFontSize(textFontSize_);
487 auto theme = pipeline->GetTheme<TextTheme>();
488 CHECK_NULL_VOID(theme);
489 TextStyle textStyle = CreateTextStyleUsingTheme(fontStyle, nullptr, theme);
490 auto content = content_->Get();
491 auto fontManager = pipeline->GetFontManager();
492 if (fontManager && fontManager->IsUseAppCustomFont()) {
493 textStyle.SetFontFamilies(Framework::ConvertStrToFontFamilies(fontManager->GetAppCustomFont()));
494 }
495 CreateParagraphAndLayout(textStyle, content);
496 }
497
CreateParagraphAndLayout(const TextStyle & textStyle,const std::string & content)498 void SliderTipModifier::CreateParagraphAndLayout(const TextStyle& textStyle, const std::string& content)
499 {
500 if (!CreateParagraph(textStyle, content)) {
501 return;
502 }
503 CHECK_NULL_VOID(paragraph_);
504 auto gridColumnType = GridSystemManager::GetInstance().GetInfoByType(GridColumnType::BUBBLE_TYPE);
505 CHECK_NULL_VOID(gridColumnType);
506 auto parent = gridColumnType->GetParent();
507 if (parent) {
508 parent->BuildColumnWidth();
509 }
510
511 auto pipeLine = PipelineBase::GetCurrentContextSafely();
512 CHECK_NULL_VOID(pipeLine);
513 auto fontScale = pipeLine->GetFontScale();
514 auto width = static_cast<float>(TEXT_MAX.ConvertToPx());
515 if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) ||
516 (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE))) {
517 width = static_cast<float>(TEXT_AGING_MAX.ConvertToPx());
518 }
519 paragraph_->Layout(width);
520 }
521
CreateParagraph(const TextStyle & textStyle,std::string content)522 bool SliderTipModifier::CreateParagraph(const TextStyle& textStyle, std::string content)
523 {
524 ParagraphStyle paraStyle = { .direction = TextDirection::LTR,
525 .align = textStyle.GetTextAlign(),
526 .maxLines = MAX_LENGTH,
527 .fontLocale = Localization::GetInstance()->GetFontLocale(),
528 .wordBreak = WordBreak::BREAK_ALL,
529 .textOverflow = TextOverflow::ELLIPSIS };
530 paragraph_ = Paragraph::Create(paraStyle, FontCollection::Current());
531 CHECK_NULL_RETURN(paragraph_, false);
532 paragraph_->PushStyle(textStyle);
533 StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
534 paragraph_->AddText(StringUtils::Str8ToStr16(content));
535 paragraph_->Build();
536 return true;
537 }
538
GetBubbleVertex()539 std::pair<OffsetF, float> SliderTipModifier::GetBubbleVertex()
540 {
541 if (!getBubbleVertexFunc_) {
542 return std::pair<OffsetF, float>();
543 }
544 auto bubbleVertexInBlock = getBubbleVertexFunc_();
545 bubbleVertexInBlock.first += contentOffset_->Get();
546 return bubbleVertexInBlock;
547 }
548
UpdateBubbleSize()549 void SliderTipModifier::UpdateBubbleSize()
550 {
551 auto pipeline = PipelineBase::GetCurrentContext();
552 CHECK_NULL_VOID(pipeline);
553 auto theme = pipeline->GetTheme<SliderTheme>();
554 CHECK_NULL_VOID(theme);
555
556 float bubbleSizeHeight = static_cast<float>(BUBBLE_HORIZONTAL_WIDTH.ConvertToPx());
557 float bubbleSizeWidth = static_cast<float>(BUBBLE_HORIZONTAL_HEIGHT.ConvertToPx());
558 if (axis_ != Axis::HORIZONTAL) {
559 bubbleSizeHeight = static_cast<float>(BUBBLE_VERTICAL_WIDTH.ConvertToPx());
560 bubbleSizeWidth = static_cast<float>(BUBBLE_VERTICAL_HEIGHT.ConvertToPx());
561 }
562
563 auto fontScale = pipeline->GetFontScale();
564 if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_1_SCALE) && LessNotEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
565 bubbleSizeHeight = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_1_WIDTH.ConvertToPx());
566 bubbleSizeWidth = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_1_HEIGHT.ConvertToPx());
567 if (axis_ != Axis::HORIZONTAL) {
568 bubbleSizeHeight = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_1_WIDTH.ConvertToPx());
569 bubbleSizeWidth = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_1_HEIGHT.ConvertToPx());
570 }
571 } else if (GreatOrEqual(fontScale, SUITABLEAGING_LEVEL_2_SCALE)) {
572 bubbleSizeHeight = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_2_WIDTH.ConvertToPx());
573 bubbleSizeWidth = static_cast<float>(BUBBLE_HORIZONTAL_SUITABLEAGING_LEVEL_2_HEIGHT.ConvertToPx());
574 if (axis_ != Axis::HORIZONTAL) {
575 bubbleSizeHeight = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_2_WIDTH.ConvertToPx());
576 bubbleSizeWidth = static_cast<float>(BUBBLE_VERTICAL_SUITABLEAGING_LEVEL_2_HEIGHT.ConvertToPx());
577 }
578 }
579 bubbleSize_ = SizeF(bubbleSizeHeight, bubbleSizeWidth);
580 }
581
UpdateOverlayRect(const SizeF & frameSize)582 bool SliderTipModifier::UpdateOverlayRect(const SizeF& frameSize)
583 {
584 auto contentSize = contentSize_->Get();
585 auto pipeline = PipelineBase::GetCurrentContext();
586 CHECK_NULL_RETURN(pipeline, false);
587 auto theme = pipeline->GetTheme<SliderTheme>();
588 CHECK_NULL_RETURN(theme, false);
589 auto vertexPair = GetBubbleVertex();
590 auto vertex = vertexPair.first;
591 auto distance = static_cast<float>(theme->GetBubbleToCircleCenterDistance().ConvertToPx());
592 auto hotShadowWidth = sliderMode_ == SliderModel::SliderMode::OUTSET
593 ? theme->GetOutsetHotBlockShadowWidth().ConvertToPx()
594 : theme->GetInsetHotBlockShadowWidth().ConvertToPx();
595 auto circleSize = SizeF(blockSize_.Width() + hotShadowWidth / HALF, blockSize_.Height() + hotShadowWidth / HALF);
596 RectF rect;
597 if (axis_ == Axis::HORIZONTAL) {
598 auto maxWidth = std::max(circleSize.Height(), frameSize.Height());
599 if (sliderGlobalOffset_->Get().GetY() + vertex.GetY() < bubbleSize_.Height()) {
600 rect.SetOffset(OffsetF(-bubbleSize_.Width(), bubbleSize_.Height() + distance));
601 } else {
602 rect.SetOffset(OffsetF(-bubbleSize_.Width(), -bubbleSize_.Height() - distance));
603 }
604 rect.SetSize(
605 SizeF(contentSize.Width() + bubbleSize_.Width() / HALF, maxWidth + bubbleSize_.Height() + distance));
606 } else {
607 float bubbleCenterX = rect.GetOffset().GetX() + bubbleSize_.Width() * HALF;
608 float sliderOffsetX = sliderGlobalOffset_->Get().GetX() - bubbleCenterX;
609 auto maxWidth = std::max(circleSize.Width(), frameSize.Width());
610 if (sliderGlobalOffset_->Get().GetX() + vertex.GetX() < bubbleSize_.Width()) {
611 rect.SetOffset(OffsetF(AceApplicationInfo::GetInstance().IsRightToLeft()
612 ? (sliderOffsetX - bubbleSize_.Width() - distance)
613 : (bubbleSize_.Width() + distance), -bubbleSize_.Height()));
614 } else {
615 rect.SetOffset(OffsetF(AceApplicationInfo::GetInstance().IsRightToLeft()
616 ? (sliderOffsetX + bubbleSize_.Width() + distance)
617 : (-bubbleSize_.Width() - distance), -bubbleSize_.Height()));
618 }
619 rect.SetSize(
620 SizeF(maxWidth + bubbleSize_.Width() + distance, contentSize.Height() + bubbleSize_.Height() / HALF));
621 }
622 auto origin = GetBoundsRect();
623 if (origin.IsValid() && rect.IsValid()) {
624 rect = rect.CombineRectT(origin);
625 }
626 if (rect != origin) {
627 SetBoundsRect(rect);
628 return true;
629 }
630 return false;
631 }
632 } // namespace OHOS::Ace::NG
633