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_area/text_area_layout_algorithm.h"
17
18 #include <optional>
19
20 #include "base/geometry/dimension.h"
21 #include "base/utils/utils.h"
22 #include "core/components/common/layout/constants.h"
23 #include "core/components_ng/pattern/text_field/text_field_pattern.h"
24 #include "core/pipeline/pipeline_base.h"
25 #include "core/pipeline_ng/pipeline_context.h"
26
27 namespace OHOS::Ace::NG {
28 namespace {
29 constexpr float PARAGRAPH_SAVE_BOUNDARY = 1.0f;
30 } // namespace
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)31 std::optional<SizeF> TextAreaLayoutAlgorithm::MeasureContent(
32 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
33 {
34 auto frameNode = layoutWrapper->GetHostNode();
35 CHECK_NULL_RETURN(frameNode, std::nullopt);
36 auto textFieldLayoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
37 auto pattern = frameNode->GetPattern<TextFieldPattern>();
38 CHECK_NULL_RETURN(pattern, std::nullopt);
39
40 // Construct text style.
41 TextStyle textStyle;
42 ConstructTextStyles(frameNode, textStyle, textContent_, showPlaceHolder_);
43
44 auto isInlineStyle = pattern->IsNormalInlineState();
45 if (!isInlineStyle && textFieldLayoutProperty->HasNormalMaxViewLines()) {
46 textStyle.SetMaxLines(textFieldLayoutProperty->GetNormalMaxViewLines().value());
47 }
48
49 if (isInlineStyle && textFieldLayoutProperty->HasTextOverflow()) {
50 if (textFieldLayoutProperty->HasTextOverflowMaxLines()) {
51 textStyle.SetMaxLines(textFieldLayoutProperty->GetTextOverflowMaxLinesValue());
52 } else if (textFieldLayoutProperty->HasNormalMaxViewLines()) {
53 textStyle.SetMaxLines(textFieldLayoutProperty->GetNormalMaxViewLines().value());
54 }
55 }
56
57 direction_ = textFieldLayoutProperty->GetLayoutDirection();
58
59 // Create paragraph.
60 pattern->SetAdaptFontSize(std::nullopt);
61 auto textFieldContentConstraint = CalculateContentMaxSizeWithCalculateConstraint(contentConstraint, layoutWrapper);
62 if (IsNeedAdaptFontSize(textStyle, textFieldLayoutProperty, textFieldContentConstraint)) {
63 if (!AddAdaptFontSizeAndAnimations(textStyle, textFieldLayoutProperty, textFieldContentConstraint,
64 layoutWrapper)) {
65 return std::nullopt;
66 }
67 pattern->SetAdaptFontSize(textStyle.GetFontSize());
68 } else {
69 CreateParagraphEx(textStyle, textContent_, contentConstraint, layoutWrapper);
70 }
71
72 autoWidth_ = textFieldLayoutProperty->GetWidthAutoValue(false);
73
74 if (textContent_.empty()) {
75 // Used for empty text.
76 preferredHeight_ = pattern->PreferredLineHeight(true);
77 }
78
79 // Paragraph layout.}
80 if (isInlineStyle) {
81 auto fontSize = pattern->FontSizeConvertToPx(textStyle.GetFontSize());
82 auto paragraphData = CreateParagraphData { false, fontSize };
83 CreateInlineParagraph(textStyle, textContent_, false, pattern->GetNakedCharPosition(), paragraphData);
84 return InlineMeasureContent(textFieldContentConstraint, layoutWrapper);
85 } else if (showPlaceHolder_) {
86 return PlaceHolderMeasureContent(textFieldContentConstraint, layoutWrapper);
87 } else {
88 return TextAreaMeasureContent(textFieldContentConstraint, layoutWrapper);
89 }
90 }
91
Measure(LayoutWrapper * layoutWrapper)92 void TextAreaLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
93 {
94 OptionalSizeF frameSize;
95 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
96 auto frameNode = layoutWrapper->GetHostNode();
97 CHECK_NULL_VOID(frameNode);
98 auto pattern = frameNode->GetPattern<TextFieldPattern>();
99 CHECK_NULL_VOID(pattern);
100 float contentWidth = 0.0f;
101 float contentHeight = 0.0f;
102 if (content) {
103 auto contentSize = content->GetRect().GetSize();
104 contentWidth = contentSize.Width();
105 contentHeight = contentSize.Height();
106 }
107 // Add children height;
108 auto counterNode = pattern->GetCounterNode().Upgrade();
109 if (counterNode && !pattern->IsNormalInlineState()) {
110 auto counterSize = counterNode->GetGeometryNode()->GetFrameSize();
111 contentHeight += counterSize.Height();
112 }
113
114 auto finalWidth = 0;
115 if (pattern->IsNormalInlineState() && pattern->HasFocus()) {
116 finalWidth = LessOrEqual(contentWidth, 0) ? 0 :
117 contentWidth + pattern->GetHorizontalPaddingAndBorderSum() + PARAGRAPH_SAVE_BOUNDARY;
118 frameSize.SetWidth(finalWidth);
119 frameSize.SetHeight(contentHeight + pattern->GetVerticalPaddingAndBorderSum() + PARAGRAPH_SAVE_BOUNDARY);
120 } else {
121 // The width after MeasureContent is already optimal, but the height needs to be constrained in Measure.
122 finalWidth = LessOrEqual(contentWidth, 0) ? 0 : contentWidth + pattern->GetHorizontalPaddingAndBorderSum();
123 frameSize.SetWidth(finalWidth);
124 ConstraintHeight(layoutWrapper, frameSize, contentHeight);
125 }
126 layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize.ConvertToSizeT());
127 }
128
ConstraintHeight(LayoutWrapper * layoutWrapper,OptionalSizeF & frameSize,float contentHeight)129 void TextAreaLayoutAlgorithm::ConstraintHeight(LayoutWrapper* layoutWrapper, OptionalSizeF& frameSize,
130 float contentHeight)
131 {
132 auto frameNode = layoutWrapper->GetHostNode();
133 CHECK_NULL_VOID(frameNode);
134 auto pattern = frameNode->GetPattern<TextFieldPattern>();
135 CHECK_NULL_VOID(pattern);
136 auto textFieldLayoutProperty = pattern->GetLayoutProperty<TextFieldLayoutProperty>();
137 CHECK_NULL_VOID(textFieldLayoutProperty);
138 auto contentConstraint = layoutWrapper->GetLayoutProperty()->CreateContentConstraint();
139 auto textFieldContentConstraint =
140 CalculateContentMaxSizeWithCalculateConstraint(contentConstraint, layoutWrapper);
141 if (textFieldContentConstraint.selfIdealSize.Height().has_value()) {
142 if (LessOrEqual(textFieldContentConstraint.maxSize.Height(), 0)) {
143 frameSize.SetHeight(textFieldContentConstraint.maxSize.Height());
144 } else {
145 frameSize.SetHeight(
146 textFieldContentConstraint.maxSize.Height() + pattern->GetVerticalPaddingAndBorderSum());
147 }
148 } else {
149 frameSize.SetHeight(contentHeight + pattern->GetVerticalPaddingAndBorderSum());
150 }
151
152 // Height is constrained by the CalcLayoutConstraint.
153 const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
154 if (Container::LessThanAPIVersion(PlatformVersion::VERSION_TEN)) {
155 frameSize.Constrain(layoutConstraint->minSize, layoutConstraint->maxSize);
156 } else if (!layoutWrapper->GetLayoutProperty()->GetLayoutRect()) {
157 auto finalSize = UpdateOptionSizeByCalcLayoutConstraint(frameSize,
158 layoutWrapper->GetLayoutProperty()->GetCalcLayoutConstraint(),
159 layoutWrapper->GetLayoutProperty()->GetLayoutConstraint()->percentReference);
160 frameSize.SetHeight(finalSize.Height());
161 }
162 }
163
Layout(LayoutWrapper * layoutWrapper)164 void TextAreaLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
165 {
166 // update child position.
167 auto frameNode = layoutWrapper->GetHostNode();
168 CHECK_NULL_VOID(frameNode);
169 auto pattern = frameNode->GetPattern<TextFieldPattern>();
170 CHECK_NULL_VOID(pattern);
171 auto size = layoutWrapper->GetGeometryNode()->GetFrameSize() -
172 SizeF(pattern->GetHorizontalPaddingAndBorderSum(), pattern->GetVerticalPaddingAndBorderSum());
173
174 // Remove counterNode height.
175 auto counterNodeLayoutWrapper = layoutWrapper->GetOrCreateChildByIndex(0);
176 if (counterNodeLayoutWrapper && !pattern->IsNormalInlineState()) {
177 auto counterHeight = counterNodeLayoutWrapper->GetGeometryNode()->GetFrameSize().Height();
178 size.SetHeight(size.Height() - counterHeight);
179 }
180
181 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
182 CHECK_NULL_VOID(content);
183 auto layoutProperty = DynamicCast<TextFieldLayoutProperty>(layoutWrapper->GetLayoutProperty());
184 CHECK_NULL_VOID(layoutProperty);
185 auto context = layoutWrapper->GetHostNode()->GetContext();
186 CHECK_NULL_VOID(context);
187 parentGlobalOffset_ = layoutWrapper->GetHostNode()->GetPaintRectOffset() - context->GetRootRect().GetOffset();
188 auto align = Alignment::TOP_CENTER;
189
190 auto border = pattern->GetBorderWidthProperty();
191 auto offsetBase = OffsetF(pattern->GetPaddingLeft() + pattern->GetBorderLeft(border),
192 pattern->GetPaddingTop() + pattern->GetBorderTop(border));
193 if (layoutWrapper->GetLayoutProperty()->GetPositionProperty()) {
194 align = layoutWrapper->GetLayoutProperty()->GetPositionProperty()->GetAlignment().value_or(align);
195 }
196 // Update text position.
197 if (LessOrEqual(textRect_.Height(), content->GetRect().Height())) {
198 auto textRect = textRect_.GetSize();
199 if (LessOrEqual(textRect.Height(), 0.0)) {
200 textRect.SetHeight(content->GetRect().Height());
201 }
202 OffsetF textRectOffSet = Alignment::GetAlignPosition(size, textRect, align);
203 textRect_.SetOffset(OffsetF(0.0f, textRectOffSet.GetY()) + offsetBase);
204 content->SetOffset(OffsetF(0.0f, textRectOffSet.GetY()) + offsetBase);
205 } else {
206 textRect_.SetOffset(
207 showPlaceHolder_ ? offsetBase : OffsetF(offsetBase.GetX(), pattern->GetTextRect().GetOffset().GetY()));
208 content->SetOffset(offsetBase);
209 }
210 // CounterNode Layout.
211 auto isInlineStyle = pattern->IsNormalInlineState();
212 if (layoutProperty->GetShowCounterValue(false) && layoutProperty->HasMaxLength() && !isInlineStyle) {
213 TextFieldLayoutAlgorithm::CounterLayout(layoutWrapper);
214 }
215 }
216
CreateParagraphEx(const TextStyle & textStyle,const std::string & content,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)217 bool TextAreaLayoutAlgorithm::CreateParagraphEx(const TextStyle& textStyle, const std::string& content,
218 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
219 {
220 // update child position.
221 auto frameNode = layoutWrapper->GetHostNode();
222 CHECK_NULL_RETURN(frameNode, false);
223 auto pattern = frameNode->GetPattern<TextFieldPattern>();
224 CHECK_NULL_RETURN(pattern, false);
225 auto isInlineStyle = pattern->IsNormalInlineState();
226 auto fontSize = pattern->FontSizeConvertToPx(textStyle.GetFontSize());
227 auto paragraphData = CreateParagraphData { false, fontSize };
228 if (pattern->IsDragging() && !showPlaceHolder_ && !isInlineStyle) {
229 CreateParagraph(textStyle, pattern->GetDragContents(), content, false, paragraphData);
230 } else {
231 CreateParagraph(textStyle, content, false, pattern->GetNakedCharPosition(), paragraphData);
232 }
233 return true;
234 }
235 } // namespace OHOS::Ace::NG
236