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/select_overlay/select_overlay_content_modifier.h"
17 #include <algorithm>
18 
19 #include "base/geometry/ng/offset_t.h"
20 #include "base/utils/utils.h"
21 #include "core/components/common/properties/color.h"
22 #include "core/components/text_overlay/text_overlay_theme.h"
23 #include "core/components_ng/base/modifier.h"
24 #include "core/components_ng/pattern/select_overlay/select_overlay_layout_algorithm.h"
25 #include "core/components_ng/pattern/select_overlay/select_overlay_paint_method.h"
26 #include "core/components_ng/pattern/select_overlay/select_overlay_pattern.h"
27 #include "core/components_ng/render/drawing.h"
28 
29 namespace OHOS::Ace::NG {
30 namespace {
31 constexpr float VIEW_PORT_MODIFICATION_VALUE = 1.0f;
32 }
SelectOverlayContentModifier(const WeakPtr<Pattern> & pattern)33 SelectOverlayContentModifier::SelectOverlayContentModifier(const WeakPtr<Pattern>& pattern)
34     : inShowArea_(AceType::MakeRefPtr<PropertyBool>(false)),
35       handleReverse_(AceType::MakeRefPtr<PropertyBool>(false)),
36       isSingleHandle_(AceType::MakeRefPtr<PropertyBool>(false)),
37       firstHandleIsShow_(AceType::MakeRefPtr<PropertyBool>(false)),
38       firstCircleIsShow_(AceType::MakeRefPtr<PropertyBool>(true)),
39       secondHandleIsShow_(AceType::MakeRefPtr<PropertyBool>(false)),
40       secondCircleIsShow_(AceType::MakeRefPtr<PropertyBool>(true)),
41       isHiddenHandle_(AceType::MakeRefPtr<PropertyBool>(false)),
42       isHandleLineShow_(AceType::MakeRefPtr<PropertyBool>(false)),
43       viewPort_(AceType::MakeRefPtr<PropertyRectF>(RectF(0, 0, 0, 0))),
44       firstHandle_(AceType::MakeRefPtr<PropertyRectF>(RectF(0, 0, 0, 0))),
45       secondHandle_(AceType::MakeRefPtr<PropertyRectF>(RectF(0, 0, 0, 0))),
46       handleColor_(AceType::MakeRefPtr<PropertyColor>(Color::BLACK)),
47       innerHandleColor_(AceType::MakeRefPtr<PropertyColor>(Color::BLACK)),
48       handleRadius_(AceType::MakeRefPtr<PropertyFloat>(0.0)),
49       handleStrokeWidth_(AceType::MakeRefPtr<PropertyFloat>(0.0)),
50       innerHandleRadius_(AceType::MakeRefPtr<PropertyFloat>(0.0)),
51       handleOpacity_(AceType::MakeRefPtr<AnimatablePropertyFloat>(0.0)),
52       pattern_(pattern)
53 {
54     AttachProperty(inShowArea_);
55     AttachProperty(handleReverse_);
56     AttachProperty(isSingleHandle_);
57     AttachProperty(firstHandleIsShow_);
58     AttachProperty(firstCircleIsShow_);
59     AttachProperty(secondHandleIsShow_);
60     AttachProperty(secondCircleIsShow_);
61     AttachProperty(isHiddenHandle_);
62     AttachProperty(isHandleLineShow_);
63     AttachProperty(viewPort_);
64     AttachProperty(firstHandle_);
65     AttachProperty(secondHandle_);
66     AttachProperty(handleColor_);
67     AttachProperty(innerHandleColor_);
68     AttachProperty(handleRadius_);
69     AttachProperty(handleStrokeWidth_);
70     AttachProperty(innerHandleRadius_);
71     AttachProperty(handleOpacity_);
72 }
73 
onDraw(DrawingContext & drawingContext)74 void SelectOverlayContentModifier::onDraw(DrawingContext& drawingContext)
75 {
76     CHECK_NULL_VOID(!isUsingMouse_);
77     if (isHiddenHandle_->Get()) {
78         return;
79     }
80 
81     if (!inShowArea_->Get()) {
82         return;
83     }
84 
85     auto& canvas = drawingContext.canvas;
86     canvas.Save();
87     ClipViewPort(canvas);
88 
89     if (isSingleHandle_->Get()) {
90         PaintSingleHandle(canvas);
91     } else {
92         PaintDoubleHandle(canvas);
93     }
94 
95     canvas.Restore();
96 }
97 
PaintSingleHandle(RSCanvas & canvas)98 void SelectOverlayContentModifier::PaintSingleHandle(RSCanvas& canvas)
99 {
100     if (PaintSingleHandleWithPoints(canvas)) {
101         return;
102     }
103     PaintSingleHandleWithRect(canvas);
104 }
105 
CalculateCenterPoint(const OffsetF & start,const OffsetF & end,float radius,bool handleOnTop)106 OffsetF SelectOverlayContentModifier::CalculateCenterPoint(
107     const OffsetF& start, const OffsetF& end, float radius, bool handleOnTop)
108 {
109     float vectorX = end.GetX() - start.GetX();
110     float vectorY = end.GetY() - start.GetY();
111     if (handleOnTop) {
112         vectorX = -vectorX;
113         vectorY = -vectorY;
114     }
115     float vectorLen = std::sqrt(vectorX * vectorX + vectorY * vectorY);
116     float unitVectorX = NearZero(vectorLen) ? 0.0f : vectorX / vectorLen;
117     float unitVectorY = NearZero(vectorLen) ? 0.0f : vectorY / vectorLen;
118     float extendedVectorX = unitVectorX * radius;
119     float extendedVectorY = unitVectorY * radius;
120     float centerX = handleOnTop ? start.GetX() + extendedVectorX : end.GetX() + extendedVectorX;
121     float centerY = handleOnTop ? start.GetY() + extendedVectorY : end.GetY() + extendedVectorY;
122     return OffsetF(centerX, centerY);
123 }
124 
PaintSingleHandleWithPoints(RSCanvas & canvas)125 bool SelectOverlayContentModifier::PaintSingleHandleWithPoints(RSCanvas& canvas)
126 {
127     CHECK_NULL_RETURN(isPaintHandleUsePoints_, false);
128     if (firstHandleIsShow_->Get()) {
129         auto startPoint = firstHandlePaintInfo_.startPoint;
130         startPoint.SetY(startPoint.GetY() + 1.0f);
131         auto centerOffset = CalculateCenterPoint(
132             firstHandlePaintInfo_.startPoint, firstHandlePaintInfo_.endPoint, GetDrawHandleRadius(), false);
133         HandleDrawInfo drawInfo = {
134             .startPoint = startPoint - centerOffset,
135             .endPoint = firstHandlePaintInfo_.endPoint - centerOffset,
136             .centerOffset = centerOffset,
137             .handleWidth = firstHandlePaintInfo_.width,
138             .isHandleLineShow = isHandleLineShow_->Get(),
139             .isCircleShow = firstCircleIsShow_->Get()
140         };
141         PaintHandle(canvas, drawInfo);
142     }
143     if (secondHandleIsShow_->Get()) {
144         auto startPoint = secondHandlePaintInfo_.startPoint;
145         startPoint.SetY(startPoint.GetY() + 1.0f);
146         auto centerOffset = CalculateCenterPoint(
147             secondHandlePaintInfo_.startPoint, secondHandlePaintInfo_.endPoint, GetDrawHandleRadius(), false);
148         HandleDrawInfo drawInfo = {
149             .startPoint = startPoint - centerOffset,
150             .endPoint = secondHandlePaintInfo_.endPoint - centerOffset,
151             .centerOffset = centerOffset,
152             .handleWidth = secondHandlePaintInfo_.width,
153             .isHandleLineShow = isHandleLineShow_->Get(),
154             .isCircleShow = secondCircleIsShow_->Get()
155         };
156         PaintHandle(canvas, drawInfo);
157     }
158     return true;
159 }
160 
PaintSingleHandleWithRect(RSCanvas & canvas)161 void SelectOverlayContentModifier::PaintSingleHandleWithRect(RSCanvas& canvas)
162 {
163     if (firstHandleIsShow_->Get()) {
164         PaintHandle(canvas, firstHandle_->Get(), false,
165             { isHandleLineShow_->Get(), firstCircleIsShow_->Get(), IsDraggingHandle(true) });
166         return;
167     }
168     if (secondHandleIsShow_->Get() || isClipHandleDrawRect_) {
169         PaintHandle(canvas, secondHandle_->Get(), false,
170             { isHandleLineShow_->Get(), secondCircleIsShow_->Get(), IsDraggingHandle(false) });
171     }
172 }
173 
PaintDoubleHandle(RSCanvas & canvas)174 void SelectOverlayContentModifier::PaintDoubleHandle(RSCanvas& canvas)
175 {
176     if (PaintDoubleHandleWithPoint(canvas)) {
177         return;
178     }
179     PaintDoubleHandleWithRect(canvas);
180 }
181 
PaintDoubleHandleWithPoint(RSCanvas & canvas)182 bool SelectOverlayContentModifier::PaintDoubleHandleWithPoint(RSCanvas& canvas)
183 {
184     CHECK_NULL_RETURN(isPaintHandleUsePoints_, false);
185     if (firstHandleIsShow_->Get()) {
186         auto handleOnTop = !handleReverse_->Get();
187         auto centerOffset = CalculateCenterPoint(
188             firstHandlePaintInfo_.startPoint, firstHandlePaintInfo_.endPoint, GetDrawHandleRadius(), handleOnTop);
189         auto offsetY = handleOnTop ? -1.0f : 1.0f;
190         auto startPoint = firstHandlePaintInfo_.startPoint;
191         startPoint.SetY(startPoint.GetY() + offsetY);
192         HandleDrawInfo drawInfo = {
193             .startPoint = startPoint - centerOffset,
194             .endPoint = firstHandlePaintInfo_.endPoint - centerOffset,
195             .centerOffset = centerOffset,
196             .handleWidth = firstHandlePaintInfo_.width,
197             .isCircleShow = firstCircleIsShow_->Get()
198         };
199         PaintHandle(canvas, drawInfo);
200     }
201     if (secondHandleIsShow_->Get()) {
202         auto handleOnTop = handleReverse_->Get();
203         auto centerOffset = CalculateCenterPoint(
204             secondHandlePaintInfo_.startPoint, secondHandlePaintInfo_.endPoint, GetDrawHandleRadius(), handleOnTop);
205         auto offsetY = handleOnTop ? -1.0f : 1.0f;
206         auto startPoint = secondHandlePaintInfo_.startPoint;
207         startPoint.SetY(startPoint.GetY() + offsetY);
208         HandleDrawInfo drawInfo = {
209             .startPoint = startPoint - centerOffset,
210             .endPoint = secondHandlePaintInfo_.endPoint - centerOffset,
211             .centerOffset = centerOffset,
212             .handleWidth = secondHandlePaintInfo_.width,
213             .isCircleShow = secondCircleIsShow_->Get()
214         };
215         PaintHandle(canvas, drawInfo);
216     }
217     return true;
218 }
219 
PaintDoubleHandleWithRect(RSCanvas & canvas)220 void SelectOverlayContentModifier::PaintDoubleHandleWithRect(RSCanvas& canvas)
221 {
222     if (firstHandleIsShow_->Get() || isClipHandleDrawRect_) {
223         PaintHandle(canvas, firstHandle_->Get(), !handleReverse_->Get(),
224             { true, firstCircleIsShow_->Get(), IsDraggingHandle(true) });
225     }
226     if (secondHandleIsShow_->Get() || isClipHandleDrawRect_) {
227         PaintHandle(canvas, secondHandle_->Get(), handleReverse_->Get(),
228             { true, secondCircleIsShow_->Get(), IsDraggingHandle(false) });
229     }
230 }
231 
ClipViewPort(RSCanvas & canvas)232 void SelectOverlayContentModifier::ClipViewPort(RSCanvas& canvas)
233 {
234     if (!isOverlayMode_ || isClipHandleDrawRect_) {
235         return;
236     }
237     auto left = viewPort_->Get().Left();
238     auto top = viewPort_->Get().Top();
239     auto right = viewPort_->Get().Right();
240     auto bottom = viewPort_->Get().Bottom();
241     auto upHandle = GetFirstPaintRect();
242     auto upHandleIsShow = firstHandleIsShow_->Get();
243     auto downHandle = GetSecondPaintRect();
244     auto downHandleIsShow = secondHandleIsShow_->Get();
245     if (isSingleHandle_->Get()) {
246         upHandleIsShow = false;
247         downHandleIsShow = firstHandleIsShow_->Get() || secondHandleIsShow_->Get();
248         downHandle = firstHandleIsShow_->Get() ? GetFirstPaintRect()
249                                                : (secondHandleIsShow_->Get() ? GetSecondPaintRect() : downHandle);
250     } else if (handleReverse_->Get()) {
251         upHandle = GetSecondPaintRect();
252         upHandleIsShow = secondHandleIsShow_->Get();
253         downHandle = GetFirstPaintRect();
254         downHandleIsShow = firstHandleIsShow_->Get();
255     }
256     auto handleDiameter = handleRadius_->Get() * 2;
257     auto handleRadius = isPaintHandleUsePoints_ ? 0.0f : handleRadius_->Get();
258     if (upHandleIsShow) {
259         auto halfWidth = isPaintHandleUsePoints_ ? 0.0f : upHandle.Width() / 2.0f;
260         left = std::min(upHandle.Left() + halfWidth - handleRadius, left);
261         right = std::max(upHandle.Right() - halfWidth + handleRadius, right);
262         top = std::min(upHandle.Top() - handleDiameter, top);
263         bottom = std::max(upHandle.Bottom(), bottom);
264     }
265     if (downHandleIsShow) {
266         auto halfWidth = isPaintHandleUsePoints_ ? 0 : downHandle.Width() / 2.0f;
267         left = std::min(downHandle.Left() + halfWidth - handleRadius, left);
268         right = std::max(downHandle.Right() - halfWidth + handleRadius, right);
269         top = std::min(downHandle.Top(), top);
270         bottom = std::max(downHandle.Bottom() + handleDiameter, bottom);
271     }
272     auto strokeWidth = handleStrokeWidth_->Get();
273     RSRect clipInnerRect = RSRect(left - strokeWidth, top - strokeWidth, right + strokeWidth, bottom + strokeWidth);
274     canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
275 }
276 
ConvertPointsToRect(const SelectHandlePaintInfo & paintInfo) const277 RectF SelectOverlayContentModifier::ConvertPointsToRect(const SelectHandlePaintInfo& paintInfo) const
278 {
279     auto handleDiameter = handleRadius_->Get() * 2;
280     auto left = std::min(paintInfo.startPoint.GetX(), paintInfo.endPoint.GetX()) - handleDiameter;
281     auto right = std::max(paintInfo.startPoint.GetX(), paintInfo.endPoint.GetX()) + handleDiameter;
282     auto top = std::min(paintInfo.startPoint.GetY(), paintInfo.endPoint.GetY()) - handleDiameter;
283     auto bottom = std::max(paintInfo.startPoint.GetY(), paintInfo.endPoint.GetY()) + handleDiameter;
284     auto width = std::max(right - left, paintInfo.width);
285     auto height = std::max(bottom - top, paintInfo.width);
286     return RectF(OffsetF(left, top), SizeF(width, height));
287 }
288 
GetFirstPaintRect() const289 RectF SelectOverlayContentModifier::GetFirstPaintRect() const
290 {
291     if (isPaintHandleUsePoints_) {
292         return ConvertPointsToRect(firstHandlePaintInfo_);
293     }
294     return firstHandle_->Get();
295 }
296 
GetSecondPaintRect() const297 RectF SelectOverlayContentModifier::GetSecondPaintRect() const
298 {
299     if (isPaintHandleUsePoints_) {
300         return ConvertPointsToRect(secondHandlePaintInfo_);
301     }
302     return secondHandle_->Get();
303 }
304 
PaintHandle(RSCanvas & canvas,const RectF & handleRect,bool handleOnTop,const PaintHandleParams & params)305 void SelectOverlayContentModifier::PaintHandle(
306     RSCanvas& canvas, const RectF& handleRect, bool handleOnTop, const PaintHandleParams& params)
307 {
308     auto rectTopX = (handleRect.Left() + handleRect.Right()) / 2.0f;
309     auto centerOffset = OffsetF(rectTopX, 0.0f);
310     OffsetF startPoint(0.0, 0.0);
311     OffsetF endPoint(0.0, 0.0);
312     auto scaleY = isOverlayMode_ ? 1.0f : scale_.y;
313     auto handleRadius = handleRadius_->Get() * scaleY;
314     auto gap = NearEqual(scaleY, 1.0f) ? 0.0f : handleStrokeWidth_->Get() * scaleY;
315     if (handleOnTop) {
316         centerOffset.SetY(handleRect.Top() - handleRadius);
317         startPoint.SetY(handleRadius + gap);
318         endPoint.SetY(handleRadius + handleRect.Height() + gap);
319     } else {
320         centerOffset.SetY(handleRect.Bottom() + handleRadius);
321         startPoint.SetY(-handleRadius - gap);
322         endPoint.SetY(-handleRadius - handleRect.Height() - gap);
323     }
324     auto checkCircleIsShow = !isClipHandleDrawRect_ || CheckHandleCircleIsShow(handleRect);
325     HandleDrawInfo drawInfo = { .startPoint = startPoint,
326         .endPoint = endPoint,
327         .centerOffset = centerOffset,
328         .handleWidth = handleRect.Width(),
329         .isHandleLineShow = params.isHandleLineShow,
330         .isCircleShow = params.isCircleShow && checkCircleIsShow };
331     canvas.Save();
332     ClipHandleDrawRect(canvas, handleRect, handleOnTop, params.isDragging);
333     PaintHandle(canvas, drawInfo);
334     canvas.Restore();
335 }
336 
PaintHandle(RSCanvas & canvas,const HandleDrawInfo & handleInfo)337 void SelectOverlayContentModifier::PaintHandle(RSCanvas& canvas, const HandleDrawInfo& handleInfo)
338 {
339     auto scaleX = isOverlayMode_ ? 1.0f : scale_.x;
340     auto scaleY = isOverlayMode_ ? 1.0f : scale_.y;
341     canvas.Save();
342     canvas.Translate(handleInfo.centerOffset.GetX(), handleInfo.centerOffset.GetY());
343     Color handleColor = handleColor_->Get();
344     handleColor = handleColor.BlendOpacity(handleOpacity_->Get());
345     if (handleInfo.isCircleShow) {
346         canvas.Save();
347         canvas.Scale(scaleX, scaleY);
348         // Paint inner circle.
349         Color innerHandleColor = innerHandleColor_->Get();
350         innerHandleColor = innerHandleColor.BlendOpacity(handleOpacity_->Get());
351         RSBrush brush;
352         brush.SetAntiAlias(true);
353         brush.SetColor(innerHandleColor.GetValue());
354         canvas.AttachBrush(brush);
355         canvas.DrawCircle(RSPoint(0.0, 0.0), innerHandleRadius_->Get());
356         canvas.DetachBrush();
357         // Paint outer hollow circle.
358         float strokeWidth = handleStrokeWidth_->Get();
359         RSPen strokePen;
360         strokePen.SetAntiAlias(true);
361         strokePen.SetColor(handleColor.GetValue());
362         strokePen.SetWidth(strokeWidth);
363         canvas.AttachPen(strokePen);
364         canvas.DrawCircle(RSPoint(0.0, 0.0), handleRadius_->Get());
365         canvas.DetachPen();
366         canvas.Restore();
367     }
368     float handleLineWidth = handleInfo.handleWidth;
369     if (handleInfo.isHandleLineShow && !NearZero(handleLineWidth)) {
370         canvas.Save();
371         canvas.Scale(scaleX, 1.0f);
372         RSPen pen;
373         pen.SetAntiAlias(true);
374         // Paint line of handle.
375         pen.SetColor(handleColor.GetValue());
376         pen.SetWidth(handleLineWidth);
377         pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
378         canvas.AttachPen(pen);
379         canvas.DrawLine(RSPoint(handleInfo.startPoint.GetX(), handleInfo.startPoint.GetY()),
380             RSPoint(handleInfo.endPoint.GetX(), handleInfo.endPoint.GetY()));
381         canvas.DetachPen();
382         canvas.Restore();
383     }
384     canvas.Restore();
385 }
386 
CheckHandleCircleIsShow(const RectF & handleRect)387 bool SelectOverlayContentModifier::CheckHandleCircleIsShow(const RectF& handleRect)
388 {
389     auto viewPort = viewPort_->Get();
390     return GreatOrEqual(handleRect.Right(), viewPort.Left() - VIEW_PORT_MODIFICATION_VALUE) &&
391            LessOrEqual(handleRect.Left(), viewPort.Right() + VIEW_PORT_MODIFICATION_VALUE);
392 }
393 
ClipHandleDrawRect(RSCanvas & canvas,const RectF & handleRect,bool handleOnTop,bool isDragging)394 void SelectOverlayContentModifier::ClipHandleDrawRect(
395     RSCanvas& canvas, const RectF& handleRect, bool handleOnTop, bool isDragging)
396 {
397     if (!isClipHandleDrawRect_) {
398         return;
399     }
400     auto extendDimension = handleRadius_->Get() + handleStrokeWidth_->Get() / 2.0f;
401     auto viewPort = viewPort_->Get();
402     auto left = GreatOrEqual(handleRect.Right(), viewPort.Left() - VIEW_PORT_MODIFICATION_VALUE)
403                     ? handleRect.Left() - extendDimension
404                     : viewPort.Left();
405     auto topInViewPort = GreatOrEqual(handleRect.Top(), viewPort.Top() - VIEW_PORT_MODIFICATION_VALUE);
406     // 扩大裁剪区域绘制手柄末端的圆弧
407     auto top = viewPort.Top() - (topInViewPort ? handleRect.Width() / 2.0f : 0.0f);
408     // 扩大裁剪区域绘制手柄圆圈
409     if (handleOnTop && topInViewPort) {
410         top = viewPort.Top() - extendDimension * 2.0f;
411     }
412     auto right = LessOrEqual(handleRect.Left(), viewPort.Right() + VIEW_PORT_MODIFICATION_VALUE)
413                      ? handleRect.Right() + extendDimension
414                      : viewPort.Right();
415     auto bottomInViewPort = LessOrEqual(handleRect.Bottom(), viewPort.Bottom() + VIEW_PORT_MODIFICATION_VALUE);
416     auto bottom = viewPort.Bottom() + (bottomInViewPort ? handleRect.Width() / 2.0f : 0.0f);
417     if (!handleOnTop && bottomInViewPort) {
418         bottom = viewPort.Bottom() + extendDimension * 2.0f;
419     }
420     RSRect clipInnerRect = RSRect(left, top, right, bottom);
421     if (isDragging) {
422         RSRect draggingRect = RSRect(handleRect.Left(), handleRect.Top(), handleRect.Right(), handleRect.Bottom());
423         clipInnerRect.Join(draggingRect);
424     }
425     canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
426 }
427 
IsDraggingHandle(bool isFirst)428 bool SelectOverlayContentModifier::IsDraggingHandle(bool isFirst)
429 {
430     auto overlayPattern = AceType::DynamicCast<SelectOverlayPattern>(pattern_.Upgrade());
431     CHECK_NULL_RETURN(overlayPattern, false);
432     return overlayPattern->IsDraggingHandle(isFirst);
433 }
434 } // namespace OHOS::Ace::NG
435