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