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_field/text_field_overlay_modifier.h"
17 
18 #include "base/utils/utils.h"
19 #include "core/components_ng/base/modifier.h"
20 #include "core/components_ng/pattern/text_field/text_field_model.h"
21 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
22 #include "core/components_ng/render/adapter/pixelmap_image.h"
23 #include "core/components_ng/render/drawing.h"
24 #include "core/components_ng/render/drawing_prop_convertor.h"
25 #include "core/components_ng/render/image_painter.h"
26 
27 namespace OHOS::Ace::NG {
TextFieldOverlayModifier(const WeakPtr<OHOS::Ace::NG::Pattern> & pattern,WeakPtr<ScrollEdgeEffect> && edgeEffect)28 TextFieldOverlayModifier::TextFieldOverlayModifier(
29     const WeakPtr<OHOS::Ace::NG::Pattern>& pattern, WeakPtr<ScrollEdgeEffect>&& edgeEffect)
30     : pattern_(pattern), edgeEffect_(edgeEffect)
31 {
32     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
33     CHECK_NULL_VOID(textFieldPattern);
34     auto theme = textFieldPattern->GetTheme();
35     CHECK_NULL_VOID(theme);
36     cursorColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color()));
37     cursorWidth_ =
38         AceType::MakeRefPtr<AnimatablePropertyFloat>(static_cast<float>(theme->GetCursorWidth().ConvertToPx()));
39     selectedColor_ = AceType::MakeRefPtr<AnimatablePropertyColor>(LinearColor(Color()));
40     cursorVisible_ = AceType::MakeRefPtr<PropertyBool>(false);
41     showSelect_ = AceType::MakeRefPtr<PropertyBool>(false);
42     contentSize_ = AceType::MakeRefPtr<PropertySizeF>(SizeF());
43     contentOffset_ = AceType::MakeRefPtr<PropertyOffsetF>(OffsetF());
44     cursorOffset_ = AceType::MakeRefPtr<PropertyOffsetF>(textFieldPattern->GetCaretOffset());
45     frameSize_ = AceType::MakeRefPtr<PropertySizeF>(SizeF());
46     currentOffset_ = AceType::MakeRefPtr<PropertyFloat>(0.0f);
47     underlineWidth_ = AceType::MakeRefPtr<PropertyFloat>(0.0f);
48     underlineColor_ = AceType::MakeRefPtr<PropertyColor>(Color());
49     changeSelectedRects_ = AceType::MakeRefPtr<PropertyBool>(false);
50     firstHandleOffset_ = AceType::MakeRefPtr<PropertyOffsetF>(OffsetF());
51     secondHandleOffset_ = AceType::MakeRefPtr<PropertyOffsetF>(OffsetF());
52     showPreviewText_ = AceType::MakeRefPtr<PropertyBool>(false);
53     changePreviewTextRects_ = AceType::MakeRefPtr<PropertyBool>(false);
54     previewTextDecorationColor_ = AceType::MakeRefPtr<PropertyColor>(Color());
55     previewTextStyle_ = PreviewTextStyle::NORMAL;
56     contentChange_ = AceType::MakeRefPtr<PropertyBool>(false);
57 
58     AttachProperty(cursorColor_);
59     AttachProperty(cursorWidth_);
60     AttachProperty(selectedColor_);
61     AttachProperty(cursorVisible_);
62     AttachProperty(showSelect_);
63     AttachProperty(contentSize_);
64     AttachProperty(contentOffset_);
65     AttachProperty(cursorOffset_);
66     AttachProperty(frameSize_);
67     AttachProperty(currentOffset_);
68     AttachProperty(underlineWidth_);
69     AttachProperty(underlineColor_);
70     AttachProperty(changeSelectedRects_);
71     AttachProperty(firstHandleOffset_);
72     AttachProperty(secondHandleOffset_);
73     AttachProperty(showPreviewText_);
74     AttachProperty(changePreviewTextRects_);
75     AttachProperty(previewTextDecorationColor_);
76     AttachProperty(contentChange_);
77 }
78 
ContentChange()79 void TextFieldOverlayModifier::ContentChange()
80 {
81     CHECK_NULL_VOID(contentChange_);
82     contentChange_->Set(!contentChange_->Get());
83 }
84 
SetFirstHandleOffset(const OffsetF & offset)85 void TextFieldOverlayModifier::SetFirstHandleOffset(const OffsetF& offset)
86 {
87     firstHandleOffset_->Set(offset);
88 }
89 
SetSecondHandleOffset(const OffsetF & offset)90 void TextFieldOverlayModifier::SetSecondHandleOffset(const OffsetF& offset)
91 {
92     secondHandleOffset_->Set(offset);
93 }
94 
onDraw(DrawingContext & context)95 void TextFieldOverlayModifier::onDraw(DrawingContext& context)
96 {
97     auto& canvas = context.canvas;
98     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
99         canvas.Save();
100         RSRect clipRect;
101         std::vector<RSPoint> clipRadius;
102         GetFrameRectClip(clipRect, clipRadius);
103         canvas.ClipRoundRect(clipRect, clipRadius, true);
104     }
105     PaintCursor(context);
106     PaintSelection(context);
107     if (Container::GreatOrEqualAPIVersion(PlatformVersion::VERSION_ELEVEN)) {
108         canvas.Restore();
109     }
110     PaintScrollBar(context);
111     PaintEdgeEffect(frameSize_->Get(), context.canvas);
112     PaintUnderline(context.canvas);
113     PaintPreviewTextDecoration(context);
114 }
115 
GetFrameRectClip(RSRect & clipRect,std::vector<RSPoint> & clipRadius)116 void TextFieldOverlayModifier::GetFrameRectClip(RSRect& clipRect, std::vector<RSPoint>& clipRadius)
117 {
118     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
119     CHECK_NULL_VOID(textFieldPattern);
120     auto host = textFieldPattern->GetHost();
121     CHECK_NULL_VOID(host);
122     auto renderContext = host->GetRenderContext();
123     CHECK_NULL_VOID(renderContext);
124     auto textFrameRect = textFieldPattern->GetFrameRect();
125     clipRect = RSRect(0.0f, 0.0f, textFrameRect.Width(), textFrameRect.Height());
126     auto radius = renderContext->GetBorderRadius().value_or(BorderRadiusProperty());
127     auto radiusTopLeft = RSPoint(static_cast<float>(radius.radiusTopLeft.value_or(0.0_vp).ConvertToPx()),
128         static_cast<float>(radius.radiusTopLeft.value_or(0.0_vp).ConvertToPx()));
129     clipRadius.emplace_back(radiusTopLeft);
130     auto radiusTopRight = RSPoint(static_cast<float>(radius.radiusTopRight.value_or(0.0_vp).ConvertToPx()),
131         static_cast<float>(radius.radiusTopRight.value_or(0.0_vp).ConvertToPx()));
132     clipRadius.emplace_back(radiusTopRight);
133     auto radiusBottomRight = RSPoint(static_cast<float>(radius.radiusBottomRight.value_or(0.0_vp).ConvertToPx()),
134         static_cast<float>(radius.radiusBottomRight.value_or(0.0_vp).ConvertToPx()));
135     clipRadius.emplace_back(radiusBottomRight);
136     auto radiusBottomLeft = RSPoint(static_cast<float>(radius.radiusBottomLeft.value_or(0.0_vp).ConvertToPx()),
137         static_cast<float>(radius.radiusBottomLeft.value_or(0.0_vp).ConvertToPx()));
138     clipRadius.emplace_back(radiusBottomLeft);
139 }
140 
PaintUnderline(RSCanvas & canvas) const141 void TextFieldOverlayModifier::PaintUnderline(RSCanvas& canvas) const
142 {
143     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
144     CHECK_NULL_VOID(textFieldPattern);
145     auto layoutProperty = textFieldPattern->GetLayoutProperty<TextFieldLayoutProperty>();
146     CHECK_NULL_VOID(layoutProperty);
147     if (!(layoutProperty->GetShowUnderlineValue(false) && textFieldPattern->IsUnspecifiedOrTextType())) {
148         return;
149     }
150     if (textFieldPattern->IsNormalInlineState() && textFieldPattern->HasFocus()) {
151         return;
152     }
153     auto contentRect = textFieldPattern->GetContentRect();
154     auto textFrameRect = textFieldPattern->GetFrameRect();
155     auto responseArea = textFieldPattern->GetResponseArea();
156     auto responseAreaWidth = responseArea ? responseArea->GetAreaRect().Width() : 0.0f;
157     auto clearNodeResponseArea = textFieldPattern->GetCleanNodeResponseArea();
158     responseAreaWidth += clearNodeResponseArea ? clearNodeResponseArea->GetAreaRect().Width() : 0.0f;
159     auto hasResponseArea = GreatNotEqual(responseAreaWidth, 0.0f);
160     auto isRTL = layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
161     Point leftPoint, rightPoint;
162     if (isRTL) {
163         leftPoint.SetX(hasResponseArea ? 0.0 : contentRect.Left());
164         rightPoint.SetX(contentRect.Right());
165     } else {
166         leftPoint.SetX(contentRect.Left());
167         rightPoint.SetX(hasResponseArea ? textFrameRect.Width() : contentRect.Right());
168     }
169 
170     leftPoint.SetY(textFrameRect.Height());
171     rightPoint.SetY(textFrameRect.Height());
172 
173     RSPen pen;
174     pen.SetColor(ToRSColor(underlineColor_->Get()));
175     pen.SetWidth(underlineWidth_->Get());
176     pen.SetAntiAlias(true);
177     canvas.AttachPen(pen);
178     canvas.DrawLine(ToRSPoint(PointF(leftPoint.GetX(), leftPoint.GetY())),
179         ToRSPoint(PointF(rightPoint.GetX(), rightPoint.GetY())));
180     canvas.DetachPen();
181 }
182 
PaintSelection(DrawingContext & context) const183 void TextFieldOverlayModifier::PaintSelection(DrawingContext& context) const
184 {
185     if (!showSelect_->Get() && !needPaintSelect_) {
186         return;
187     }
188     auto& canvas = context.canvas;
189     canvas.Save();
190     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
191     CHECK_NULL_VOID(textFieldPattern);
192     auto host = textFieldPattern->GetHost();
193     CHECK_NULL_VOID(host);
194     auto pipelineContext = host->GetContext();
195     CHECK_NULL_VOID(pipelineContext);
196     auto themeManager = pipelineContext->GetThemeManager();
197     CHECK_NULL_VOID(themeManager);
198     auto theme = themeManager->GetTheme<TextFieldTheme>();
199     RSBrush brush;
200     brush.SetAntiAlias(true);
201     brush.SetColor(ToRSColor(selectedColor_->Get()));
202     canvas.AttachBrush(brush);
203     auto paintOffset = textFieldPattern->GetContentRect().GetOffset();
204     auto textBoxes = textFieldPattern->GetTextBoxes();
205     auto textRect = textFieldPattern->GetTextRect();
206     bool isTextArea = textFieldPattern->IsTextArea();
207     float clipRectHeight = 0.0f;
208     clipRectHeight = paintOffset.GetY() + contentSize_->Get().Height();
209     RSRect clipInnerRect;
210     auto defaultStyle = !textFieldPattern->IsNormalInlineState() || isTextArea;
211     if (defaultStyle) {
212         clipInnerRect = RSRect(paintOffset.GetX(), paintOffset.GetY(),
213             paintOffset.GetX() + contentSize_->Get().Width() + textFieldPattern->GetInlinePadding(), clipRectHeight);
214         canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
215     } else {
216         clipInnerRect = RSRect(paintOffset.GetX(), 0.0f, paintOffset.GetX() + contentSize_->Get().Width(),
217             textFieldPattern->GetFrameRect().Height());
218         canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
219     }
220     // for default style, selection height is equal to the content height
221     for (const auto& textBox : textBoxes) {
222         canvas.DrawRect(RSRect(textBox.Left() + (isTextArea ? contentOffset_->Get().GetX() : textRect.GetX()),
223             defaultStyle
224                 ? (textBox.Top() + (isTextArea ? textRect.GetY() : contentOffset_->Get().GetY()))
225                 : 0.0f,
226             textBox.Right() + (isTextArea ? contentOffset_->Get().GetX() : textRect.GetX()),
227             defaultStyle
228                 ? (textBox.Bottom() + (isTextArea ? textRect.GetY() : contentOffset_->Get().GetY()))
229                 : textFieldPattern->GetFrameRect().Height()));
230     }
231     canvas.DetachBrush();
232     canvas.Restore();
233 }
234 
PaintCursor(DrawingContext & context) const235 void TextFieldOverlayModifier::PaintCursor(DrawingContext& context) const
236 {
237     float cursorWidth = static_cast<float>(cursorWidth_->Get());
238     if (NearZero(cursorWidth)) {
239         return; // will not draw cursor
240     }
241 
242     auto& canvas = context.canvas;
243     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
244     CHECK_NULL_VOID(textFieldPattern);
245     auto magnifierController = textFieldPattern->GetMagnifierController();
246     CHECK_NULL_VOID(magnifierController);
247     if (!cursorVisible_->Get() || textFieldPattern->IsSelected()) {
248         return;
249     }
250     canvas.Save();
251 
252     RSPen pen;
253     pen.SetAntiAlias(true);
254     pen.SetWidth(cursorWidth);
255     pen.SetCapStyle(RSPen::CapStyle::ROUND_CAP);
256     pen.SetColor(ToRSColor(cursorColor_->Get()));
257     canvas.AttachPen(pen);
258     auto paintOffset = contentOffset_->Get();
259     ACE_LAYOUT_SCOPED_TRACE("PaintCursor[offset:%f, %f]", paintOffset.GetX(), paintOffset.GetY());
260     float clipRectHeight = 0.0f;
261     clipRectHeight = paintOffset.GetY() + contentSize_->Get().Height();
262     RSRect clipInnerRect(paintOffset.GetX(), paintOffset.GetY(),
263         // add extra clip space for cases such as auto width
264         paintOffset.GetX() + contentSize_->Get().Width() +
265             (LessOrEqual(contentSize_->Get().Width(), 0.0) ? cursorWidth_->Get() : 0.0f),
266         clipRectHeight);
267     canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
268 
269     auto caretRect = textFieldPattern->GetCaretRect();
270     float midPosX = caretRect.GetX() + cursorWidth / 2;
271     float startPosY = caretRect.GetY();
272     float endPosY = caretRect.GetY() + caretRect.Height();
273     float roundCapRadius = static_cast<float>(cursorWidth_->Get()) / 2;
274     canvas.DrawLine(RSPoint(midPosX, startPosY + roundCapRadius),
275         RSPoint(midPosX, endPosY - roundCapRadius));
276     canvas.DetachPen();
277     canvas.Restore();
278 }
279 
PaintEdgeEffect(const SizeF & frameSize,RSCanvas & canvas)280 void TextFieldOverlayModifier::PaintEdgeEffect(const SizeF& frameSize, RSCanvas& canvas)
281 {
282     auto edgeEffect = edgeEffect_.Upgrade();
283     CHECK_NULL_VOID(edgeEffect);
284     edgeEffect->Paint(canvas, frameSize, { 0.0f, 0.0f });
285 }
286 
PaintScrollBar(DrawingContext & context)287 void TextFieldOverlayModifier::PaintScrollBar(DrawingContext& context)
288 {
289     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
290     CHECK_NULL_VOID(textFieldPattern);
291     if (textFieldPattern->GetScrollBarVisible() && textFieldPattern->IsTextArea()) {
292         ScrollBarOverlayModifier::onDraw(context);
293     }
294 }
295 
PaintPreviewTextDecoration(DrawingContext & context) const296 void TextFieldOverlayModifier::PaintPreviewTextDecoration(DrawingContext& context) const
297 {
298     if (previewTextStyle_ != PreviewTextStyle::UNDERLINE ||
299         (!showPreviewText_->Get() && !needPaintPreviewText)) {
300         return;
301     }
302 
303     auto& canvas = context.canvas;
304     canvas.Save();
305     auto textFieldPattern = DynamicCast<TextFieldPattern>(pattern_.Upgrade());
306     CHECK_NULL_VOID(textFieldPattern);
307     auto host = textFieldPattern->GetHost();
308     CHECK_NULL_VOID(host);
309     auto pipelineContext = host->GetContext();
310     CHECK_NULL_VOID(pipelineContext);
311     auto themeManager = pipelineContext->GetThemeManager();
312     CHECK_NULL_VOID(themeManager);
313     auto theme = themeManager->GetTheme<TextFieldTheme>();
314 
315     auto textRect = textFieldPattern->GetTextRect();
316     bool isTextArea = textFieldPattern->IsTextArea();
317 
318     float offsetX = isTextArea ? contentOffset_->Get().GetX() : textRect.GetX();
319     float offsetY = isTextArea ? textRect.GetY() : contentOffset_->Get().GetY();
320     auto previewTextRect = textFieldPattern->GetPreviewTextRects();
321     if (previewTextRect.empty()) {
322         return;
323     }
324 
325     auto paintOffset = contentOffset_->Get();
326     float clipRectHeight = paintOffset.GetY() + contentSize_->Get().Height();
327     RSRect clipInnerRect;
328     auto defaultStyle = !textFieldPattern->IsNormalInlineState() || isTextArea;
329     if (defaultStyle) {
330         clipInnerRect = RSRect(paintOffset.GetX(), paintOffset.GetY(),
331             paintOffset.GetX() + contentSize_->Get().Width() + textFieldPattern->GetInlinePadding(), clipRectHeight);
332         canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
333     } else {
334         clipInnerRect = RSRect(paintOffset.GetX(), 0.0f, paintOffset.GetX() + contentSize_->Get().Width(),
335             textFieldPattern->GetFrameRect().Height());
336         canvas.ClipRect(clipInnerRect, RSClipOp::INTERSECT);
337     }
338 
339     auto underlineWidth = textFieldPattern->GetPreviewUnderlineWidth();
340     RSBrush brush;
341     brush.SetAntiAlias(true);
342     brush.SetColor(ToRSColor(previewTextDecorationColor_->Get()));
343     canvas.AttachBrush(brush);
344     for (const auto& drawRect : previewTextRect) {
345         RSRect rect(drawRect.Left() + offsetX, drawRect.Bottom() + offsetY - underlineWidth,
346                 drawRect.Right() + offsetX, drawRect.Bottom() + offsetY);
347         canvas.DrawRoundRect(RSRoundRect(rect, underlineWidth / 2, underlineWidth / 2));
348     }
349     canvas.DetachBrush();
350     canvas.Restore();
351 }
352 
SetCursorColor(Color & value)353 void TextFieldOverlayModifier::SetCursorColor(Color& value)
354 {
355     cursorColor_->Set(LinearColor(value));
356 }
357 
SetCursorWidth(float value)358 void TextFieldOverlayModifier::SetCursorWidth(float value)
359 {
360     cursorWidth_->Set(value);
361 }
362 
SetSelectedBackGroundColor(Color & value)363 void TextFieldOverlayModifier::SetSelectedBackGroundColor(Color& value)
364 {
365     selectedColor_->Set(LinearColor(value));
366 }
367 
SetCursorVisible(bool value)368 void TextFieldOverlayModifier::SetCursorVisible(bool value)
369 {
370     cursorVisible_->Set(value);
371 }
372 
SetContentSize(SizeF & value)373 void TextFieldOverlayModifier::SetContentSize(SizeF& value)
374 {
375     contentSize_->Set(value);
376 }
377 
SetContentOffset(OffsetF & value)378 void TextFieldOverlayModifier::SetContentOffset(OffsetF& value)
379 {
380     contentOffset_->Set(value);
381 }
382 
SetCursorOffset(const OffsetF & value)383 void TextFieldOverlayModifier::SetCursorOffset(const OffsetF& value)
384 {
385     cursorOffset_->Set(value);
386 }
387 
SetInputStyle(InputStyle & value)388 void TextFieldOverlayModifier::SetInputStyle(InputStyle& value)
389 {
390     inputStyle_ = value;
391 }
392 
SetFrameSize(const SizeF & value)393 void TextFieldOverlayModifier::SetFrameSize(const SizeF& value)
394 {
395     frameSize_->Set(value);
396 }
397 
SetCurrentOffset(float value)398 void TextFieldOverlayModifier::SetCurrentOffset(float value)
399 {
400     currentOffset_->Set(value);
401 }
402 
SetUnderlineWidth(float value)403 void TextFieldOverlayModifier::SetUnderlineWidth(float value)
404 {
405     underlineWidth_->Set(value);
406 }
407 
SetUnderlineColor(const Color & value)408 void TextFieldOverlayModifier::SetUnderlineColor(const Color& value)
409 {
410     underlineColor_->Set(value);
411 }
412 
SetScrollBar(const RefPtr<ScrollBar> & scrollBar)413 void TextFieldOverlayModifier::SetScrollBar(const RefPtr<ScrollBar>& scrollBar)
414 {
415     scrollBar_ = scrollBar;
416 }
417 
SetChangeSelectedRects(bool value)418 void TextFieldOverlayModifier::SetChangeSelectedRects(bool value)
419 {
420     if (value) {
421         changeSelectedRects_->Set(!changeSelectedRects_->Get());
422     }
423     needPaintSelect_ = value;
424 }
425 
SetShowSelect(bool value)426 void TextFieldOverlayModifier::SetShowSelect(bool value)
427 {
428     showSelect_->Set(value);
429 }
SetShowPreviewTextDecoration(bool value)430 void TextFieldOverlayModifier::SetShowPreviewTextDecoration(bool value)
431 {
432     showPreviewText_->Set(value);
433 }
SetPreviewTextRects(bool value)434 void TextFieldOverlayModifier::SetPreviewTextRects(bool value)
435 {
436     if (value) {
437         changePreviewTextRects_->Set(!changePreviewTextRects_->Get());
438     }
439     needPaintPreviewText = value;
440 }
SetPreviewTextDecorationColor(const Color & value)441 void TextFieldOverlayModifier::SetPreviewTextDecorationColor(const Color& value)
442 {
443     previewTextDecorationColor_->Set(value);
444 }
SetPreviewTextStyle(PreviewTextStyle style)445 void TextFieldOverlayModifier::SetPreviewTextStyle(PreviewTextStyle style)
446 {
447     previewTextStyle_ = style;
448 }
449 } // namespace OHOS::Ace::NG
450