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