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/rich_editor/rich_editor_layout_algorithm.h"
17
18 #include "base/log/ace_trace.h"
19 #include "base/utils/utils.h"
20 #include "core/components/common/properties/text_style.h"
21 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
22 #include "core/components_ng/pattern/rich_editor/rich_editor_theme.h"
23 #include "core/components_ng/pattern/text/multiple_paragraph_layout_algorithm.h"
24 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
25 #include "core/components_ng/render/paragraph.h"
26 #include "core/components_v2/inspector/inspector_constants.h"
27
28 namespace OHOS::Ace::NG {
RichEditorLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,ParagraphManager * paragraphs,std::optional<TextStyle> typingTextStyle)29 RichEditorLayoutAlgorithm::RichEditorLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans, ParagraphManager* paragraphs,
30 std::optional<TextStyle> typingTextStyle)
31 : pManager_(paragraphs), typingTextStyle_(typingTextStyle)
32 {
33 allSpans_ = spans;
34 // split spans into groups by \newline
35 auto it = spans.begin();
36 while (it != spans.end()) {
37 auto span = *it;
38 // only checking the last char
39 if (StringUtils::ToWstring(span->content).back() == L'\n') {
40 span->SetNeedRemoveNewLine(true);
41 std::list<RefPtr<SpanItem>> newGroup;
42 newGroup.splice(newGroup.begin(), spans, spans.begin(), std::next(it));
43 spans_.push_back(std::move(newGroup));
44
45 it = spans.begin();
46 continue;
47 }
48 span->SetNeedRemoveNewLine(false);
49 // clear placeholder textstyle,it should be modified by text line
50 auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(span);
51 if (placeholderSpanItem) {
52 TextStyle textStyle;
53 placeholderSpanItem->textStyle = textStyle;
54 }
55 ++it;
56 }
57 if (!spans.empty()) {
58 spans_.push_back(std::move(spans));
59 }
60 AppendNewLineSpan();
61 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "spans=%{private}s", SpansToString().c_str());
62 }
63
AppendNewLineSpan()64 void RichEditorLayoutAlgorithm::AppendNewLineSpan()
65 {
66 auto lastSpan = allSpans_.back();
67 CHECK_NULL_VOID(lastSpan);
68 if (StringUtils::ToWstring(lastSpan->content).back() == L'\n') {
69 std::list<RefPtr<SpanItem>> newGroup;
70 auto tailNewLineSpan = AceType::MakeRefPtr<SpanItem>();
71 tailNewLineSpan->content = "\n";
72 tailNewLineSpan->SetNeedRemoveNewLine(true);
73 CopySpanStyle(lastSpan, tailNewLineSpan);
74 newGroup.push_back(tailNewLineSpan);
75 spans_.push_back(std::move(newGroup));
76 }
77 }
78
CopySpanStyle(RefPtr<SpanItem> source,RefPtr<SpanItem> target)79 void RichEditorLayoutAlgorithm::CopySpanStyle(RefPtr<SpanItem> source, RefPtr<SpanItem> target)
80 {
81 if (typingTextStyle_.has_value()) {
82 auto typingTextStyle = typingTextStyle_.value();
83 target->fontStyle->UpdateFontSize(typingTextStyle.GetFontSize());
84 target->textLineStyle->UpdateLineHeight(typingTextStyle.GetLineHeight());
85 return;
86 }
87 if (source->fontStyle->HasFontSize()) {
88 target->fontStyle->UpdateFontSize(source->fontStyle->GetFontSizeValue());
89 }
90
91 if (source->textLineStyle->HasLeadingMargin()) {
92 auto leadingMargin = source->textLineStyle->GetLeadingMarginValue();
93 leadingMargin.pixmap.Reset();
94 target->textLineStyle->UpdateLeadingMargin(leadingMargin);
95 }
96
97 if (source->textLineStyle->HasTextAlign()) {
98 target->textLineStyle->UpdateTextAlign(source->textLineStyle->GetTextAlignValue());
99 }
100
101 if (source->textLineStyle->HasLineHeight()) {
102 target->textLineStyle->UpdateLineHeight(source->textLineStyle->GetLineHeightValue());
103 }
104 }
105
MeasureEmptyContentSize(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)106 std::optional<SizeF> RichEditorLayoutAlgorithm::MeasureEmptyContentSize(
107 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
108 {
109 auto host = layoutWrapper->GetHostNode();
110 CHECK_NULL_RETURN(host, {});
111 auto pipeline = host->GetContext();
112 CHECK_NULL_RETURN(pipeline, {});
113 auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
114 CHECK_NULL_RETURN(richEditorTheme, {});
115 auto defaultCaretHeight = richEditorTheme->GetDefaultCaretHeight().ConvertToPx();
116 auto width = contentConstraint.selfIdealSize.Width().value_or(contentConstraint.maxSize.Width());
117 auto pattern = host->GetPattern<RichEditorPattern>();
118 CHECK_NULL_RETURN(pattern, {});
119 auto presetParagraph = pattern->GetPresetParagraph();
120 if (!presetParagraph) {
121 pattern->PreferredParagraph();
122 }
123 auto contentHeight = defaultCaretHeight;
124 presetParagraph = pattern->GetPresetParagraph();
125 if (presetParagraph) {
126 contentHeight = presetParagraph->GetHeight();
127 }
128 return SizeF(width, static_cast<float>(contentHeight));
129 }
130
MeasureContentSize(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)131 std::optional<SizeF> RichEditorLayoutAlgorithm::MeasureContentSize(
132 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
133 {
134 auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
135 CHECK_NULL_RETURN(layoutProperty, {});
136 TextStyle textStyle;
137 ConstructTextStyles(contentConstraint, layoutWrapper, textStyle);
138 CHECK_NULL_RETURN(BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper), {});
139 pManager_->SetParagraphs(GetParagraphs());
140 return SizeF(pManager_->GetMaxWidth(), pManager_->GetHeight());
141 }
142
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)143 std::optional<SizeF> RichEditorLayoutAlgorithm::MeasureContent(
144 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
145 {
146 ACE_SCOPED_TRACE("RichEditorMeasureContent");
147 pManager_->Reset();
148 SetPlaceholder(layoutWrapper);
149 auto optionalTextSize = spans_.empty()
150 ? MeasureEmptyContentSize(contentConstraint, layoutWrapper)
151 : MeasureContentSize(contentConstraint, layoutWrapper);
152 CHECK_NULL_RETURN(optionalTextSize, {});
153 SizeF res = optionalTextSize.value();
154 res.AddHeight(spans_.empty() ? 0 : shadowOffset_);
155 CHECK_NULL_RETURN(res.IsNonNegative(), {});
156 UpdateRichTextRect(optionalTextSize.value(), layoutWrapper);
157 auto maxHeight = contentConstraint.selfIdealSize.Height().value_or(contentConstraint.maxSize.Height());
158 auto contentHeight = std::min(res.Height(), maxHeight);
159 return SizeF(res.Width(), contentHeight);
160 }
161
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)162 bool RichEditorLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
163 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
164 {
165 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
166 if (!CreateParagraph(textStyle, layoutProperty->GetContent().value_or(""), layoutWrapper, maxSize.Width())) {
167 return false;
168 }
169 CHECK_NULL_RETURN(paragraphManager_, false);
170 auto paragraphInfo = paragraphManager_->GetParagraphs();
171 for (auto pIter = paragraphInfo.begin(); pIter != paragraphInfo.end(); pIter++) {
172 auto paragraph = pIter->paragraph;
173 CHECK_NULL_RETURN(paragraph, false);
174 paragraph->Layout(maxSize.Width());
175 }
176 return ParagraphReLayout(contentConstraint);
177 }
178
CreateParagraph(const TextStyle & textStyle,std::string content,LayoutWrapper * layoutWrapper,double maxWidth)179 bool RichEditorLayoutAlgorithm::CreateParagraph(
180 const TextStyle& textStyle, std::string content, LayoutWrapper* layoutWrapper, double maxWidth)
181 {
182 CHECK_NULL_RETURN(!spans_.empty(), false);
183 if (!paragraphManager_) {
184 paragraphManager_ = AceType::MakeRefPtr<ParagraphManager>();
185 }
186 paragraphManager_->Reset();
187 auto frameNode = layoutWrapper->GetHostNode();
188 CHECK_NULL_RETURN(frameNode, false);
189 auto pipeline = frameNode->GetContextRefPtr();
190 CHECK_NULL_RETURN(pipeline, false);
191 // default paragraph style
192 auto paraStyle = GetParagraphStyle(textStyle, content, layoutWrapper);
193 return UpdateParagraphBySpan(layoutWrapper, paraStyle, maxWidth, textStyle);
194 }
195
GetRichEditorPattern(LayoutWrapper * layoutWrapper)196 RefPtr<RichEditorPattern> RichEditorLayoutAlgorithm::GetRichEditorPattern(LayoutWrapper* layoutWrapper)
197 {
198 CHECK_NULL_RETURN(layoutWrapper, nullptr);
199 auto host = layoutWrapper->GetHostNode();
200 CHECK_NULL_RETURN(host, nullptr);
201 return host->GetPattern<RichEditorPattern>();
202 }
203
UpdateRichTextRect(const SizeF & textSize,LayoutWrapper * layoutWrapper)204 void RichEditorLayoutAlgorithm::UpdateRichTextRect(const SizeF& textSize, LayoutWrapper* layoutWrapper)
205 {
206 auto pattern = GetRichEditorPattern(layoutWrapper);
207 CHECK_NULL_VOID(pattern);
208 richTextRect_.SetSize(pattern->IsShowPlaceholder() ? SizeF() : textSize);
209 }
210
SetPlaceholder(LayoutWrapper * layoutWrapper)211 bool RichEditorLayoutAlgorithm::SetPlaceholder(LayoutWrapper* layoutWrapper)
212 {
213 auto pattern = GetRichEditorPattern(layoutWrapper);
214 CHECK_NULL_RETURN(pattern, false);
215 return pattern->SetPlaceholder(spans_);
216 }
217
GetShadowOffset(const std::list<RefPtr<SpanItem>> & group)218 float RichEditorLayoutAlgorithm::GetShadowOffset(const std::list<RefPtr<SpanItem>>& group)
219 {
220 float shadowOffset = 0.0f;
221 for (auto& span: group) {
222 if (!span->fontStyle || !span->fontStyle->HasTextShadow()) {
223 continue;
224 }
225 auto shadows = span->fontStyle->GetTextShadowValue();
226 float upOffsetY = 0.0f;
227 float downOffsetY = 0.0f;
228 for (const auto& shadow : shadows) {
229 auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
230 auto shadowOffsetY = shadow.GetOffset().GetY();
231 if (LessOrEqual(shadowOffsetY, 0.0f) &&
232 LessNotEqual(shadowOffsetY, upOffsetY)) {
233 upOffsetY = shadowOffsetY - shadowBlurRadius;
234 } else if (GreatOrEqual(shadowOffsetY, 0.0f) &&
235 GreatNotEqual(shadowOffsetY + shadowBlurRadius, downOffsetY)) {
236 downOffsetY = shadowOffsetY + shadowBlurRadius;
237 }
238 }
239 shadowOffset = std::max(shadowOffset, downOffsetY - upOffsetY);
240 }
241 return shadowOffset;
242 }
243
Measure(LayoutWrapper * layoutWrapper)244 void RichEditorLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
245 {
246 MultipleParagraphLayoutAlgorithm::Measure(layoutWrapper);
247 const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
248 OptionalSizeF idealSize =
249 CreateIdealSize(layoutConstraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT_MAIN_AXIS);
250 if (layoutConstraint->maxSize.Width() < layoutConstraint->minSize.Width()) {
251 idealSize.SetWidth(layoutConstraint->minSize.Width());
252 }
253 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
254 frameSize.SetWidth(idealSize.ConvertToSizeT().Width());
255 layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize);
256 }
257
Layout(LayoutWrapper * layoutWrapper)258 void RichEditorLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
259 {
260 auto context = layoutWrapper->GetHostNode()->GetContext();
261 CHECK_NULL_VOID(context);
262 parentGlobalOffset_ = layoutWrapper->GetHostNode()->GetPaintRectOffset() - context->GetRootRect().GetOffset();
263 MultipleParagraphLayoutAlgorithm::Layout(layoutWrapper);
264 }
265
GetContentOffset(LayoutWrapper * layoutWrapper)266 OffsetF RichEditorLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
267 {
268 auto contentOffset = SetContentOffset(layoutWrapper);
269 auto host = layoutWrapper->GetHostNode();
270 CHECK_NULL_RETURN(host, contentOffset);
271 auto pattern = host->GetPattern<RichEditorPattern>();
272 CHECK_NULL_RETURN(pattern, contentOffset);
273 richTextRect_.SetOffset(OffsetF(contentOffset.GetX(), pattern->GetTextRect().GetY()));
274 return richTextRect_.GetOffset();
275 }
276
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const277 ParagraphStyle RichEditorLayoutAlgorithm::GetParagraphStyle(
278 const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
279 {
280 auto style = MultipleParagraphLayoutAlgorithm::GetParagraphStyle(textStyle, content, layoutWrapper);
281 style.fontSize = textStyle.GetFontSize().ConvertToPx();
282 if (!pManager_->minParagraphFontSize.has_value() ||
283 GreatNotEqual(pManager_->minParagraphFontSize.value(), style.fontSize)) {
284 pManager_->minParagraphFontSize = style.fontSize;
285 }
286
287 return style;
288 }
289
GetFirstTextSpanItem() const290 RefPtr<SpanItem> RichEditorLayoutAlgorithm::GetFirstTextSpanItem() const
291 {
292 auto& spanGroup = GetSpans();
293 auto it = spanGroup.begin();
294 while (it != spanGroup.end()) {
295 if (!DynamicCast<PlaceholderSpanItem>(*it)) {
296 return *it;
297 }
298 ++it;
299 }
300 return *spanGroup.begin();
301 }
302
HandleEmptyParagraph(RefPtr<Paragraph> paragraph,const std::list<RefPtr<SpanItem>> & spanGroup)303 void RichEditorLayoutAlgorithm::HandleEmptyParagraph(RefPtr<Paragraph> paragraph,
304 const std::list<RefPtr<SpanItem>>& spanGroup)
305 {
306 CHECK_NULL_VOID(paragraph && spanGroup.size() == 1);
307 auto spanItem = spanGroup.front();
308 CHECK_NULL_VOID(spanItem);
309 auto content = spanItem->GetSpanContent(spanItem->GetSpanContent());
310 CHECK_NULL_VOID(content.empty());
311 auto textStyle = spanItem->GetTextStyle();
312 CHECK_NULL_VOID(textStyle);
313 paragraph->PushStyle(textStyle.value());
314 }
315
GetParagraphStyleSpanItem(const std::list<RefPtr<SpanItem>> & spanGroup)316 RefPtr<SpanItem> RichEditorLayoutAlgorithm::GetParagraphStyleSpanItem(const std::list<RefPtr<SpanItem>>& spanGroup)
317 {
318 auto it = spanGroup.begin();
319 while (it != spanGroup.end()) {
320 if (!AceType::DynamicCast<PlaceholderSpanItem>(*it)) {
321 return *it;
322 }
323 ++it;
324 }
325 return *spanGroup.begin();
326 }
327
328 } // namespace OHOS::Ace::NG
329