• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params,
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