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/text_drag/text_drag_overlay_modifier.h"
17
18 #include <variant>
19
20 #include "base/geometry/rect.h"
21 #include "base/utils/utils.h"
22 #include "core/components_ng/pattern/image/image_pattern.h"
23 #include "core/components_ng/pattern/text_drag/text_drag_pattern.h"
24 #include "core/components_ng/render/adapter/pixelmap_image.h"
25 #include "core/components_ng/render/drawing_prop_convertor.h"
26 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_info.h"
27 #include "core/components/text_overlay/text_overlay_theme.h"
28 #include "core/components/text/text_theme.h"
29
30 namespace OHOS::Ace::NG {
31 constexpr float DEFAULT_LIGHT_HEIGHT = 600.0f;
32 constexpr uint32_t DEFAULT_AMBIENT_COLOR = 0X0A000000;
33 constexpr float DEFAULT_SHADOW_COLOR = 0x33000000;
34 constexpr float DEFAULT_LIGHT_RADIUS = 800.0f;
35 constexpr float DEFAULT_ELEVATION = 120.0f;
36 constexpr int32_t HANDLE_RING_DEGREE = 360;
37 constexpr float CONSTANT_DOUBLE = 2.0f;
38
TextDragOverlayModifier(const WeakPtr<OHOS::Ace::NG::Pattern> & pattern)39 TextDragOverlayModifier::TextDragOverlayModifier(const WeakPtr<OHOS::Ace::NG::Pattern>& pattern) : pattern_(pattern)
40 {
41 backgroundOffset_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(TEXT_DRAG_OFFSET.ConvertToPx());
42 selectedBackgroundOpacity_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0);
43 shadowOpacity_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0);
44 handleOpacity_ = AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0);
45 firstHandle_ = AceType::MakeRefPtr<PropertyRectF>(RectF(0, 0, 0, 0));
46 secondHandle_ = AceType::MakeRefPtr<PropertyRectF>(RectF(0, 0, 0, 0));
47 handleRadius_ = AceType::MakeRefPtr<PropertyFloat>(0.0);
48 innerHandleRadius_ = AceType::MakeRefPtr<PropertyFloat>(0.0);
49 innerHandleColor_ = AceType::MakeRefPtr<PropertyColor>(Color::BLACK);
50 handleColor_ = AceType::MakeRefPtr<PropertyColor>(Color::BLACK);
51 selectedColor_ = AceType::MakeRefPtr<PropertyInt>(0);
52 AttachProperty(backgroundOffset_);
53 AttachProperty(selectedBackgroundOpacity_);
54 AttachProperty(shadowOpacity_);
55 AttachProperty(firstHandle_);
56 AttachProperty(secondHandle_);
57 AttachProperty(handleOpacity_);
58 AttachProperty(handleRadius_);
59 AttachProperty(innerHandleRadius_);
60 AttachProperty(innerHandleColor_);
61 AttachProperty(handleColor_);
62 AttachProperty(selectedColor_);
63 }
64
StartFloatingAnimate()65 void TextDragOverlayModifier::StartFloatingAnimate()
66 {
67 isAnimating_ = true;
68 type_ = DragAnimType::FLOATING;
69 SetHandleOpacity(IsHandlesShow() ? 1.0 : 0.0);
70 AnimationOption handleOption;
71 handleOption.SetDuration(TEXT_FLOATING_ANIMATE_HANDLE_OPACITY_DURATION);
72 handleOption.SetCurve(Curves::LINEAR);
73 handleOption.SetDelay(0);
74 handleOption.SetFillMode(FillMode::FORWARDS);
75 auto handlePropertyCallback = [weakModifier = WeakClaim(this)]() {
76 auto modifier = weakModifier.Upgrade();
77 CHECK_NULL_VOID(modifier);
78 modifier->SetHandleOpacity(0.0);
79 };
80 auto pattern = pattern_.Upgrade();
81 auto host = pattern ? pattern->GetHost() : nullptr;
82 auto contextPtr = host ? host->GetContextRefPtr() : nullptr;
83 AnimationUtils::Animate(handleOption, handlePropertyCallback, nullptr, nullptr, contextPtr);
84 SetShadowOpacity(0.0);
85 AnimationOption shadowOption;
86 shadowOption.SetDuration(TEXT_ANIMATION_DURATION);
87 shadowOption.SetCurve(Curves::SHARP);
88 shadowOption.SetDelay(0);
89 shadowOption.SetFillMode(FillMode::FORWARDS);
90 auto shadowPropertyCallback = [weakModifier = WeakClaim(this)]() {
91 auto modifier = weakModifier.Upgrade();
92 CHECK_NULL_VOID(modifier);
93 modifier->SetShadowOpacity(1.0);
94 };
95 AnimationUtils::Animate(shadowOption, shadowPropertyCallback, nullptr, nullptr, contextPtr);
96 StartFloatingSelBackgroundAnimate();
97 }
98
StartFloatingSelBackgroundAnimate()99 void TextDragOverlayModifier::StartFloatingSelBackgroundAnimate()
100 {
101 SetBackgroundOffset(0);
102 SetSelectedBackgroundOpacity(IsHandlesShow() ? 1.0 : 0.0);
103 AnimationOption option;
104 option.SetDuration(TEXT_ANIMATION_DURATION);
105 option.SetCurve(Curves::FRICTION);
106 option.SetDelay(0);
107 auto finishFuc = [weakModifier = WeakClaim(this)]() {
108 auto modifier = weakModifier.Upgrade();
109 CHECK_NULL_VOID(modifier);
110 modifier->SetAnimateFlag(false);
111 };
112 option.SetOnFinishEvent(finishFuc);
113 option.SetFillMode(FillMode::FORWARDS);
114 auto propertyCallback = [weakModifier = WeakClaim(this)]() {
115 auto modifier = weakModifier.Upgrade();
116 CHECK_NULL_VOID(modifier);
117 modifier->SetBackgroundOffset(TEXT_DRAG_DEFAULT_OFFSET.ConvertToPx());
118 modifier->SetSelectedBackgroundOpacity(0.0);
119 };
120 auto pattern = pattern_.Upgrade();
121 auto host = pattern ? pattern->GetHost() : nullptr;
122 auto contextPtr = host ? host->GetContextRefPtr() : nullptr;
123 AnimationUtils::Animate(option, propertyCallback, option.GetOnFinishEvent(), nullptr, contextPtr);
124 }
125
PaintShadow(const RSPath & path,const Shadow & shadow,RSCanvas & canvas)126 void TextDragOverlayModifier::PaintShadow(const RSPath& path, const Shadow& shadow, RSCanvas& canvas)
127 {
128 if (type_ == DragAnimType::DEFAULT) {
129 return;
130 }
131 RSRecordingPath rsPath;
132 rsPath.AddPath(path);
133 rsPath.Offset(shadow.GetOffset().GetX(), shadow.GetOffset().GetY());
134 Color color = shadow.GetColor();
135 color = color.BlendOpacity(shadowOpacity_->Get());
136 RSColor spotColor = ToRSColor(color);
137 RSPoint3 planeParams = { 0.0, 0.0, shadow.GetElevation() };
138 auto bounds = rsPath.GetBounds();
139 RSPoint3 lightPos = { bounds.GetLeft() + bounds.GetWidth() / 2.0, bounds.GetTop() + bounds.GetHeight() / 2.0,
140 DEFAULT_LIGHT_HEIGHT };
141 RSColor ambientColor = ToRSColor(Color(DEFAULT_AMBIENT_COLOR));
142 canvas.DrawShadowStyle(rsPath, planeParams, lightPos, DEFAULT_LIGHT_RADIUS, ambientColor, spotColor,
143 RSShadowFlags::TRANSPARENT_OCCLUDER, true);
144 canvas.Restore();
145 }
146
PaintBackground(const RSPath & path,RSCanvas & canvas,RefPtr<TextDragPattern> textDragPattern)147 void TextDragOverlayModifier::PaintBackground(const RSPath& path, RSCanvas& canvas,
148 RefPtr<TextDragPattern> textDragPattern)
149 {
150 auto shadow = Shadow(DEFAULT_ELEVATION, {0.0, 0.0}, Color(DEFAULT_SHADOW_COLOR), ShadowStyle::OuterFloatingSM);
151 PaintShadow(path, shadow, canvas);
152 auto pattern = DynamicCast<TextDragPattern>(pattern_.Upgrade());
153 CHECK_NULL_VOID(pattern);
154 Color color = pattern->GetDragBackgroundColor();
155 RSBrush brush;
156 brush.SetColor(ToRSColor(color));
157 brush.SetAntiAlias(true);
158 canvas.AttachBrush(brush);
159 canvas.DrawPath(path);
160 canvas.DetachBrush();
161 if (type_ == DragAnimType::DEFAULT) {
162 return;
163 }
164 canvas.Save();
165 canvas.ClipPath(path, RSClipOp::INTERSECT, true);
166 std::shared_ptr<RSPath> selPath = textDragPattern->GetSelBackgroundPath();
167 RSBrush selBrush;
168 Color selColor = pattern->GetDragBackgroundColor();
169 if (type_ == DragAnimType::FLOATING) {
170 selColor = selColor.BlendOpacity(selectedBackgroundOpacity_->Get());
171 }
172 selBrush.SetColor(ToRSColor(selColor));
173 selBrush.SetAntiAlias(true);
174 canvas.AttachBrush(selBrush);
175 canvas.DrawPath(*selPath);
176 canvas.DetachBrush();
177 canvas.Restore();
178 }
179
PaintHandle(RSCanvas & canvas,const RectF & handleRect,bool isFirstHandle,float startX,float startY)180 void TextDragOverlayModifier::PaintHandle(RSCanvas& canvas, const RectF& handleRect, bool isFirstHandle,
181 float startX, float startY)
182 {
183 if (!isAnimating_ || type_ == DragAnimType::DEFAULT) {
184 return;
185 }
186 if (NearZero(handleOpacity_->Get()) || NearZero(handleRect.Width())) {
187 return;
188 }
189 auto rectTopX = (handleRect.Right() - handleRect.Left()) / CONSTANT_DOUBLE + startX;
190 auto centerOffset = OffsetF(rectTopX, 0.0);
191 OffsetF startPoint(0.0, 0.0);
192 OffsetF endPoint(0.0, 0.0);
193 float offset = backgroundOffset_->Get();
194 auto outerHandleRadius = handleRadius_->Get();
195 auto handleRectHeight = handleRect.Height();
196 if (isFirstHandle) {
197 centerOffset.SetX(centerOffset.GetX() - offset);
198 centerOffset.SetY(startY - outerHandleRadius);
199 startPoint.SetY(outerHandleRadius - 1.0);
200 endPoint.SetY(outerHandleRadius + handleRectHeight);
201 } else {
202 centerOffset.SetX(centerOffset.GetX() + offset);
203 centerOffset.SetY(startY + outerHandleRadius);
204 startPoint.SetY(-outerHandleRadius + 1.0);
205 endPoint.SetY(-outerHandleRadius - handleRectHeight);
206 }
207 canvas.Save();
208 canvas.Translate(centerOffset.GetX(), centerOffset.GetY());
209 PaintHandleRing(canvas);
210 PaintHandleHold(canvas, handleRect, startPoint, endPoint);
211 canvas.Restore();
212 }
213
PaintHandleRing(RSCanvas & canvas)214 void TextDragOverlayModifier::PaintHandleRing(RSCanvas& canvas)
215 {
216 RSPen pen;
217 pen.SetAntiAlias(true);
218 Color handleColor = handleColor_->Get();
219 auto handleOpacity = handleOpacity_->Get();
220 handleColor = handleColor.BlendOpacity(handleOpacity);
221 pen.SetColor(handleColor.GetValue());
222 auto outerHandleRadius = handleRadius_->Get();
223 auto innerHandleRadius = innerHandleRadius_->Get();
224 pen.SetWidth(outerHandleRadius - innerHandleRadius);
225 pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
226 canvas.AttachPen(pen);
227 auto radius = (innerHandleRadius + outerHandleRadius) / 2;
228 RSPath ringPath;
229 ringPath.AddArc({ -radius, -radius, radius, radius }, 0, HANDLE_RING_DEGREE);
230 canvas.DrawPath(ringPath);
231 canvas.DetachPen();
232 Color innerHandleColor = innerHandleColor_->Get();
233 innerHandleColor = innerHandleColor.BlendOpacity(handleOpacity);
234 RSBrush brush;
235 brush.SetAntiAlias(true);
236 brush.SetColor(innerHandleColor.GetValue());
237 canvas.AttachBrush(brush);
238 canvas.DrawCircle(RSPoint(0.0, 0.0), innerHandleRadius);
239 canvas.DetachBrush();
240 }
241
PaintHandleHold(RSCanvas & canvas,const RectF & handleRect,const OffsetF & startPoint,const OffsetF & endPoint)242 void TextDragOverlayModifier::PaintHandleHold(RSCanvas& canvas, const RectF& handleRect,
243 const OffsetF& startPoint, const OffsetF& endPoint)
244 {
245 Color handleColor = handleColor_->Get();
246 auto handleOpacity = handleOpacity_->Get();
247 handleColor = handleColor.BlendOpacity(handleOpacity);
248 RSPen linePen;
249 linePen.SetAntiAlias(true);
250 linePen.SetColor(handleColor.GetValue());
251 linePen.SetWidth(handleRect.Width());
252 linePen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
253 canvas.AttachPen(linePen);
254 canvas.DrawLine(RSPoint(startPoint.GetX(), startPoint.GetY()), RSPoint(endPoint.GetX(), endPoint.GetY()));
255 canvas.DetachPen();
256 }
257
PaintSelBackground(RSCanvas & canvas,const RefPtr<TextDragPattern> & textDragPattern)258 void TextDragOverlayModifier::PaintSelBackground(RSCanvas& canvas, const RefPtr<TextDragPattern>& textDragPattern)
259 {
260 if (type_ == DragAnimType::DEFAULT || NearZero(selectedBackgroundOpacity_->Get())) {
261 return;
262 }
263 std::shared_ptr<RSPath> path = textDragPattern->GetSelBackgroundPath();
264 float offset = isAnimating_ ? backgroundOffset_->Get() : 0.0;
265 if (!NearZero(offset)) {
266 path = textDragPattern->GenerateSelBackgroundPath(offset);
267 }
268 RSBrush selBrush;
269 Color selColor = Color(selectedColor_->Get());
270 selColor = selColor.BlendOpacity(selectedBackgroundOpacity_->Get());
271 selBrush.SetColor(ToRSColor(selColor));
272 selBrush.SetAntiAlias(true);
273 canvas.AttachBrush(selBrush);
274 canvas.DrawPath(*path);
275 canvas.DetachBrush();
276 }
277
onDraw(DrawingContext & context)278 void TextDragOverlayModifier::onDraw(DrawingContext& context)
279 {
280 auto pattern = DynamicCast<TextDragPattern>(pattern_.Upgrade());
281 CHECK_NULL_VOID(pattern);
282 auto& canvas = context.canvas;
283 std::shared_ptr<RSPath> path;
284 if (!isAnimating_) {
285 path = pattern->GetBackgroundPath();
286 } else {
287 path = pattern->GenerateBackgroundPath(backgroundOffset_->Get(), 1 - selectedBackgroundOpacity_->Get());
288 }
289 PaintBackground(*path, canvas, pattern);
290 canvas.Save();
291 canvas.ClipPath(*pattern->GetClipPath(), RSClipOp::INTERSECT, true);
292 auto paragraph = pattern->GetParagraph().Upgrade();
293 if (paragraph) {
294 paragraph->Paint(canvas, pattern->GetTextRect().GetX(), pattern->GetTextRect().GetY());
295 }
296 PaintImage(canvas);
297 canvas.Restore();
298 canvas.Save();
299 canvas.ClipPath(*path, RSClipOp::INTERSECT, true);
300 PaintSelBackground(canvas, pattern);
301 canvas.Restore();
302 if (firstHandle_ && isFirstHandleAnimated_) {
303 auto selectPosition = pattern->GetSelectPosition();
304 auto rect = firstHandle_->Get();
305 auto startY = rect.Top() - selectPosition.globalY_;
306 PaintHandle(canvas, firstHandle_->Get(), true, rect.Left() - selectPosition.globalX_, startY);
307 }
308 if (secondHandle_ && isSecondHandleAnimated_) {
309 auto selectPosition = pattern->GetSelectPosition();
310 auto rect = secondHandle_->Get();
311 auto startY = rect.Bottom() - selectPosition.globalY_;
312 PaintHandle(canvas, secondHandle_->Get(), false, rect.Left() - selectPosition.globalX_, startY);
313 }
314 }
315
PaintImage(RSCanvas & canvas)316 void TextDragOverlayModifier::PaintImage(RSCanvas& canvas)
317 {
318 auto pattern = DynamicCast<TextDragPattern>(pattern_.Upgrade());
319 CHECK_NULL_VOID(pattern);
320 size_t index = 0;
321 auto contentOffset = pattern->GetContentOffset();
322 auto imageChildren = pattern->GetImageChildren();
323 auto rectsForPlaceholders = pattern->GetRectsForPlaceholders();
324 for (const auto& child : imageChildren) {
325 auto rect = rectsForPlaceholders.at(index);
326 auto offset = OffsetF(rect.Left(), rect.Top()) - contentOffset;
327 auto imageChild = DynamicCast<ImagePattern>(child->GetPattern());
328 if (imageChild) {
329 RectF imageRect(offset.GetX(), offset.GetY(), rect.Width(), rect.Height());
330 auto canvasImage = imageChild->GetCanvasImage();
331 CHECK_NULL_VOID(canvasImage);
332 auto pixelMapImage = DynamicCast<PixelMapImage>(canvasImage);
333 CHECK_NULL_VOID(pixelMapImage);
334 pixelMapImage->DrawRect(canvas, ToRSRect(imageRect));
335 }
336 ++index;
337 }
338 }
339
SetBackgroundOffset(float offset)340 void TextDragOverlayModifier::SetBackgroundOffset(float offset)
341 {
342 backgroundOffset_->Set(offset);
343 }
344
SetSelectedBackgroundOpacity(float offset)345 void TextDragOverlayModifier::SetSelectedBackgroundOpacity(float offset)
346 {
347 selectedBackgroundOpacity_->Set(offset);
348 }
349
SetAnimateFlag(bool isAnimate)350 void TextDragOverlayModifier::SetAnimateFlag(bool isAnimate)
351 {
352 isAnimating_ = isAnimate;
353 if (!isAnimating_) {
354 auto pattern = DynamicCast<TextDragPattern>(pattern_.Upgrade());
355 CHECK_NULL_VOID(pattern);
356 pattern->ResetAnimatingParagraph();
357 }
358 }
359 } // namespace OHOS::Ace::NG
360