1 /*
2 * Copyright (c) 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_input/text_input_layout_algorithm.h"
17
18 #include "base/utils/utils.h"
19 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
20
21 namespace OHOS::Ace::NG {
22
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)23 std::optional<SizeF> TextInputLayoutAlgorithm::MeasureContent(
24 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
25 {
26 auto frameNode = layoutWrapper->GetHostNode();
27 CHECK_NULL_RETURN(frameNode, std::nullopt);
28 auto textFieldLayoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
29 auto pattern = frameNode->GetPattern<TextFieldPattern>();
30 CHECK_NULL_RETURN(pattern, std::nullopt);
31
32 // Construct text style.
33 TextStyle textStyle;
34 ConstructTextStyles(frameNode, textStyle, textContent_, showPlaceHolder_);
35 std::replace(textContent_.begin(), textContent_.end(), u'\n', u' ');
36
37 auto isInlineStyle = pattern->IsNormalInlineState();
38
39 direction_ = textFieldLayoutProperty->GetLayoutDirection();
40
41 // Create paragraph.
42 pattern->SetAdaptFontSize(std::nullopt);
43 auto disableTextAlign = !pattern->IsTextArea() && !showPlaceHolder_ && !isInlineStyle;
44 textFieldContentConstraint_ = CalculateContentMaxSizeWithCalculateConstraint(contentConstraint, layoutWrapper);
45 auto contentConstraintWithoutResponseArea =
46 BuildLayoutConstraintWithoutResponseArea(textFieldContentConstraint_, layoutWrapper);
47 if (IsNeedAdaptFontSize(textStyle, textFieldLayoutProperty, textFieldContentConstraint_)) {
48 if (!AddAdaptFontSizeAndAnimations(
49 textStyle, textFieldLayoutProperty, contentConstraintWithoutResponseArea, layoutWrapper)) {
50 return std::nullopt;
51 }
52 pattern->SetAdaptFontSize(textStyle.GetFontSize());
53 } else {
54 CreateParagraphEx(textStyle, textContent_, contentConstraint, layoutWrapper);
55 }
56
57 autoWidth_ = textFieldLayoutProperty->GetWidthAutoValue(false);
58 isFontSizeNonPositive_ = IsFontSizeNonPositive(textStyle);
59
60 if (textContent_.empty()) {
61 // Used for empty text.
62 preferredHeight_ = pattern->PreferredLineHeight(true);
63 }
64
65 // Paragraph layout.
66 if (isInlineStyle) {
67 auto fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
68 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale(), textStyle.IsAllowScale());
69 auto paragraphData = CreateParagraphData { disableTextAlign, fontSize };
70 CreateInlineParagraph(textStyle, textContent_, false, pattern->GetNakedCharPosition(), paragraphData);
71 return InlineMeasureContent(contentConstraintWithoutResponseArea, layoutWrapper);
72 }
73 if (showPlaceHolder_) {
74 return PlaceHolderMeasureContent(contentConstraintWithoutResponseArea, layoutWrapper, 0);
75 }
76 return TextInputMeasureContent(contentConstraintWithoutResponseArea, layoutWrapper, 0);
77 }
78
IsFontSizeNonPositive(const TextStyle & textStyle) const79 bool TextInputLayoutAlgorithm::IsFontSizeNonPositive(const TextStyle& textStyle) const
80 {
81 return textStyle.GetFontSize().IsNonPositive();
82 }
83
Measure(LayoutWrapper * layoutWrapper)84 void TextInputLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
85 {
86 const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
87 OptionalSizeF frameSize;
88 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
89 auto frameNode = layoutWrapper->GetHostNode();
90 CHECK_NULL_VOID(frameNode);
91 auto pattern = frameNode->GetPattern<TextFieldPattern>();
92 CHECK_NULL_VOID(pattern);
93 float contentWidth = 0.0f;
94 float contentHeight = 0.0f;
95 if (content) {
96 auto contentSize = content->GetRect().GetSize();
97 contentWidth = contentSize.Width();
98 contentHeight = contentSize.Height();
99 }
100 auto defaultHeight = GetDefaultHeightByType(layoutWrapper);
101
102 auto responseAreaWidth = 0.0f;
103 if (pattern->GetCleanNodeResponseArea()) {
104 responseAreaWidth += pattern->GetCleanNodeResponseArea()->GetFrameSize().Width();
105 }
106 if (pattern->GetResponseArea()) {
107 responseAreaWidth += pattern->GetResponseArea()->GetFrameSize().Width();
108 }
109 frameSize.SetWidth(contentWidth + pattern->GetHorizontalPaddingAndBorderSum() + responseAreaWidth);
110
111 if (textFieldContentConstraint_.selfIdealSize.Height().has_value()) {
112 if (LessOrEqual(contentWidth, 0)) {
113 frameSize.SetHeight(textFieldContentConstraint_.maxSize.Height());
114 } else {
115 frameSize.SetHeight(
116 textFieldContentConstraint_.maxSize.Height() + pattern->GetVerticalPaddingAndBorderSum());
117 }
118 } else {
119 auto height = LessNotEqual(contentHeight, defaultHeight)
120 ? defaultHeight + pattern->GetVerticalPaddingAndBorderSum()
121 : contentHeight + pattern->GetVerticalPaddingAndBorderSum();
122 frameSize.SetHeight(height);
123 }
124 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
125 frameSize.Constrain(layoutConstraint->minSize, layoutConstraint->maxSize);
126 } else if (!layoutWrapper->GetLayoutProperty()->GetLayoutRect()) {
127 auto frameSizeConstraint = CalculateFrameSizeConstraint(textFieldContentConstraint_, layoutWrapper);
128 auto finalSize = UpdateOptionSizeByCalcLayoutConstraint(frameSize,
129 layoutWrapper->GetLayoutProperty()->GetCalcLayoutConstraint(),
130 layoutWrapper->GetLayoutProperty()->GetLayoutConstraint()->percentReference);
131 frameSize.SetHeight(finalSize.Height());
132 frameSize.Constrain(frameSizeConstraint.minSize, frameSizeConstraint.maxSize);
133 }
134 layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize.ConvertToSizeT());
135 }
136
Layout(LayoutWrapper * layoutWrapper)137 void TextInputLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
138 {
139 auto frameNode = layoutWrapper->GetHostNode();
140 CHECK_NULL_VOID(frameNode);
141 auto pattern = frameNode->GetPattern<TextFieldPattern>();
142 CHECK_NULL_VOID(pattern);
143 auto geometryNode = layoutWrapper->GetGeometryNode();
144 CHECK_NULL_VOID(geometryNode);
145 auto size = geometryNode->GetFrameSize() -
146 SizeF(pattern->GetHorizontalPaddingAndBorderSum(), pattern->GetVerticalPaddingAndBorderSum());
147 const auto& content = geometryNode->GetContent();
148 CHECK_NULL_VOID(content);
149 SizeT<float> contentSize = content->GetRect().GetSize();
150 auto layoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
151 CHECK_NULL_VOID(layoutProperty);
152 PipelineContext* context = frameNode->GetContext();
153 CHECK_NULL_VOID(context);
154 parentGlobalOffset_ = frameNode->GetPaintRectOffset(false, true) - context->GetRootRect().GetOffset();
155 Alignment align = Alignment::CENTER;
156 auto isRTL = layoutProperty->GetNonAutoLayoutDirection() == TextDirection::RTL;
157 if (layoutProperty->GetPositionProperty()) {
158 align = layoutProperty->GetPositionProperty()->GetAlignment().value_or(align);
159 }
160 auto border = pattern->GetBorderWidthProperty();
161 OffsetF offsetBase = OffsetF(pattern->GetPaddingLeft() + pattern->GetBorderLeft(border),
162 pattern->GetPaddingTop() + pattern->GetBorderTop(border));
163
164 auto responseArea = pattern->GetResponseArea();
165 auto cleanNodeResponseArea = pattern->GetCleanNodeResponseArea();
166 auto unitNodeWidth = 0.0f;
167 if (responseArea) {
168 int32_t childIndex = frameNode->GetChildIndex(responseArea->GetFrameNode());
169 responseArea->Layout(layoutWrapper, childIndex, unitNodeWidth);
170 }
171 if (cleanNodeResponseArea) {
172 int32_t childIndex = frameNode->GetChildIndex(cleanNodeResponseArea->GetFrameNode());
173 cleanNodeResponseArea->Layout(layoutWrapper, childIndex, unitNodeWidth);
174 }
175
176 UpdateContentPositionParams params = {
177 .isRTL = isRTL,
178 .offsetBase = offsetBase,
179 .size = size,
180 .contentSize = contentSize,
181 .align = align,
182 .responseArea = responseArea,
183 .cleanResponseArea = cleanNodeResponseArea
184 };
185 UpdateContentPosition(params, content);
186
187 auto paintProperty = pattern->GetPaintProperty<TextFieldPaintProperty>();
188 CHECK_NULL_VOID(paintProperty);
189 UpdateTextRectParams updateTextRectParams = {
190 .layoutProperty = layoutProperty,
191 .pattern = pattern,
192 .contentSize = contentSize,
193 .isRTL = isRTL,
194 .responseArea = responseArea,
195 .cleanResponseArea = cleanNodeResponseArea,
196 .contentOffset = content->GetRect().GetOffset()
197 };
198 UpdateTextRect(updateTextRectParams);
199
200 bool isInlineStyle = pattern->IsNormalInlineState();
201 if (layoutProperty->GetShowCounterValue(false) && layoutProperty->HasMaxLength() && !isInlineStyle) {
202 TextFieldLayoutAlgorithm::CounterLayout(layoutWrapper);
203 }
204 if (pattern->IsShowError()) {
205 TextFieldLayoutAlgorithm::ErrorLayout(layoutWrapper);
206 }
207 }
208
UpdateContentPosition(const UpdateContentPositionParams & params,const std::unique_ptr<GeometryProperty> & content)209 void TextInputLayoutAlgorithm::UpdateContentPosition(const UpdateContentPositionParams ¶ms,
210 const std::unique_ptr<GeometryProperty> &content)
211 {
212 OffsetF contentOffset =
213 params.offsetBase + Alignment::GetAlignPosition(params.size, params.contentSize, params.align);
214 auto offsetBaseX = params.offsetBase.GetX();
215 if (params.isRTL) {
216 if (params.responseArea) {
217 offsetBaseX += params.responseArea->GetAreaRect().Width();
218 }
219 if (params.cleanResponseArea) {
220 offsetBaseX += params.cleanResponseArea->GetAreaRect().Width();
221 }
222 }
223 content->SetOffset(OffsetF(offsetBaseX, contentOffset.GetY()));
224 }
225
UpdateTextRect(const UpdateTextRectParams & params)226 void TextInputLayoutAlgorithm::UpdateTextRect(const UpdateTextRectParams& params)
227 {
228 if (LessOrEqual(textRect_.Width(), params.contentSize.Width())) {
229 float textRectOffsetX = 0.0f;
230 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
231 textRectOffsetX = params.pattern->GetPaddingLeft();
232 } else {
233 auto border = params.pattern->GetBorderWidthProperty();
234 textRectOffsetX = params.pattern->GetPaddingLeft() + params.pattern->GetBorderLeft(border);
235 }
236 bool isEmptyTextEditValue = params.pattern->GetTextUtf16Value().empty();
237 bool isInlineStyle = params.pattern->IsNormalInlineState();
238 if (!isEmptyTextEditValue && !isInlineStyle) {
239 TextAlign textAlign = params.layoutProperty->GetTextAlignValue(TextAlign::START);
240 params.pattern->CheckTextAlignByDirection(textAlign, direction_);
241 }
242 if (params.isRTL) {
243 if (params.responseArea) {
244 RectF responseAreaRect = params.responseArea->GetAreaRect();
245 textRectOffsetX += responseAreaRect.Width();
246 }
247 if (params.cleanResponseArea) {
248 RectF cleanResponseAreaRect = params.cleanResponseArea->GetAreaRect();
249 textRectOffsetX += cleanResponseAreaRect.Width();
250 }
251 textRect_.SetOffset(OffsetF(textRectOffsetX, params.contentOffset.GetY()));
252 } else {
253 textRect_.SetOffset(OffsetF(textRectOffsetX, params.contentOffset.GetY()));
254 }
255 } else {
256 textRect_.SetOffset({ params.pattern->GetTextRect().GetOffset().GetX(), params.contentOffset.GetY() });
257 }
258 }
259
GetDefaultHeightByType(LayoutWrapper * layoutWrapper)260 float TextInputLayoutAlgorithm::GetDefaultHeightByType(LayoutWrapper* layoutWrapper)
261 {
262 auto frameNode = layoutWrapper->GetHostNode();
263 CHECK_NULL_RETURN(frameNode, 0.0f);
264 auto pipeline = frameNode->GetContext();
265 CHECK_NULL_RETURN(pipeline, 0.0f);
266 auto textFieldTheme = pipeline->GetTheme<TextFieldTheme>();
267 CHECK_NULL_RETURN(textFieldTheme, 0.0f);
268 return static_cast<float>(textFieldTheme->GetContentHeight().ConvertToPx());
269 }
270
CreateParagraphEx(const TextStyle & textStyle,const std::u16string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)271 bool TextInputLayoutAlgorithm::CreateParagraphEx(const TextStyle& textStyle, const std::u16string& content,
272 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
273 {
274 // update child position.
275 auto frameNode = layoutWrapper->GetHostNode();
276 CHECK_NULL_RETURN(frameNode, false);
277 auto pattern = frameNode->GetPattern<TextFieldPattern>();
278 CHECK_NULL_RETURN(pattern, false);
279 auto isInlineStyle = pattern->IsNormalInlineState();
280 auto isPasswordType = pattern->IsInPasswordMode();
281 auto disableTextAlign = false;
282 auto fontSize = textStyle.GetFontSize().ConvertToPxDistribute(
283 textStyle.GetMinFontScale(), textStyle.GetMaxFontScale());
284 auto paragraphData = CreateParagraphData { disableTextAlign, fontSize };
285
286 if (pattern->IsDragging() && !showPlaceHolder_ && !isInlineStyle) {
287 CreateParagraph(textStyle, pattern->GetDragContents(), content,
288 isPasswordType && pattern->GetTextObscured() && !showPlaceHolder_, paragraphData);
289 } else {
290 CreateParagraph(textStyle, content, isPasswordType && pattern->GetTextObscured() && !showPlaceHolder_,
291 pattern->GetNakedCharPosition(), paragraphData);
292 }
293 return true;
294 }
295
BuildLayoutConstraintWithoutResponseArea(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)296 LayoutConstraintF TextInputLayoutAlgorithm::BuildLayoutConstraintWithoutResponseArea(
297 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
298 {
299 auto frameNode = layoutWrapper->GetHostNode();
300 CHECK_NULL_RETURN(frameNode, contentConstraint);
301 auto pattern = frameNode->GetPattern<TextFieldPattern>();
302 CHECK_NULL_RETURN(pattern, contentConstraint);
303
304 auto responseArea = pattern->GetResponseArea();
305 auto cleanNodeResponseArea = pattern->GetCleanNodeResponseArea();
306 float childWidth = 0.0f;
307 if (responseArea) {
308 auto childIndex = frameNode->GetChildIndex(responseArea->GetFrameNode());
309 childWidth += responseArea->Measure(layoutWrapper, childIndex).Width();
310 }
311 if (cleanNodeResponseArea) {
312 auto childIndex = frameNode->GetChildIndex(cleanNodeResponseArea->GetFrameNode());
313 childWidth += cleanNodeResponseArea->Measure(layoutWrapper, childIndex).Width();
314 }
315
316 auto newLayoutConstraint = contentConstraint;
317 newLayoutConstraint.maxSize.SetWidth(std::max(newLayoutConstraint.maxSize.Width() - childWidth, 0.0f));
318 newLayoutConstraint.minSize.SetWidth(std::max(newLayoutConstraint.minSize.Width() - childWidth, 0.0f));
319 if (newLayoutConstraint.selfIdealSize.Width()) {
320 newLayoutConstraint.selfIdealSize.SetWidth(newLayoutConstraint.selfIdealSize.Width().value() - childWidth);
321 }
322 return newLayoutConstraint;
323 }
324 } // namespace OHOS::Ace::NG
325