• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "core/components_ng/pattern/text_field/text_field_layout_algorithm.h"
17 
18 #include "unicode/uchar.h"
19 
20 #include "base/geometry/axis.h"
21 #include "base/geometry/dimension.h"
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/rect_t.h"
24 #include "base/geometry/ng/size_t.h"
25 #include "base/i18n/localization.h"
26 #include "base/memory/referenced.h"
27 #include "base/utils/utils.h"
28 #include "core/common/font_manager.h"
29 #include "core/components/common/layout/constants.h"
30 #include "core/components/scroll/scroll_bar_theme.h"
31 #include "core/components/text/text_theme.h"
32 #include "core/components/theme/theme_manager.h"
33 #include "core/components_ng/base/frame_node.h"
34 #include "core/components_ng/pattern/text/text_layout_property.h"
35 #include "core/components_ng/pattern/text_field/text_field_content_modifier.h"
36 #include "core/components_ng/pattern/text_field/text_field_layout_property.h"
37 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
38 #include "core/components_ng/pattern/text_field/text_selector.h"
39 #include "core/components_ng/property/measure_utils.h"
40 #include "core/components_ng/render/drawing_prop_convertor.h"
41 #include "core/components_ng/render/font_collection.h"
42 #include "core/pipeline_ng/pipeline_context.h"
43 
44 namespace OHOS::Ace::NG {
45 namespace {
46 constexpr uint32_t COUNTER_TEXT_MAXLINE = 1;
47 constexpr float INLINE_SAFE_BOUNDARY_VALUE = 2.0f;
48 } // namespace
49 
Measure(LayoutWrapper * layoutWrapper)50 void TextFieldLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
51 {
52     const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
53     OptionalSizeF frameSize =
54         CreateIdealSize(layoutConstraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT_MAIN_AXIS);
55     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
56     const auto& calcLayoutConstraint = layoutWrapper->GetLayoutProperty()->GetCalcLayoutConstraint();
57     auto frameNode = layoutWrapper->GetHostNode();
58     CHECK_NULL_VOID(frameNode);
59     auto pattern = frameNode->GetPattern<TextFieldPattern>();
60     CHECK_NULL_VOID(pattern);
61     float contentWidth = 0.0f;
62     float contentHeight = 0.0f;
63     if (content) {
64         auto contentSize = content->GetRect().GetSize();
65         contentWidth = contentSize.Width();
66         contentHeight = contentSize.Height();
67     }
68     if (pattern->IsTextArea()) {
69         if (!frameSize.Width().has_value()) {
70             // If width is not set, select the maximum value of minWidth and maxWidth to layoutConstraint
71             if (calcLayoutConstraint && calcLayoutConstraint->maxSize.has_value() &&
72                 calcLayoutConstraint->maxSize.value().Width().has_value()) {
73                 frameSize.SetHeight(std::min(layoutConstraint->maxSize.Width(),
74                     contentWidth + pattern->GetHorizontalPaddingSum()));
75             } else if (!calcLayoutConstraint) {
76                 // If calcLayoutConstraint has not set, use the LayoutConstraint initial value
77                 frameSize.SetWidth(contentWidth + pattern->GetHorizontalPaddingSum());
78             } else {
79                 // If maxWidth is not set and calcLayoutConstraint is set, set minWidth to layoutConstraint
80                 frameSize.SetWidth(layoutConstraint->minSize.Width());
81             }
82         }
83         if (pattern->IsNormalInlineState() && pattern->GetTextInputFlag()) {
84             frameSize.SetWidth(contentWidth + pattern->GetHorizontalPaddingSum());
85         }
86         if (!frameSize.Height().has_value()) {
87             // Like width
88             if (calcLayoutConstraint && calcLayoutConstraint->maxSize.has_value() &&
89                 calcLayoutConstraint->maxSize.value().Height().has_value()) {
90                 frameSize.SetHeight(std::min(layoutConstraint->maxSize.Height(),
91                     contentHeight + pattern->GetVerticalPaddingSum()));
92             } else if (!calcLayoutConstraint || NearZero(layoutConstraint->minSize.Height())) {
93                 // calcLayoutConstraint initialized once when setting width, set minHeight=0,
94                 // so add "minHeight=0" to the constraint.
95                 frameSize.SetHeight(
96                     std::min(layoutConstraint->maxSize.Height(), contentHeight + pattern->GetVerticalPaddingSum()));
97             } else {
98                 frameSize.SetHeight(
99                     std::max(layoutConstraint->minSize.Height(), contentHeight + pattern->GetVerticalPaddingSum()));
100             }
101         }
102 
103         // Here's what happens when the height or width is set at list one
104         frameSize.Constrain(layoutConstraint->minSize, layoutConstraint->maxSize);
105         if (layoutConstraint->maxSize.Height() < layoutConstraint->minSize.Height()) {
106             frameSize.SetHeight(layoutConstraint->minSize.Height());
107         }
108         if (layoutConstraint->maxSize.Width() < layoutConstraint->minSize.Width()) {
109             frameSize.SetWidth(layoutConstraint->minSize.Width());
110         }
111         layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize.ConvertToSizeT());
112 
113         frameRect_ =
114             RectF(layoutWrapper->GetGeometryNode()->GetFrameOffset(), layoutWrapper->GetGeometryNode()->GetFrameSize());
115         return;
116     }
117     auto pipeline = PipelineBase::GetCurrentContext();
118     CHECK_NULL_VOID(pipeline);
119     auto textFieldTheme = pipeline->GetTheme<TextFieldTheme>();
120     CHECK_NULL_VOID(textFieldTheme);
121     auto defaultHeight = textFieldTheme->GetHeight().ConvertToPx();
122     if (!frameSize.Height().has_value()) {
123         if (calcLayoutConstraint && calcLayoutConstraint->maxSize.has_value() &&
124             calcLayoutConstraint->maxSize.value().Height().has_value()) {
125             frameSize.SetHeight(std::max(layoutConstraint->maxSize.Height(), layoutConstraint->minSize.Height()));
126         } else if (!calcLayoutConstraint || NearZero(layoutConstraint->minSize.Height())) {
127             auto height = contentHeight + pattern->GetVerticalPaddingSum() < defaultHeight
128                               ? defaultHeight
129                               : contentHeight + pattern->GetVerticalPaddingSum();
130             frameSize.SetHeight(
131                 std::min(layoutConstraint->maxSize.Height(), static_cast<float>(height)));
132         } else {
133             frameSize.SetHeight(layoutConstraint->minSize.Height());
134         }
135     }
136     auto textfieldLayoutProperty = AceType::DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
137     CHECK_NULL_VOID(textfieldLayoutProperty);
138 
139     if (textfieldLayoutProperty->GetWidthAutoValue(false)) {
140         auto width =
141             std::max(std::min(layoutConstraint->maxSize.Width(), contentWidth + pattern->GetHorizontalPaddingSum()),
142                 layoutConstraint->minSize.Width());
143         frameSize.SetWidth(width);
144     }
145     if (layoutConstraint->maxSize.Height() < layoutConstraint->minSize.Height()) {
146         frameSize.SetHeight(layoutConstraint->minSize.Height());
147     }
148     if (layoutConstraint->maxSize.Width() < layoutConstraint->minSize.Width()) {
149         frameSize.SetWidth(layoutConstraint->minSize.Width());
150     }
151     layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize.ConvertToSizeT());
152     frameRect_ =
153         RectF(layoutWrapper->GetGeometryNode()->GetFrameOffset(), layoutWrapper->GetGeometryNode()->GetFrameSize());
154 
155     auto children = frameNode->GetChildren();
156     auto layoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
157     CHECK_NULL_VOID(layoutProperty);
158     if (!children.empty() && layoutProperty->GetShowUnderlineValue(false) &&
159         layoutProperty->GetTextInputTypeValue(TextInputType::UNSPECIFIED) == TextInputType::UNSPECIFIED) {
160         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
161         auto childLayoutConstraint = textfieldLayoutProperty->CreateChildConstraint();
162         CHECK_NULL_VOID(childWrapper);
163         childWrapper->Measure(childLayoutConstraint);
164     }
165 }
166 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)167 std::optional<SizeF> TextFieldLayoutAlgorithm::MeasureContent(
168     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
169 {
170     auto frameNode = layoutWrapper->GetHostNode();
171     CHECK_NULL_RETURN(frameNode, std::nullopt);
172     auto pipeline = frameNode->GetContext();
173     CHECK_NULL_RETURN(pipeline, std::nullopt);
174     auto textFieldLayoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
175     CHECK_NULL_RETURN(textFieldLayoutProperty, std::nullopt);
176     auto textFieldTheme = pipeline->GetTheme<TextFieldTheme>();
177     CHECK_NULL_RETURN(textFieldTheme, std::nullopt);
178     auto pattern = frameNode->GetPattern<TextFieldPattern>();
179     CHECK_NULL_RETURN(pattern, std::nullopt);
180 
181     // Construct a textstyle.
182     TextStyle textStyle;
183     std::string textContent;
184     bool showPlaceHolder = false;
185     auto idealWidth = contentConstraint.selfIdealSize.Width().value_or(contentConstraint.maxSize.Width());
186     auto idealHeight = contentConstraint.selfIdealSize.Height().value_or(contentConstraint.maxSize.Height());
187     auto idealSize = SizeF { idealWidth, idealHeight };
188     idealSize.UpdateSizeWhenSmaller(contentConstraint.maxSize);
189     idealWidth = idealSize.Width();
190     idealHeight = idealSize.Height();
191     auto isInlineStyle = pattern->IsNormalInlineState();
192     if (!textFieldLayoutProperty->GetValueValue("").empty()) {
193         UpdateTextStyle(frameNode, textFieldLayoutProperty, textFieldTheme, textStyle, pattern->IsDisabled());
194         textContent = textFieldLayoutProperty->GetValueValue("");
195         if (!pattern->IsTextArea() && isInlineStyle) {
196             textStyle.SetTextOverflow(TextOverflow::ELLIPSIS);
197         }
198     } else {
199         UpdatePlaceholderTextStyle(frameNode, textFieldLayoutProperty, textFieldTheme,
200             textStyle, pattern->IsDisabled());
201         textContent = textFieldLayoutProperty->GetPlaceholderValue("");
202         showPlaceHolder = true;
203     }
204 
205     // use for modifier.
206     auto contentModifier = pattern->GetContentModifier();
207     if (contentModifier) {
208         SetPropertyToModifier(textStyle, contentModifier);
209         contentModifier->ModifyTextStyle(textStyle);
210         contentModifier->SetFontReady(false);
211     }
212     auto isPasswordType =
213         textFieldLayoutProperty->GetTextInputTypeValue(TextInputType::UNSPECIFIED) == TextInputType::VISIBLE_PASSWORD;
214     auto disableTextAlign = !pattern->IsTextArea() && !showPlaceHolder && !isInlineStyle;
215     // Create paragraph.
216     if (pattern->IsDragging() && !showPlaceHolder) {
217         TextStyle dragTextStyle = textStyle;
218         Color color = textStyle.GetTextColor().ChangeAlpha(DRAGGED_TEXT_OPACITY);
219         dragTextStyle.SetTextColor(color);
220         std::vector<TextStyle> textStyles { textStyle, dragTextStyle, textStyle };
221         CreateParagraph(textStyles, pattern->GetDragContents(), textContent,
222             isPasswordType && pattern->GetTextObscured() && !showPlaceHolder, disableTextAlign);
223     } else {
224         CreateParagraph(textStyle, textContent, isPasswordType && pattern->GetTextObscured() && !showPlaceHolder,
225             pattern->GetNakedCharPosition(), disableTextAlign);
226     }
227 
228     // paragraph  Layout.
229     float imageSize = 0.0f;
230     auto showPasswordIcon = textFieldLayoutProperty->GetShowPasswordIcon().value_or(true);
231     imageSize = showPasswordIcon ? pattern->GetIconSize() : 0.0f;
232     auto imageHotZoneWidth = showPasswordIcon ? imageSize + pattern->GetIconRightOffset() : 0.0f;
233     auto scrollBarTheme = pipeline->GetTheme<ScrollBarTheme>();
234     CHECK_NULL_RETURN(scrollBarTheme, std::nullopt);
235     const auto& layoutConstraint = textFieldLayoutProperty->GetLayoutConstraint();
236     if (isInlineStyle) {
237         // for InlineStyle, max width is content width with safe boundary.
238         float inlineBoxWidth = 0.0f;
239         auto safeBoundary = textFieldTheme->GetInlineBorderWidth().ConvertToPx() * 2 + INLINE_SAFE_BOUNDARY_VALUE;
240         if (pattern->IsSelected()) {
241             inlineBoxWidth = pattern->GetPreviewWidth() < layoutConstraint->maxSize.Width()
242                                  ? (pattern->GetPreviewWidth() + safeBoundary)
243                                  : (layoutConstraint->maxSize.Width() - safeBoundary);
244         } else {
245             inlineBoxWidth = idealWidth;
246         }
247         paragraph_->Layout(pattern->GetPreviewWidth() == 0 ? idealWidth : inlineBoxWidth);
248     } else if (showPlaceHolder) {
249         // for placeholder.
250         if (isPasswordType) {
251             paragraph_->Layout(idealWidth - imageHotZoneWidth);
252         } else {
253             paragraph_->Layout(idealWidth);
254         }
255     } else if (textStyle.GetMaxLines() == 1) {
256         // for text input case, need to measure in one line without constraint.
257         paragraph_->Layout(std::numeric_limits<double>::infinity());
258     } else {
259         // for text area, max width is content width without scroll bar.
260         paragraph_->Layout(
261             idealWidth - scrollBarTheme->GetActiveWidth().ConvertToPx() - SCROLL_BAR_LEFT_WIDTH.ConvertToPx());
262     }
263 
264     // counterParagraph Layout.
265     if (textFieldLayoutProperty->GetShowCounterValue(false) && textFieldLayoutProperty->HasMaxLength() &&
266         !isInlineStyle) {
267         auto textLength = showPlaceHolder ? 0 : StringUtils::ToWstring(textContent).length();
268         auto maxLength = textFieldLayoutProperty->GetMaxLength().value();
269         CreateCounterParagraph(textLength, maxLength, textFieldTheme);
270         if (counterParagraph_) {
271             counterParagraph_->Layout(
272                 idealWidth - scrollBarTheme->GetActiveWidth().ConvertToPx() - SCROLL_BAR_LEFT_WIDTH.ConvertToPx());
273         }
274     }
275     // errorParagraph  Layout.
276     if (textFieldLayoutProperty->GetShowErrorTextValue(false)) {
277         CreateErrorParagraph(textFieldLayoutProperty->GetErrorTextValue(""), textFieldTheme);
278         if (errorParagraph_) {
279             errorParagraph_->Layout(std::numeric_limits<double>::infinity());
280         }
281     }
282     auto paragraphNewWidth = static_cast<float>(paragraph_->GetMaxIntrinsicWidth());
283     if (!NearEqual(paragraphNewWidth, paragraph_->GetMaxWidth()) && !pattern->IsTextArea() && !showPlaceHolder &&
284         !isInlineStyle) {
285         paragraph_->Layout(std::ceil(paragraphNewWidth));
286         if (counterParagraph_) {
287             counterParagraph_->Layout(std::ceil(paragraphNewWidth));
288         }
289     }
290     auto preferredHeight = static_cast<float>(paragraph_->GetHeight());
291     if (textContent.empty() || showPlaceHolder) {
292         preferredHeight = pattern->PreferredLineHeight();
293     }
294     if (isInlineStyle && showPlaceHolder && !textContent.empty()) {
295         preferredHeight = static_cast<float>(paragraph_->GetHeight());
296     }
297     if (pattern->GetTextInputFlag() && !pattern->IsTextArea()) {
298         pattern->SetSingleLineHeight(preferredHeight);
299     }
300     paragraphWidth_ = paragraph_->GetLongestLine();
301     // textarea size.
302     if (pattern->IsTextArea()) {
303         auto paragraphHeight =
304             (textContent.empty() || !showPlaceHolder) ? preferredHeight : static_cast<float>(paragraph_->GetHeight());
305         auto useHeight =
306             static_cast<float>(paragraphHeight + (counterParagraph_ ? counterParagraph_->GetHeight() : 0.0f));
307         if (isInlineStyle && pattern->GetTextInputFlag()) {
308             idealHeight = pattern->GetSingleLineHeight() *
309                           textFieldLayoutProperty->GetMaxViewLinesValue(INLINE_DEFAULT_VIEW_MAXLINE);
310             idealWidth = paragraph_->GetLongestLine();
311         }
312         if (counterParagraph_ && idealHeight < useHeight) {
313             pattern->SetISCounterIdealHeight(true);
314             idealHeight = idealHeight - counterParagraph_->GetHeight();
315         }
316         textRect_.SetSize(SizeF(
317             idealWidth - scrollBarTheme->GetActiveWidth().ConvertToPx() - SCROLL_BAR_LEFT_WIDTH.ConvertToPx(),
318             paragraph_->GetHeight()));
319         return SizeF(idealWidth, std::min(idealHeight, useHeight));
320     }
321     // check password image size.
322     if (!showPasswordIcon || !isPasswordType) {
323         textRect_.SetSize(SizeF(static_cast<float>(paragraph_->GetLongestLine()), preferredHeight));
324         imageRect_.Reset();
325         if (textFieldLayoutProperty->GetWidthAutoValue(false)) {
326             if (LessOrEqual(contentConstraint.minSize.Width(), 0.0f)) {
327                 idealWidth = std::clamp(textRect_.GetSize().Width(), 0.0f, contentConstraint.maxSize.Width());
328             } else if (LessOrEqual(textRect_.Width(), 0.0f)) {
329                 idealWidth = contentConstraint.minSize.Width();
330             } else {
331                 idealWidth =
332                     std::clamp(textRect_.Width(), contentConstraint.minSize.Width(), contentConstraint.maxSize.Width());
333             }
334         }
335         return SizeF(idealWidth, std::min(preferredHeight, idealHeight));
336     }
337 
338     // password type size.
339     imageRect_.SetSize(SizeF(imageSize, imageSize));
340     if (pattern->GetTextObscured() && pattern->GetHidePasswordIconCtx()) {
341         pattern->GetHidePasswordIconCtx()->MakeCanvasImage(imageRect_.GetSize(), true, ImageFit::NONE);
342     } else if (!pattern->GetTextObscured() && pattern->GetShowPasswordIconCtx()) {
343         pattern->GetShowPasswordIconCtx()->MakeCanvasImage(imageRect_.GetSize(), true, ImageFit::NONE);
344     }
345     preferredHeight = std::min(static_cast<float>(paragraph_->GetHeight()), idealHeight);
346     textRect_.SetSize(SizeF(static_cast<float>(paragraph_->GetLongestLine()), static_cast<float>(preferredHeight)));
347     return SizeF(idealWidth - imageHotZoneWidth, std::min(idealHeight, preferredHeight));
348 }
349 
Layout(LayoutWrapper * layoutWrapper)350 void TextFieldLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
351 {
352     // update child position.
353     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
354     auto frameNode = layoutWrapper->GetHostNode();
355     CHECK_NULL_VOID(frameNode);
356     auto pattern = frameNode->GetPattern<TextFieldPattern>();
357     CHECK_NULL_VOID(pattern);
358     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
359     CHECK_NULL_VOID(content);
360     auto contentSize = content->GetRect().GetSize();
361     auto layoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
362     CHECK_NULL_VOID(layoutProperty);
363     auto context = layoutWrapper->GetHostNode()->GetContext();
364     CHECK_NULL_VOID(context);
365     parentGlobalOffset_ = layoutWrapper->GetHostNode()->GetPaintRectOffset() - context->GetRootRect().GetOffset();
366     auto align = Alignment::CENTER;
367     bool hasAlign = false;
368     if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
369         align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
370         hasAlign = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().has_value();
371     }
372     // Update content position.
373     OffsetF contentOffset = Alignment::GetAlignPosition(size, contentSize, align);
374     if (pattern->IsTextArea()) {
375         auto isInlineStyle = pattern->IsNormalInlineState();
376         if (hasAlign) {
377             if (isInlineStyle) {
378                 content->SetOffset(OffsetF(pattern->GetUtilPadding().Offset().GetX(), contentOffset.GetY()));
379             } else {
380                 content->SetOffset(OffsetF(pattern->GetUtilPadding().Offset().GetX() + pattern->GetBorderLeft(),
381                     contentOffset.GetY() + pattern->GetBorderTop()));
382             }
383 
384             OffsetF textRectOffSet = Alignment::GetAlignPosition(size, textRect_.GetSize(), align);
385             if (LessOrEqual(textRect_.Height(), content->GetRect().Height())) {
386                 textRect_.SetOffset(OffsetF(pattern->GetTextRect().GetOffset().GetX(), textRectOffSet.GetY()));
387             } else {
388                 textRect_.SetOffset(pattern->GetTextRect().GetOffset());
389             }
390         } else {
391             if (isInlineStyle) {
392                 content->SetOffset(pattern->GetUtilPadding().Offset());
393             } else {
394                 content->SetOffset(OffsetF(pattern->GetUtilPadding().Offset().GetX() + pattern->GetBorderLeft(),
395                     pattern->GetUtilPadding().Offset().GetY() + pattern->GetBorderTop()));
396             }
397             textRect_.SetOffset(pattern->GetTextRect().GetOffset());
398         }
399         return;
400     }
401     content->SetOffset(OffsetF(pattern->GetPaddingLeft(), contentOffset.GetY()));
402     // if handler is moving, no need to adjust text rect in pattern
403     auto isUsingMouse = pattern->GetMouseStatus() == MouseStatus::MOVE ||
404                         pattern->GetMouseStatus() == MouseStatus::RELEASED || pattern->GetIsMousePressed();
405     auto needForceCheck = ((pattern->GetCaretUpdateType() == CaretUpdateType::INPUT ||
406                                pattern->GetCaretUpdateType() == CaretUpdateType::DEL) &&
407                               (paragraphWidth_ <= contentSize.Width())) ||
408                           pattern->GetCaretUpdateType() == CaretUpdateType::ICON_PRESSED ||
409                           pattern->GetCaretUpdateType() == CaretUpdateType::VISIBLE_PASSWORD_ICON ||
410                           layoutProperty->GetTextAlignChangedValue(false);
411     auto needToKeepTextRect = isUsingMouse || !needForceCheck;
412     if (needToKeepTextRect) {
413         textRect_.SetOffset(pattern->GetTextRect().GetOffset());
414     }
415     auto paintProperty = pattern->GetPaintProperty<TextFieldPaintProperty>();
416     CHECK_NULL_VOID(paintProperty);
417     if (!pattern->IsTextArea() && !needToKeepTextRect) {
418         auto textOffset = Alignment::GetAlignPosition(contentSize, textRect_.GetSize(), Alignment::CENTER_LEFT);
419         // adjust text rect to the basic padding
420         auto textRectOffsetX = pattern->GetPaddingLeft() + pattern->GetBorderLeft();
421         if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
422             textRectOffsetX = pattern->GetPaddingLeft();
423         }
424         auto isEmptyTextEditValue = pattern->GetTextEditingValue().text.empty();
425         if (!isEmptyTextEditValue) {
426             switch (layoutProperty->GetTextAlignValue(TextAlign::START)) {
427                 case TextAlign::START:
428                     break;
429                 case TextAlign::CENTER:
430                     textRectOffsetX += (contentSize.Width() - textRect_.Width()) * 0.5f;
431                     break;
432                 case TextAlign::END:
433                     textRectOffsetX += contentSize.Width() - textRect_.Width();
434                     break;
435                 default:
436                     break;
437             }
438         }
439         textRect_.SetOffset(OffsetF(textRectOffsetX, textOffset.GetY()));
440     }
441 
442     // update image rect.
443     if (!imageRect_.IsEmpty()) {
444         auto imageOffset = Alignment::GetAlignPosition(size, imageRect_.GetSize(), Alignment::CENTER_RIGHT);
445         imageOffset.AddX(-pattern->GetIconRightOffset());
446         imageRect_.SetOffset(imageOffset);
447     }
448 
449     UpdateUnitLayout(layoutWrapper);
450 }
451 
UpdateTextStyle(const RefPtr<FrameNode> & frameNode,const RefPtr<TextFieldLayoutProperty> & layoutProperty,const RefPtr<TextFieldTheme> & theme,TextStyle & textStyle,bool isDisabled)452 void TextFieldLayoutAlgorithm::UpdateTextStyle(const RefPtr<FrameNode>& frameNode,
453     const RefPtr<TextFieldLayoutProperty>& layoutProperty, const RefPtr<TextFieldTheme>& theme, TextStyle& textStyle,
454     bool isDisabled)
455 {
456     const std::vector<std::string> defaultFontFamily = { "sans-serif" };
457     textStyle.SetFontFamilies(layoutProperty->GetFontFamilyValue(defaultFontFamily));
458     FontRegisterCallback(frameNode, textStyle.GetFontFamilies());
459 
460     Dimension fontSize;
461     if (layoutProperty->HasFontSize() && layoutProperty->GetFontSize().value_or(Dimension()).IsNonNegative()) {
462         fontSize = layoutProperty->GetFontSizeValue(Dimension());
463     } else {
464         fontSize = theme ? theme->GetFontSize() : textStyle.GetFontSize();
465     }
466     textStyle.SetFontSize(fontSize);
467     textStyle.SetTextAlign(layoutProperty->GetTextAlignValue(TextAlign::START));
468     textStyle.SetFontWeight(
469         layoutProperty->GetFontWeightValue(theme ? theme->GetFontWeight() : textStyle.GetFontWeight()));
470     if (isDisabled) {
471         textStyle.SetTextColor(theme ? theme->GetDisableTextColor() : textStyle.GetTextColor());
472         if (layoutProperty->GetShowUnderlineValue(false) &&
473             layoutProperty->GetTextInputTypeValue(TextInputType::UNSPECIFIED) == TextInputType::UNSPECIFIED) {
474             textStyle.SetTextColor(theme ? theme->GetTextColorDisable() : textStyle.GetTextColor());
475         }
476     } else {
477         auto renderContext = frameNode->GetRenderContext();
478         if (renderContext->HasForegroundColor()) {
479             textStyle.SetTextColor(renderContext->GetForegroundColor().value());
480         } else if (renderContext->HasForegroundColorStrategy()) {
481             textStyle.SetTextColor(Color::BLACK);
482         } else {
483             textStyle.SetTextColor(
484                 layoutProperty->GetTextColorValue(theme ? theme->GetTextColor() : textStyle.GetTextColor()));
485         }
486     }
487     if (layoutProperty->GetMaxLines()) {
488         textStyle.SetMaxLines(layoutProperty->GetMaxLines().value());
489     }
490     if (layoutProperty->HasItalicFontStyle()) {
491         textStyle.SetFontStyle(layoutProperty->GetItalicFontStyle().value());
492     }
493     if (layoutProperty->HasTextAlign()) {
494         textStyle.SetTextAlign(layoutProperty->GetTextAlign().value());
495     }
496 }
497 
UpdatePlaceholderTextStyle(const RefPtr<FrameNode> & frameNode,const RefPtr<TextFieldLayoutProperty> & layoutProperty,const RefPtr<TextFieldTheme> & theme,TextStyle & textStyle,bool isDisabled)498 void TextFieldLayoutAlgorithm::UpdatePlaceholderTextStyle(const RefPtr<FrameNode>& frameNode,
499     const RefPtr<TextFieldLayoutProperty>& layoutProperty, const RefPtr<TextFieldTheme>& theme, TextStyle& textStyle,
500     bool isDisabled)
501 {
502     const std::vector<std::string> defaultFontFamily = { "sans-serif" };
503     textStyle.SetFontFamilies(layoutProperty->GetPlaceholderFontFamilyValue(defaultFontFamily));
504     FontRegisterCallback(frameNode, textStyle.GetFontFamilies());
505 
506     Dimension fontSize;
507     if (layoutProperty->GetPlaceholderValue("").empty()) {
508         if (layoutProperty->HasFontSize() && layoutProperty->GetFontSize().value_or(Dimension()).IsNonNegative()) {
509             fontSize = layoutProperty->GetFontSizeValue(Dimension());
510         } else {
511             fontSize = theme ? theme->GetFontSize() : textStyle.GetFontSize();
512         }
513     } else {
514         if (layoutProperty->HasPlaceholderFontSize() &&
515             layoutProperty->GetPlaceholderFontSize().value_or(Dimension()).IsNonNegative()) {
516             fontSize = layoutProperty->GetPlaceholderFontSizeValue(Dimension());
517         } else {
518             fontSize = theme ? theme->GetFontSize() : textStyle.GetFontSize();
519         }
520     }
521 
522     textStyle.SetFontSize(fontSize);
523     textStyle.SetFontWeight(
524         layoutProperty->GetPlaceholderFontWeightValue(theme ? theme->GetFontWeight() : textStyle.GetFontWeight()));
525     if (isDisabled) {
526         textStyle.SetTextColor(theme ? theme->GetDisableTextColor() : textStyle.GetTextColor());
527         if (layoutProperty->GetShowUnderlineValue(false) &&
528             layoutProperty->GetTextInputTypeValue(TextInputType::UNSPECIFIED) == TextInputType::UNSPECIFIED) {
529             textStyle.SetTextColor(theme ? theme->GetTextColorDisable() : textStyle.GetTextColor());
530         }
531     } else {
532         textStyle.SetTextColor(layoutProperty->GetPlaceholderTextColorValue(
533             theme ? theme->GetPlaceholderColor() : textStyle.GetTextColor()));
534     }
535     if (layoutProperty->HasPlaceholderMaxLines()) {
536         textStyle.SetMaxLines(layoutProperty->GetPlaceholderMaxLines().value());
537     }
538     if (layoutProperty->HasPlaceholderItalicFontStyle()) {
539         textStyle.SetFontStyle(layoutProperty->GetPlaceholderItalicFontStyle().value());
540     }
541     if (layoutProperty->HasPlaceholderTextAlign()) {
542         textStyle.SetTextAlign(layoutProperty->GetPlaceholderTextAlign().value());
543     }
544     textStyle.SetTextOverflow(TextOverflow::ELLIPSIS);
545     textStyle.SetTextAlign(layoutProperty->GetTextAlignValue(TextAlign::START));
546 }
547 
FontRegisterCallback(const RefPtr<FrameNode> & frameNode,const std::vector<std::string> & fontFamilies)548 void TextFieldLayoutAlgorithm::FontRegisterCallback(
549     const RefPtr<FrameNode>& frameNode, const std::vector<std::string>& fontFamilies)
550 {
551     auto callback = [weakNode = WeakPtr<FrameNode>(frameNode)] {
552         auto frameNode = weakNode.Upgrade();
553         CHECK_NULL_VOID(frameNode);
554         frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
555         auto pattern = frameNode->GetPattern<TextFieldPattern>();
556         CHECK_NULL_VOID(pattern);
557         auto modifier = DynamicCast<TextFieldContentModifier>(pattern->GetContentModifier());
558         CHECK_NULL_VOID(modifier);
559         modifier->SetFontReady(true);
560     };
561     auto pipeline = frameNode->GetContext();
562     CHECK_NULL_VOID(pipeline);
563     auto fontManager = pipeline->GetFontManager();
564     if (fontManager) {
565         bool isCustomFont = false;
566         for (const auto& familyName : fontFamilies) {
567             bool customFont = fontManager->RegisterCallbackNG(frameNode, familyName, callback);
568             if (customFont) {
569                 isCustomFont = true;
570             }
571         }
572         fontManager->AddVariationNodeNG(frameNode);
573         if (isCustomFont) {
574             auto pattern = frameNode->GetPattern<TextFieldPattern>();
575             CHECK_NULL_VOID(pattern);
576             pattern->SetIsCustomFont(true);
577             auto modifier = DynamicCast<TextFieldContentModifier>(pattern->GetContentModifier());
578             CHECK_NULL_VOID(modifier);
579             modifier->SetIsCustomFont(true);
580         }
581     }
582 }
583 
CreateParagraph(const TextStyle & textStyle,std::string content,bool needObscureText,int32_t nakedCharPosition,bool disableTextAlign)584 void TextFieldLayoutAlgorithm::CreateParagraph(const TextStyle& textStyle, std::string content,
585     bool needObscureText, int32_t nakedCharPosition, bool disableTextAlign)
586 {
587     RSParagraphStyle paraStyle;
588     paraStyle.textDirection_ = ToRSTextDirection(GetTextDirection(content));
589     if (!disableTextAlign) {
590         paraStyle.textAlign_ = ToRSTextAlign(textStyle.GetTextAlign());
591     }
592     paraStyle.maxLines_ = textStyle.GetMaxLines();
593     paraStyle.locale_ = Localization::GetInstance()->GetFontLocale();
594     paraStyle.wordBreakType_ = ToRSWordBreakType(textStyle.GetWordBreak());
595     paraStyle.fontSize_ = textStyle.GetFontSize().ConvertToPx();
596     auto fontFamilies = textStyle.GetFontFamilies();
597     if (!fontFamilies.empty()) {
598         paraStyle.fontFamily_ = fontFamilies.at(0);
599     }
600     if (textStyle.GetTextOverflow() == TextOverflow::ELLIPSIS) {
601         paraStyle.ellipsis_ = StringUtils::Str8ToStr16(StringUtils::ELLIPSIS);
602     }
603     auto builder = RSParagraphBuilder::CreateRosenBuilder(paraStyle, RSFontCollection::GetInstance(false));
604     builder->PushStyle(ToRSTextStyle(PipelineContext::GetCurrentContext(), textStyle));
605     StringUtils::TransformStrCase(content, static_cast<int32_t>(textStyle.GetTextCase()));
606     auto displayText = TextFieldPattern::CreateDisplayText(content, nakedCharPosition, needObscureText);
607     builder->AddText(displayText);
608     builder->Pop();
609 
610     auto paragraph = builder->Build();
611     paragraph_.reset(paragraph.release());
612 }
613 
CreateParagraph(const std::vector<TextStyle> & textStyles,const std::vector<std::string> & contents,const std::string & content,bool needObscureText,bool disableTextAlign)614 void TextFieldLayoutAlgorithm::CreateParagraph(const std::vector<TextStyle>& textStyles,
615     const std::vector<std::string>& contents, const std::string& content, bool needObscureText, bool disableTextAlign)
616 {
617     auto textStyle = textStyles.begin();
618     RSParagraphStyle paraStyle;
619     paraStyle.textDirection_ = ToRSTextDirection(GetTextDirection(content));
620     if (!disableTextAlign) {
621         paraStyle.textAlign_ = ToRSTextAlign(textStyle->GetTextAlign());
622     }
623     paraStyle.maxLines_ = textStyle->GetMaxLines();
624     paraStyle.locale_ = Localization::GetInstance()->GetFontLocale();
625     paraStyle.wordBreakType_ = ToRSWordBreakType(textStyle->GetWordBreak());
626     paraStyle.fontSize_ = textStyle->GetFontSize().ConvertToPx();
627     auto fontFamilies = textStyle->GetFontFamilies();
628     if (!fontFamilies.empty()) {
629         paraStyle.fontFamily_ = fontFamilies.at(0);
630     }
631     if (textStyle->GetTextOverflow() == TextOverflow::ELLIPSIS) {
632         paraStyle.ellipsis_ = StringUtils::Str8ToStr16(StringUtils::ELLIPSIS);
633     }
634     auto builder = RSParagraphBuilder::CreateRosenBuilder(paraStyle, RSFontCollection::GetInstance(false));
635     for (size_t i = 0; i < contents.size(); i++) {
636         std::string splitStr = contents[i];
637         if (splitStr.empty()) {
638             continue;
639         }
640         auto& style = textStyles[i];
641         builder->PushStyle(ToRSTextStyle(PipelineContext::GetCurrentContext(), style));
642         StringUtils::TransformStrCase(splitStr, static_cast<int32_t>(style.GetTextCase()));
643         if (needObscureText) {
644             builder->AddText(
645                 TextFieldPattern::CreateObscuredText(static_cast<int32_t>(StringUtils::ToWstring(splitStr).length())));
646         } else {
647             builder->AddText(StringUtils::Str8ToStr16(splitStr));
648         }
649     }
650     builder->Pop();
651 
652     auto paragraph = builder->Build();
653     paragraph_.reset(paragraph.release());
654 }
655 
CreateCounterParagraph(int32_t textLength,int32_t maxLength,const RefPtr<TextFieldTheme> & theme)656 void TextFieldLayoutAlgorithm::CreateCounterParagraph(
657     int32_t textLength, int32_t maxLength, const RefPtr<TextFieldTheme>& theme)
658 {
659     CHECK_NULL_VOID(theme);
660     TextStyle countTextStyle = (textLength != maxLength) ? theme->GetCountTextStyle() : theme->GetOverCountTextStyle();
661     std::string counterText = std::to_string(textLength) + "/" + std::to_string(maxLength);
662     RSParagraphStyle paraStyle;
663     paraStyle.fontSize_ = countTextStyle.GetFontSize().ConvertToPx();
664     paraStyle.textAlign_ = ToRSTextAlign(TextAlign::END);
665     paraStyle.maxLines_ = COUNTER_TEXT_MAXLINE;
666     auto builder = RSParagraphBuilder::CreateRosenBuilder(paraStyle, RSFontCollection::GetInstance(false));
667     builder->PushStyle(ToRSTextStyle(PipelineContext::GetCurrentContext(), countTextStyle));
668     StringUtils::TransformStrCase(counterText, static_cast<int32_t>(countTextStyle.GetTextCase()));
669     builder->AddText(StringUtils::Str8ToStr16(counterText));
670     builder->Pop();
671 
672     auto paragraph = builder->Build();
673     counterParagraph_.reset(paragraph.release());
674 }
675 
CreateErrorParagraph(const std::string & content,const RefPtr<TextFieldTheme> & theme)676 void TextFieldLayoutAlgorithm::CreateErrorParagraph(const std::string& content, const RefPtr<TextFieldTheme>& theme)
677 {
678     CHECK_NULL_VOID(theme);
679     TextStyle errorTextStyle = theme->GetErrorTextStyle();
680     std::string counterText = content;
681     RSParagraphStyle paraStyle;
682     paraStyle.fontSize_ = errorTextStyle.GetFontSize().ConvertToPx();
683     paraStyle.textAlign_ = ToRSTextAlign(TextAlign::START);
684     auto builder = RSParagraphBuilder::CreateRosenBuilder(paraStyle, RSFontCollection::GetInstance(false));
685     builder->PushStyle(ToRSTextStyle(PipelineContext::GetCurrentContext(), errorTextStyle));
686     StringUtils::TransformStrCase(counterText, static_cast<int32_t>(errorTextStyle.GetTextCase()));
687     builder->AddText(StringUtils::Str8ToStr16(counterText));
688     builder->Pop();
689 
690     auto paragraph = builder->Build();
691     errorParagraph_.reset(paragraph.release());
692 }
693 
GetTextDirection(const std::string & content)694 TextDirection TextFieldLayoutAlgorithm::GetTextDirection(const std::string& content)
695 {
696     TextDirection textDirection = TextDirection::LTR;
697     auto showingTextForWString = StringUtils::ToWstring(content);
698     for (const auto& charOfShowingText : showingTextForWString) {
699         if (u_charDirection(charOfShowingText) == UCharDirection::U_LEFT_TO_RIGHT) {
700             textDirection = TextDirection::LTR;
701         } else if (u_charDirection(charOfShowingText) == UCharDirection::U_RIGHT_TO_LEFT) {
702             textDirection = TextDirection::RTL;
703         } else if (u_charDirection(charOfShowingText) == UCharDirection::U_RIGHT_TO_LEFT_ARABIC) {
704             textDirection = TextDirection::RTL;
705         }
706     }
707     return textDirection;
708 }
709 
GetParagraph()710 const std::shared_ptr<RSParagraph>& TextFieldLayoutAlgorithm::GetParagraph()
711 {
712     return paragraph_;
713 }
714 
GetCounterParagraph() const715 const std::shared_ptr<RSParagraph>& TextFieldLayoutAlgorithm::GetCounterParagraph() const
716 {
717     return counterParagraph_;
718 }
719 
GetErrorParagraph() const720 const std::shared_ptr<RSParagraph>& TextFieldLayoutAlgorithm::GetErrorParagraph() const
721 {
722     return errorParagraph_;
723 }
724 
GetTextFieldDefaultHeight()725 float TextFieldLayoutAlgorithm::GetTextFieldDefaultHeight()
726 {
727     const auto defaultHeight = 40.0_vp;
728     auto pipeline = PipelineContext::GetCurrentContext();
729     CHECK_NULL_RETURN(pipeline, defaultHeight.ConvertToPx());
730     auto textFieldTheme = pipeline->GetTheme<TextFieldTheme>();
731     CHECK_NULL_RETURN(textFieldTheme, defaultHeight.ConvertToPx());
732     auto height = textFieldTheme->GetHeight();
733     return static_cast<float>(height.ConvertToPx());
734 }
735 
GetTextFieldDefaultImageHeight()736 float TextFieldLayoutAlgorithm::GetTextFieldDefaultImageHeight()
737 {
738     const auto defaultHeight = 40.0_vp;
739     auto pipeline = PipelineContext::GetCurrentContext();
740     CHECK_NULL_RETURN(pipeline, defaultHeight.ConvertToPx());
741     auto textFieldTheme = pipeline->GetTheme<TextFieldTheme>();
742     CHECK_NULL_RETURN(textFieldTheme, defaultHeight.ConvertToPx());
743     auto height = textFieldTheme->GetIconHotZoneSize();
744     return static_cast<float>(height.ConvertToPx());
745 }
746 
SetPropertyToModifier(const TextStyle & textStyle,RefPtr<TextFieldContentModifier> modifier)747 void TextFieldLayoutAlgorithm::SetPropertyToModifier(
748     const TextStyle& textStyle, RefPtr<TextFieldContentModifier> modifier)
749 {
750     CHECK_NULL_VOID(modifier);
751     modifier->SetFontFamilies(textStyle.GetFontFamilies());
752     modifier->SetFontSize(textStyle.GetFontSize());
753     modifier->SetFontWeight(textStyle.GetFontWeight());
754     modifier->SetTextColor(textStyle.GetTextColor());
755     modifier->SetFontStyle(textStyle.GetFontStyle());
756 }
757 
UpdateUnitLayout(LayoutWrapper * layoutWrapper)758 void TextFieldLayoutAlgorithm::UpdateUnitLayout(LayoutWrapper* layoutWrapper)
759 {
760     auto frameNode = layoutWrapper->GetHostNode();
761     CHECK_NULL_VOID(frameNode);
762     auto pattern = frameNode->GetPattern<TextFieldPattern>();
763     CHECK_NULL_VOID(pattern);
764     auto children = frameNode->GetChildren();
765     const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
766     CHECK_NULL_VOID(content);
767     auto contentSize = content->GetRect().GetSize();
768     auto size = layoutWrapper->GetGeometryNode()->GetFrameSize();
769     auto layoutProperty = AceType::DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
770     CHECK_NULL_VOID(layoutProperty);
771     if (!children.empty() && layoutProperty->GetShowUnderlineValue(false) &&
772         layoutProperty->GetTextInputTypeValue(TextInputType::UNSPECIFIED) == TextInputType::UNSPECIFIED) {
773         auto childWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
774         CHECK_NULL_VOID(childWrapper);
775         auto textLayoutProperty = DynamicCast<TextLayoutProperty>(childWrapper->GetLayoutProperty());
776         auto textGeometryNode = childWrapper->GetGeometryNode();
777         CHECK_NULL_VOID(textGeometryNode);
778         auto childFrameSize = textGeometryNode->GetFrameSize();
779         unitWidth_ = childFrameSize.Width();
780         textGeometryNode->SetFrameOffset(
781             OffsetF({ content->GetRect().GetX() + contentSize.Width() - childFrameSize.Width(), 0.0 }));
782         if (childFrameSize.Height() < size.Height()) {
783             childWrapper->GetGeometryNode()->SetFrameSize(SizeF({ unitWidth_, size.Height() }));
784         }
785         childWrapper->Layout();
786     }
787 }
788 } // namespace OHOS::Ace::NG
789