• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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