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