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