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