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