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