1 /*
2 * Copyright (c) 2024 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_component_decorator.h"
17
18 #include "core/components_ng/pattern/text/text_layout_property.h"
19 #include "frameworks/base/utils/utils.h"
20 #include "frameworks/core/components_ng/pattern/text_field/text_field_pattern.h"
21 #include "core/components_ng/pattern/text/text_pattern.h"
22
23 namespace OHOS::Ace::NG {
24
25 namespace {
26
27 constexpr int32_t DEFAULT_MODE = -1;
28 constexpr int32_t SHOW_COUNTER_PERCENT = 100;
29 const std::string INSPECTOR_PREFIX = "__SearchField__";
30 const std::string ERRORNODE_PREFIX = "ErrorNodeField__";
31
32 } // namespace
33
TextComponentDecorator(const RefPtr<FrameNode> & decoratedNode)34 TextComponentDecorator::TextComponentDecorator(const RefPtr<FrameNode>& decoratedNode)
35 : decoratedNode_(decoratedNode), textNode_(nullptr)
36 {
37 BuildDecorator();
38 }
39
~TextComponentDecorator()40 TextComponentDecorator::~TextComponentDecorator()
41 {
42 CleanDecorator();
43 }
44
BuildDecorator()45 void TextComponentDecorator::BuildDecorator()
46 {
47 auto decoratedNode = decoratedNode_.Upgrade();
48 CHECK_NULL_VOID(decoratedNode);
49 auto textNode = FrameNode::GetOrCreateFrameNode(V2::TEXT_ETS_TAG,
50 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<TextPattern>(); });
51 textNode_ = textNode;
52 CHECK_NULL_VOID(textNode);
53 textNode->MountToParent(decoratedNode);
54 }
55
CleanDecorator()56 void TextComponentDecorator::CleanDecorator()
57 {
58 auto decoratedNode = decoratedNode_.Upgrade();
59 CHECK_NULL_VOID(decoratedNode);
60 auto textNode = textNode_.Upgrade();
61 CHECK_NULL_VOID(textNode);
62 decoratedNode->RemoveChild(textNode);
63 decoratedNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
64 }
65
66 // call after measure
GetDecoratorHeight() const67 float TextComponentDecorator::GetDecoratorHeight() const
68 {
69 auto textNode = textNode_.Upgrade();
70 CHECK_NULL_RETURN(textNode, 0.0);
71 auto geometryNode = textNode->GetGeometryNode();
72 CHECK_NULL_RETURN(geometryNode, 0.0);
73 return geometryNode->GetFrameRect().Height();
74 }
75
GetContentWidth() const76 float TextComponentDecorator::GetContentWidth() const
77 {
78 auto textNode = textNode_.Upgrade();
79 CHECK_NULL_RETURN(textNode, 0.0);
80 auto counterTextPattern = textNode->GetPattern<TextPattern>();
81 CHECK_NULL_RETURN(counterTextPattern, 0.0f);
82
83 auto counterWidth = 0.0f;
84 auto counterParagraphs = counterTextPattern->GetParagraphs();
85 for (auto &&info : counterParagraphs) {
86 if (info.paragraph) {
87 float width = info.paragraph->GetLongestLine();
88 counterWidth = std::max(counterWidth, width);
89 }
90 }
91 return counterWidth;
92 }
93
UpdateTextFieldMargin()94 void CounterDecorator::UpdateTextFieldMargin()
95 {
96 auto decoratedNode = decoratedNode_.Upgrade();
97 CHECK_NULL_VOID(decoratedNode);
98 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty<TextFieldLayoutProperty>();
99 CHECK_NULL_VOID(textFieldLayoutProperty);
100 auto textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
101 CHECK_NULL_VOID(textFieldPattern);
102 auto pipeline = decoratedNode->GetContext();
103 CHECK_NULL_VOID(pipeline);
104 auto theme = textFieldPattern->GetTheme();
105 CHECK_NULL_VOID(theme);
106 // Update TextInput's bottom margin, Counter is inside of TextArea, no need to update its bottom margin
107 if (!textFieldPattern->IsTextArea() && textFieldPattern->IsShowCount()) {
108 const auto& currentMargin = textFieldLayoutProperty->GetMarginProperty();
109
110 auto counterHeight = MeasureTextNodeHeight();
111 auto curFontScale = pipeline->GetFontScale();
112 auto standardHeight = theme->GetStandardCounterTextMargin().ConvertToPx();
113 auto otherHeight = theme->GetCounterTextTopMargin().ConvertToPx() +
114 theme->GetCounterTextBottomMargin().ConvertToPx() + counterHeight;
115 auto marginHeight = (NearEqual(curFontScale, 1.0f)) ? standardHeight : otherHeight;
116 Dimension newBottomMargin(marginHeight, DimensionUnit::PX);
117
118 if (!currentMargin) {
119 MarginProperty margin;
120 margin.bottom = CalcLength(newBottomMargin);
121 textFieldLayoutProperty->UpdateMargin(margin);
122 } else {
123 auto currentBottomMargin = currentMargin->bottom->GetDimension();
124 if (LessNotEqual(currentBottomMargin.ConvertToPx(), newBottomMargin.ConvertToPx())) {
125 currentMargin->bottom = CalcLength(newBottomMargin);
126 }
127 textFieldLayoutProperty->UpdateMargin(*currentMargin);
128 }
129 }
130 }
131
MeasureTextNodeHeight()132 float CounterDecorator::MeasureTextNodeHeight()
133 {
134 auto decoratedNode = decoratedNode_.Upgrade();
135 CHECK_NULL_RETURN(decoratedNode, 0.0);
136 auto textNode = textNode_.Upgrade();
137 CHECK_NULL_RETURN(textNode, 0.0);
138 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty<TextFieldLayoutProperty>();
139 CHECK_NULL_RETURN(textFieldLayoutProperty, 0.0);
140 auto textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
141 CHECK_NULL_RETURN(textFieldPattern, 0.0);
142 auto contentController = textFieldPattern->GetTextContentController();
143 CHECK_NULL_RETURN(contentController, 0.0);
144 auto counterGeometryNode = textNode->GetGeometryNode();
145 CHECK_NULL_RETURN(counterGeometryNode, 0.0);
146
147 // For efficiency: keep content same, make full use of rs cache.
148 auto textContent = contentController->GetTextValue();
149 auto textLength = static_cast<uint32_t>(textContent.length());
150 auto maxLength = static_cast<uint32_t>(textFieldLayoutProperty->GetMaxLengthValue(Infinity<uint32_t>()));
151 UpdateCounterContentAndStyle(textLength, maxLength);
152 // Both the non-backend rendering process and the backend rendering process will be called.
153 // note using this statement have any impact on the back-end rendering process.
154 ScopedLayout scope(decoratedNode->GetContext());
155 textNode->Measure(LayoutConstraintF());
156 return counterGeometryNode->GetFrameRect().Height();
157 }
158
UpdateCounterContentAndStyle(uint32_t textLength,uint32_t maxLength,bool isVisible)159 void CounterDecorator::UpdateCounterContentAndStyle(uint32_t textLength, uint32_t maxLength, bool isVisible)
160 {
161 auto decoratedNode = decoratedNode_.Upgrade();
162 CHECK_NULL_VOID(decoratedNode);
163 auto textNode = textNode_.Upgrade();
164 CHECK_NULL_VOID(textNode);
165 auto textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
166 CHECK_NULL_VOID(textFieldPattern);
167 auto theme = textFieldPattern->GetTheme();
168 CHECK_NULL_VOID(theme);
169 auto counterNodeLayoutProperty = DynamicCast<TextLayoutProperty>(textNode->GetLayoutProperty());
170 CHECK_NULL_VOID(counterNodeLayoutProperty);
171 auto context = textNode->GetRenderContext();
172 CHECK_NULL_VOID(context);
173 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty<TextFieldLayoutProperty>();
174 CHECK_NULL_VOID(textFieldLayoutProperty);
175 std::string counterText;
176 if (isVisible) {
177 counterText = std::to_string(textLength) + "/" + std::to_string(maxLength);
178 }
179 TextStyle countTextStyle = (textFieldPattern->GetShowCounterStyleValue() && textFieldPattern->HasFocus()) ?
180 theme->GetOverCountTextStyle() :
181 theme->GetCountTextStyle();
182 counterNodeLayoutProperty->UpdateContent(counterText);
183
184 if (textFieldLayoutProperty->HasMaxFontScale()) {
185 auto maxFontScale = textFieldLayoutProperty->GetMaxFontScale().value();
186 counterNodeLayoutProperty->UpdateMaxFontScale(maxFontScale);
187 }
188 if (textFieldLayoutProperty->HasMinFontScale()) {
189 auto minFontScale = textFieldLayoutProperty->GetMinFontScale().value();
190 counterNodeLayoutProperty->UpdateMinFontScale(minFontScale);
191 }
192 counterNodeLayoutProperty->UpdateFontSize(countTextStyle.GetFontSize());
193 counterNodeLayoutProperty->UpdateTextColor(countTextStyle.GetTextColor());
194 counterNodeLayoutProperty->UpdateFontWeight(countTextStyle.GetFontWeight());
195 counterNodeLayoutProperty->UpdateTextAlign(GetCounterNodeAlignment());
196 counterNodeLayoutProperty->UpdateMaxLines(theme->GetCounterTextMaxline());
197 context->UpdateForegroundColor(countTextStyle.GetTextColor());
198 }
199
GetCounterNodeAlignment()200 TextAlign CounterDecorator::GetCounterNodeAlignment()
201 {
202 auto decoratedNode = decoratedNode_.Upgrade();
203 CHECK_NULL_RETURN(decoratedNode, TextAlign::END);
204 RefPtr<LayoutProperty> property = decoratedNode->GetLayoutProperty();
205 CHECK_NULL_RETURN(property, TextAlign::END);
206
207 bool isRTL = AceApplicationInfo::GetInstance().IsRightToLeft();
208 TextDirection layoutDirection = property->GetLayoutDirection();
209 if ((layoutDirection == TextDirection::RTL && !isRTL) ||
210 (layoutDirection == TextDirection::LTR && isRTL)) {
211 return TextAlign::START;
212 }
213 return TextAlign::END;
214 }
215
MeasureDecorator(float contentWidth,const std::u16string & textContent,bool showPlaceHolder)216 float CounterDecorator::MeasureDecorator(float contentWidth, const std::u16string& textContent, bool showPlaceHolder)
217 {
218 auto decoratedNode = decoratedNode_.Upgrade();
219 CHECK_NULL_RETURN(decoratedNode, 0.0);
220 auto textNode = textNode_.Upgrade();
221 CHECK_NULL_RETURN(textNode, 0.0);
222 auto textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
223 CHECK_NULL_RETURN(textFieldPattern, 0.0f);
224 auto textFieldLayoutProperty = textFieldPattern->GetLayoutProperty<TextFieldLayoutProperty>();
225 CHECK_NULL_RETURN(textFieldLayoutProperty, 0.0f);
226
227 auto isInlineStyle = textFieldPattern->IsNormalInlineState();
228 auto isShowPassword = textFieldPattern->IsShowPasswordIcon();
229 if (textFieldLayoutProperty->GetShowCounterValue(false) && textFieldLayoutProperty->HasMaxLength() &&
230 !isInlineStyle && !isShowPassword) {
231 auto counterNodeLayoutWrapper = decoratedNode->GetOrCreateChildByIndex(decoratedNode->GetChildIndex(textNode));
232 if (counterNodeLayoutWrapper) {
233 auto textLength =
234 static_cast<uint32_t>(showPlaceHolder ? 0 : textContent.length());
235 auto maxLength = static_cast<uint32_t>(textFieldLayoutProperty->GetMaxLength().value());
236 LayoutConstraintF textContentConstraint;
237 textContentConstraint.UpdateIllegalSelfIdealSizeWithCheck(OptionalSizeF(contentWidth, std::nullopt));
238 UpdateTextNodeAndMeasure(textLength, maxLength, textContentConstraint);
239 return textNode->GetGeometryNode()->GetFrameSize().Height();
240 }
241 }
242 return 0.0f;
243 }
244
UpdateTextNodeAndMeasure(uint32_t textLength,uint32_t maxLength,const LayoutConstraintF & contentConstraint)245 void CounterDecorator::UpdateTextNodeAndMeasure(
246 uint32_t textLength, uint32_t maxLength, const LayoutConstraintF& contentConstraint)
247 {
248 auto decoratedNode = decoratedNode_.Upgrade();
249 CHECK_NULL_VOID(decoratedNode);
250 auto textNode = textNode_.Upgrade();
251 CHECK_NULL_VOID(textNode);
252 auto pipeline = decoratedNode->GetContext();
253 CHECK_NULL_VOID(pipeline);
254 auto textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
255 CHECK_NULL_VOID(textFieldPattern);
256 auto textFieldLayoutProperty = textFieldPattern->GetLayoutProperty<TextFieldLayoutProperty>();
257 CHECK_NULL_VOID(textFieldLayoutProperty);
258
259 auto counterType = textFieldLayoutProperty->GetSetCounterValue(DEFAULT_MODE);
260 auto limitSize = static_cast<uint32_t>(static_cast<int32_t>(maxLength) * counterType / SHOW_COUNTER_PERCENT);
261 if (counterType == DEFAULT_MODE || (textLength >= limitSize && counterType != DEFAULT_MODE)) {
262 UpdateCounterContentAndStyle(textLength, maxLength, true);
263 } else {
264 UpdateCounterContentAndStyle(textLength, maxLength, false);
265 }
266 // TextInput's counter is outside of it,
267 // hence need to check whether counter's width is longer than TextInput's constraint
268 if (!textFieldPattern->IsTextArea() && contentConstraint.selfIdealSize.Width().has_value()) {
269 textNode->Measure(LayoutConstraintF());
270 if (GetContentWidth() > contentConstraint.selfIdealSize.Width().value()) {
271 return;
272 }
273 }
274 textNode->Measure(contentConstraint);
275 }
276
LayoutDecorator()277 void CounterDecorator::LayoutDecorator()
278 {
279 auto decoratedNode = decoratedNode_.Upgrade();
280 CHECK_NULL_VOID(decoratedNode);
281 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
282 CHECK_NULL_VOID(textFieldPattern);
283
284 bool isInlineStyle = textFieldPattern->IsNormalInlineState();
285 bool isShowPassword = textFieldPattern->IsShowPasswordIcon();
286 if (!isShowPassword && !isInlineStyle) {
287 // ShowCounter is inside of TextArea, while outside of TextInput
288 if (!textFieldPattern->IsTextArea()) {
289 HandleNonTextArea();
290 } else {
291 HandleTextArea();
292 }
293 }
294 }
295
HandleNonTextArea()296 void CounterDecorator::HandleNonTextArea()
297 {
298 auto decoratedNode = decoratedNode_.Upgrade();
299 CHECK_NULL_VOID(decoratedNode);
300 auto textNode = textNode_.Upgrade();
301 CHECK_NULL_VOID(textNode);
302 RefPtr<LayoutProperty> decoratedNodeProperty = decoratedNode->GetLayoutProperty();
303 CHECK_NULL_VOID(decoratedNodeProperty);
304 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
305 CHECK_NULL_VOID(textFieldPattern);
306 RefPtr<GeometryNode> textGeometryNode = textNode->GetGeometryNode();
307 CHECK_NULL_VOID(textGeometryNode);
308 auto pipeline = decoratedNode->GetContext();
309 CHECK_NULL_VOID(pipeline);
310 auto theme = textFieldPattern->GetTheme();
311 CHECK_NULL_VOID(theme);
312
313 bool isRTL = decoratedNodeProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
314 RectF frameRect = decoratedNode->GetGeometryNode()->GetFrameRect();
315 RectF contentRect = decoratedNode->GetGeometryNode()->GetContentRect();
316 float countX = contentRect.GetX();
317 auto responseArea = textFieldPattern->GetResponseArea();
318 auto cleanNodeResponseArea = textFieldPattern->GetCleanNodeResponseArea();
319 auto updateCountXWithArea = [&countX, isRTL](const std::vector<RefPtr<TextInputResponseArea>>& areas) {
320 for (auto area : areas) {
321 if (!area) {
322 continue;
323 }
324 if (isRTL) {
325 countX -= area->GetAreaRect().Width();
326 } else {
327 countX += area->GetAreaRect().Width();
328 }
329 }
330 };
331 updateCountXWithArea({responseArea, cleanNodeResponseArea});
332 auto curFontScale = pipeline->GetFontScale();
333 auto countY = (NearEqual(curFontScale, 1.0f)) ? (frameRect.Height() + textGeometryNode->GetFrameRect().Height()) :
334 (frameRect.Bottom() - frameRect.Top() + theme->GetCounterTextMarginOffset().ConvertToPx());
335 textGeometryNode->SetFrameOffset(OffsetF(countX, countY));
336 textNode->Layout();
337 }
338
HandleTextArea()339 void CounterDecorator::HandleTextArea()
340 {
341 auto decoratedNode = decoratedNode_.Upgrade();
342 CHECK_NULL_VOID(decoratedNode);
343 auto textNode = textNode_.Upgrade();
344 CHECK_NULL_VOID(textNode);
345 auto textFieldGeometryNode = decoratedNode->GetGeometryNode();
346 CHECK_NULL_VOID(textFieldGeometryNode);
347 const std::unique_ptr<GeometryProperty> &content = textFieldGeometryNode->GetContent();
348 CHECK_NULL_VOID(content);
349 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
350 CHECK_NULL_VOID(textFieldPattern);
351 auto counterGeometryNode = textNode->GetGeometryNode();
352 CHECK_NULL_VOID(counterGeometryNode);
353
354 RectF frameRect = textFieldGeometryNode->GetFrameRect();
355 float countX = content->GetRect().GetX();
356 counterGeometryNode->SetFrameOffset(OffsetF(countX,
357 frameRect.Height() - textFieldPattern->GetPaddingBottom() - counterGeometryNode->GetFrameRect().Height()));
358 textNode->Layout();
359 }
360
GetBoundHeight() const361 float CounterDecorator::GetBoundHeight() const
362 {
363 auto decoratedNode = decoratedNode_.Upgrade();
364 CHECK_NULL_RETURN(decoratedNode, 0.0);
365 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
366 CHECK_NULL_RETURN(textFieldPattern, 0.0);
367 auto theme = textFieldPattern->GetTheme();
368 CHECK_NULL_RETURN(theme, 0.0);
369 return theme->GetCounterTextTopMargin().ConvertToPx() + theme->GetCounterTextBottomMargin().ConvertToPx() +
370 GetDecoratorHeight();
371 }
372
HasContent() const373 bool CounterDecorator::HasContent() const
374 {
375 auto textNode = textNode_.Upgrade();
376 CHECK_NULL_RETURN(textNode, false);
377 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(textNode->GetLayoutProperty());
378 CHECK_NULL_RETURN(textLayoutProperty, false);
379 return textLayoutProperty->GetContent().has_value() && !textLayoutProperty->GetContent().value().empty();
380 }
381
UpdateTextFieldMargin()382 void ErrorDecorator::UpdateTextFieldMargin()
383 {
384 auto decoratedNode = decoratedNode_.Upgrade();
385 CHECK_NULL_VOID(decoratedNode);
386 auto textNode = textNode_.Upgrade();
387 CHECK_NULL_VOID(textNode);
388 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty<TextFieldLayoutProperty>();
389 CHECK_NULL_VOID(textFieldLayoutProperty);
390 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
391 CHECK_NULL_VOID(textFieldPattern);
392 auto textFieldPaintProperty = textFieldPattern->GetPaintProperty<TextFieldPaintProperty>();
393 CHECK_NULL_VOID(textFieldPaintProperty);
394 auto theme = textFieldPattern->GetTheme();
395 CHECK_NULL_VOID(theme);
396
397 UpdateErrorStyle();
398 MarginProperty errorMargin;
399 ScopedLayout scope(decoratedNode->GetContext());
400 textNode->Measure(LayoutConstraintF());
401 auto errorTextMargin = theme->GetErrorTextTopMargin().ConvertToPx() +
402 theme->GetErrorTextBottomMargin().ConvertToPx() + GetDecoratorHeight();
403 if (textFieldPattern->GetMarginBottom() < errorTextMargin) {
404 errorMargin.bottom = CalcLength(errorTextMargin);
405 }
406 if (textFieldPaintProperty->HasMarginByUser()) {
407 auto userMargin = textFieldPaintProperty->GetMarginByUserValue();
408 userMargin.bottom = textFieldPattern->GetMarginBottom() < errorTextMargin ?
409 errorMargin.bottom : userMargin.bottom;
410 textFieldLayoutProperty->UpdateMargin(userMargin);
411 } else {
412 textFieldLayoutProperty->UpdateMargin(errorMargin);
413 }
414 }
415
UpdateLayoutProperty()416 void ErrorDecorator::UpdateLayoutProperty()
417 {
418 auto decoratedNode = decoratedNode_.Upgrade();
419 CHECK_NULL_VOID(decoratedNode);
420 auto textNode = textNode_.Upgrade();
421 CHECK_NULL_VOID(textNode);
422 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(textNode->GetLayoutProperty());
423 CHECK_NULL_VOID(textLayoutProperty);
424 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty<TextFieldLayoutProperty>();
425 CHECK_NULL_VOID(textFieldLayoutProperty);
426 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
427 CHECK_NULL_VOID(textFieldPattern);
428 auto theme = textFieldPattern->GetTheme();
429 CHECK_NULL_VOID(theme);
430
431 TextStyle errorTextStyle = theme->GetErrorTextStyle();
432 auto errorText = textFieldLayoutProperty->GetErrorTextValue(u"");
433 StringUtils::TransformStrCase(errorText, static_cast<int32_t>(errorTextStyle.GetTextCase()));
434 textLayoutProperty->UpdateContent(errorText);
435 textLayoutProperty->UpdateTextColor(errorTextStyle.GetTextColor());
436 textLayoutProperty->UpdateFontWeight(errorTextStyle.GetFontWeight());
437 textLayoutProperty->UpdateFontSize(errorTextStyle.GetFontSize());
438 auto maxFontScale = theme->GetErrorTextMaxFontScale();
439 if (textFieldLayoutProperty->HasMaxFontScale()) {
440 maxFontScale = std::min(theme->GetErrorTextMaxFontScale(),
441 textFieldLayoutProperty->GetMaxFontScale().value());
442 }
443 textLayoutProperty->UpdateMaxFontScale(maxFontScale);
444 if (textFieldLayoutProperty->HasMinFontScale()) {
445 auto minFontScale = textFieldLayoutProperty->GetMinFontScale().value();
446 textLayoutProperty->UpdateMinFontScale(minFontScale);
447 }
448 textLayoutProperty->UpdateTextAlign(TextAlign::START);
449 textLayoutProperty->UpdateMaxLines(theme->GetErrorTextMaxLine());
450 textLayoutProperty->UpdateTextOverflow(TextOverflow::ELLIPSIS);
451 textLayoutProperty->UpdateIsAnimationNeeded(false);
452 auto isRTL = textFieldLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
453 if (isRTL) {
454 textLayoutProperty->UpdateLayoutDirection(TextDirection::RTL);
455 } else {
456 textLayoutProperty->UpdateLayoutDirection(TextDirection::LTR);
457 }
458 }
459
460 // The style of showError is basically fixed, just refresh it every time onModifyDone.
461 // Unlike showError, showCounter is not marked as dirty after insertValue and will not call onModifyDone,
462 // Only measure will be called, so counter’s style need to be refreshed every time it is measured.
UpdateErrorStyle()463 void ErrorDecorator::UpdateErrorStyle()
464 {
465 auto decoratedNode = decoratedNode_.Upgrade();
466 CHECK_NULL_VOID(decoratedNode);
467 auto textNode = textNode_.Upgrade();
468 CHECK_NULL_VOID(textNode);
469 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
470 CHECK_NULL_VOID(textFieldPattern);
471 auto theme = textFieldPattern->GetTheme();
472 CHECK_NULL_VOID(theme);
473 TextStyle errorTextStyle = theme->GetErrorTextStyle();
474 UpdateLayoutProperty();
475
476 auto accessibilityProperty = textNode->GetAccessibilityProperty<AccessibilityProperty>();
477 CHECK_NULL_VOID(accessibilityProperty);
478 accessibilityProperty->SetAccessibilityLevel("yes");
479 auto parentID = decoratedNode->GetInspectorIdValue("");
480 textNode->UpdateInspectorId(INSPECTOR_PREFIX + ERRORNODE_PREFIX + parentID);
481 textNode->SetIsCalculateInnerClip(true);
482
483 textNode->MarkModifyDone();
484 textNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF_AND_CHILD);
485 auto context = textNode->GetRenderContext();
486 CHECK_NULL_VOID(context);
487 context->UpdateForegroundColor(errorTextStyle.GetTextColor());
488 }
489
490 // Split the code from TextFieldContentModifier::ProcessErrorParagraph:
491 // Put the part that calculates width and layout into MeasureDecorator. Set the frame offset to LayoutDecorator.
492 // Should be called after the measure of counter and textInput, as it relies on them to calculate the width.
MeasureDecorator(float contentWidth,const std::u16string & textContent,bool showPlaceHolder)493 float ErrorDecorator::MeasureDecorator(float contentWidth, const std::u16string& textContent, bool showPlaceHolder)
494 {
495 auto decoratedNode = decoratedNode_.Upgrade();
496 CHECK_NULL_RETURN(decoratedNode, 0.0);
497 auto textNode = textNode_.Upgrade();
498 CHECK_NULL_RETURN(textNode, 0.0);
499 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
500 CHECK_NULL_RETURN(textFieldPattern, 0.0);
501 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty();
502 CHECK_NULL_RETURN(textFieldLayoutProperty, 0.0);
503 auto textLayoutProperty = DynamicCast<TextLayoutProperty>(textNode->GetLayoutProperty());
504 CHECK_NULL_RETURN(textLayoutProperty, 0.0);
505 RectF textFieldFrameRect = decoratedNode->GetGeometryNode()->GetFrameRect();
506 auto errorValue = textFieldPattern->GetErrorTextString();
507 if (textFieldPattern->IsShowError() && !textFieldPattern->IsDisabled() && !errorValue.empty()) {
508 float padding = 0.0f;
509 if (textFieldLayoutProperty && textFieldLayoutProperty->GetPaddingProperty()) {
510 const auto& paddingProperty = textFieldLayoutProperty->GetPaddingProperty();
511 padding = paddingProperty->left.value_or(CalcLength(0.0)).GetDimension().ConvertToPx() +
512 paddingProperty->right.value_or(CalcLength(0.0)).GetDimension().ConvertToPx();
513 }
514 float layoutWidth = textFieldFrameRect.Width() - padding;
515 auto border = textFieldPattern->GetBorderWidthProperty();
516 float borderWidth = textFieldPattern->GetBorderLeft(border) + textFieldPattern->GetBorderRight(border);
517 borderWidth = std::max(borderWidth, 0.0f);
518 layoutWidth -= borderWidth; // subtract border width
519 if (textFieldPattern->IsShowCount()) {
520 auto counterDecorator = textFieldPattern->GetCounterDecorator();
521 if (counterDecorator) {
522 layoutWidth -= counterDecorator->GetContentWidth(); // subtract counter length
523 }
524 }
525 LayoutConstraintF invisibleConstraint;
526 invisibleConstraint.UpdateMaxSizeWithCheck({0.0f, 0.0f});
527 if (LessOrEqual(layoutWidth, 0.0f)) {
528 textNode->Measure(invisibleConstraint);
529 return 0.0; // no enough space
530 }
531 LayoutConstraintF textContentConstraint;
532 textContentConstraint.UpdateMaxSizeWithCheck({layoutWidth, Infinity<float>()});
533 auto textNodeLayoutWrapper = decoratedNode->GetOrCreateChildByIndex(decoratedNode->GetChildIndex(textNode));
534 if (textNodeLayoutWrapper) {
535 textNode->Measure(textContentConstraint);
536 if (GreatNotEqual(GetContentWidth(), layoutWidth)) {
537 textNode->Measure(invisibleConstraint);
538 return 0.0; // no enough space
539 }
540 }
541 }
542 return textNode->GetGeometryNode()->GetFrameSize().Height();
543 }
544
BeforeLayout()545 void ErrorDecorator::BeforeLayout()
546 {
547 MeasureDecorator(Infinity<float>(), u"", false);
548 }
549
LayoutDecorator()550 void ErrorDecorator::LayoutDecorator()
551 {
552 BeforeLayout();
553 auto decoratedNode = decoratedNode_.Upgrade();
554 CHECK_NULL_VOID(decoratedNode);
555 auto textNode = textNode_.Upgrade();
556 CHECK_NULL_VOID(textNode);
557 auto textGeometryNode = textNode->GetGeometryNode();
558 CHECK_NULL_VOID(textGeometryNode);
559 auto textFieldLayoutProperty = decoratedNode->GetLayoutProperty<TextFieldLayoutProperty>();
560 CHECK_NULL_VOID(textFieldLayoutProperty);
561 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
562 CHECK_NULL_VOID(textFieldPattern);
563 auto textFieldGeometryNode = decoratedNode->GetGeometryNode();
564 CHECK_NULL_VOID(textFieldGeometryNode);
565 auto theme = textFieldPattern->GetTheme();
566 CHECK_NULL_VOID(theme);
567
568 float errorMargin = 0.0f;
569 if (textFieldLayoutProperty->GetShowUnderlineValue(false) && textFieldPattern->IsShowError()) {
570 errorMargin = theme->GetErrorTextUnderlineMargin().ConvertToPx();
571 } else if (textFieldPattern->NeedShowPasswordIcon() && textFieldPattern->IsShowError()) {
572 errorMargin = theme->GetErrorTextCapsuleMargin().ConvertToPx();
573 } else if (textFieldPattern->IsShowError()) {
574 errorMargin = theme->GetErrorTextCapsuleMargin().ConvertToPx();
575 } else {
576 errorMargin = 0;
577 }
578
579 RectF textFrameRect = decoratedNode->GetGeometryNode()->GetFrameRect();
580 auto offset = textFieldGeometryNode->GetContentOffset();
581 auto isRTL = textFieldLayoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
582 auto offSetX = offset.GetX();
583 auto textFrameWidth = textGeometryNode->GetFrameRect().Width();
584 if (isRTL) {
585 auto textFieldContentRect = textFieldGeometryNode->GetContentRect();
586 offSetX += textFieldContentRect.Width() - textFrameWidth;
587 }
588 if (theme->GetErrorTextCenter()) {
589 offSetX = (textFieldGeometryNode->GetFrameRect().Width() - textFrameWidth) / 2;
590 }
591 textGeometryNode->SetFrameOffset(OffsetF(offSetX, textFrameRect.Bottom() - textFrameRect.Top() + errorMargin));
592 textNode->Layout();
593 }
594
GetBoundHeight() const595 float ErrorDecorator::GetBoundHeight() const
596 {
597 auto textNode = textNode_.Upgrade();
598 CHECK_NULL_RETURN(textNode, 0.0);
599 auto geometryNode = textNode->GetGeometryNode();
600 CHECK_NULL_RETURN(geometryNode, 0.0);
601 auto decoratedNode = decoratedNode_.Upgrade();
602 CHECK_NULL_RETURN(decoratedNode, 0.0);
603 RefPtr<TextFieldPattern> textFieldPattern = decoratedNode->GetPattern<TextFieldPattern>();
604 CHECK_NULL_RETURN(textFieldPattern, 0.0);
605 auto theme = textFieldPattern->GetTheme();
606 CHECK_NULL_RETURN(theme, 0.0);
607 return theme->GetErrorTextTopMargin().ConvertToPx() + theme->GetErrorTextBottomMargin().ConvertToPx() +
608 geometryNode->GetFrameRect().Height();
609 }
610
611 }
612