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/utils/utils.h"
19 #include "core/components_ng/pattern/rich_editor/rich_editor_pattern.h"
20 #include "core/components_ng/pattern/rich_editor/rich_editor_theme.h"
21 #include "core/components_ng/pattern/rich_editor/style_manager.h"
22 #include "core/components_ng/pattern/text/multiple_paragraph_layout_algorithm.h"
23 #include "core/components_ng/pattern/text/paragraph_util.h"
24
25 namespace {
26 constexpr int32_t CHILDREN_SIZE = 1;
27 }
28
29 namespace OHOS::Ace::NG {
30
RichEditorLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,RichEditorParagraphManager * paragraphs,LRUMap<uint64_t,RefPtr<Paragraph>> * paraMapPtr,std::unique_ptr<StyleManager> & styleManager,bool needShowPlaceholder,const AISpanLayoutInfo & aiSpanLayoutInfo)31 RichEditorLayoutAlgorithm::RichEditorLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,
32 RichEditorParagraphManager* paragraphs, LRUMap<uint64_t, RefPtr<Paragraph>>* paraMapPtr,
33 std::unique_ptr<StyleManager>& styleManager, bool needShowPlaceholder, const AISpanLayoutInfo& aiSpanLayoutInfo)
34 : allSpans_(spans), pManager_(paragraphs), paraMapPtr_(paraMapPtr), styleManager_(styleManager),
35 needShowPlaceholder_(needShowPlaceholder)
36 {
37 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::Constructor");
38 // split spans into groups by \newline
39 IF_TRUE(spans.empty() && paraMapPtr_, paraMapPtr_->Clear());
40 auto it = spans.begin();
41 while (it != spans.end()) {
42 auto span = *it;
43 // only checking the last char
44 if (!span->content.empty() && span->content.back() == u'\n') {
45 span->SetNeedRemoveNewLine(true);
46 std::list<RefPtr<SpanItem>> newGroup;
47 newGroup.splice(newGroup.begin(), spans, spans.begin(), std::next(it));
48 spans_.push_back(std::move(newGroup));
49
50 it = spans.begin();
51 continue;
52 }
53 span->SetNeedRemoveNewLine(false);
54 // clear placeholder textstyle,it should be modified by text line
55 auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(span);
56 if (placeholderSpanItem) {
57 TextStyle textStyle;
58 placeholderSpanItem->textStyle_ = textStyle;
59 }
60 ++it;
61 }
62 if (!spans.empty()) {
63 spans_.push_back(std::move(spans));
64 }
65 HandleAISpan(allSpans_, aiSpanLayoutInfo);
66 HandleParagraphCache();
67 AppendNewLineSpan();
68 TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "spans=%{public}s", SpansToString().c_str());
69 }
70
HandleAISpan(const std::list<RefPtr<SpanItem>> & spans,const AISpanLayoutInfo & aiSpanLayoutInfo)71 void RichEditorLayoutAlgorithm::HandleAISpan(
72 const std::list<RefPtr<SpanItem>>& spans, const AISpanLayoutInfo& aiSpanLayoutInfo)
73 {
74 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::HandleAISpan");
75 bool needShowAIDetect = aiSpanLayoutInfo.needShowAIDetect;
76 if (!needShowAIDetect) {
77 for (const auto& span : spans) {
78 CHECK_NULL_CONTINUE(span->aiSpanResultCount != 0);
79 span->aiSpanResultCount = 0;
80 span->needReLayout = true;
81 }
82 return;
83 }
84
85 // need show ai detect
86
87 // 1. record ai result count
88 std::vector<int> aiSpanCounts;
89 aiSpanCounts.reserve(spans.size());
90 for (const auto& span : spans) {
91 aiSpanCounts.push_back(span->aiSpanResultCount);
92 span->aiSpanResultCount = 0;
93 }
94
95 // 2. update ai result count
96 HandleAISpan(spans, aiSpanLayoutInfo.aiSpanMap);
97
98 // 3. mark need relayout if result count changed
99 auto spanIter = spans.begin();
100 auto countsIter = aiSpanCounts.begin();
101 for (; spanIter != spans.end() && countsIter != aiSpanCounts.end(); ++spanIter, ++countsIter) {
102 auto& span = (*spanIter);
103 IF_TRUE(span->aiSpanResultCount != 0, span->needReLayout |= span->needReLayoutParagraph);
104 IF_TRUE(span->aiSpanResultCount != (*countsIter), span->needReLayout = true);
105 }
106 }
107
HandleAISpan(const std::list<RefPtr<SpanItem>> & spans,const std::map<int32_t,AISpan> & aiSpanMap)108 void RichEditorLayoutAlgorithm::HandleAISpan(
109 const std::list<RefPtr<SpanItem>>& spans, const std::map<int32_t, AISpan>& aiSpanMap)
110 {
111 auto spanIter = spans.begin();
112 auto aiSpanIter = aiSpanMap.begin();
113 while (spanIter != spans.end() && aiSpanIter != aiSpanMap.end()) {
114 const AISpan& aiSpan = aiSpanIter->second;
115 const RefPtr<SpanItem>& span = *spanIter;
116
117 bool hasIntersection = span->rangeStart < aiSpan.end && aiSpan.start < span->position;
118 if (hasIntersection) {
119 ++span->aiSpanResultCount;
120 IF_TRUE(aiSpan.end <= span->position, ++aiSpanIter);
121 IF_TRUE(span->position <= aiSpan.end, ++spanIter);
122 continue;
123 }
124
125 if (!hasIntersection) {
126 bool aiSpanRangeAhead = aiSpan.start >= span->position;
127 bool spanRangeAhead = aiSpan.end <= span->rangeStart;
128 IF_TRUE(aiSpanRangeAhead, ++spanIter);
129 IF_TRUE(spanRangeAhead, ++aiSpanIter);
130 continue;
131 }
132
133 TAG_LOGE(AceLogTag::ACE_RICH_TEXT,
134 "HandleAISpan range error, aiSpanRange=[%{public}d,%{public}d], spanRange=[%{public}d,%{public}d]",
135 aiSpan.start, aiSpan.end, span->rangeStart, span->position);
136 ++aiSpanIter;
137 ++spanIter;
138 }
139 }
140
Hash(uint64_t hash,const RefPtr<SpanItem> & span)141 inline uint64_t RichEditorLayoutAlgorithm::Hash(uint64_t hash, const RefPtr<SpanItem>& span)
142 {
143 constexpr uint64_t MAGIC_NUMBER = 0x9e3779b9;
144 constexpr int32_t LEFT_SHIFT = 6;
145 constexpr int32_t RIGHT_SHIFT = 2;
146 return hash ^ (static_cast<uint64_t>(span->nodeId_) + MAGIC_NUMBER + (hash << LEFT_SHIFT) + (hash >> RIGHT_SHIFT));
147 }
148
HandleParagraphCache()149 void RichEditorLayoutAlgorithm::HandleParagraphCache()
150 {
151 CHECK_NULL_VOID(paraMapPtr_);
152 for (const auto& group : spans_) {
153 uint64_t hash = 0;
154 bool needReLayout = false;
155 for (const auto& child : group) {
156 hash = Hash(hash, child);
157 needReLayout |= child->needReLayout;
158 }
159 if (needReLayout) {
160 paraMapPtr_->Erase(hash);
161 }
162 }
163 }
164
Hash(const std::list<RefPtr<SpanItem>> & spanGroup)165 uint64_t RichEditorLayoutAlgorithm::Hash(const std::list<RefPtr<SpanItem>>& spanGroup)
166 {
167 uint64_t hash = 0;
168 for (const auto& child : spanGroup) {
169 hash = Hash(hash, child);
170 }
171 return hash;
172 }
173
GetOrCreateParagraph(const std::list<RefPtr<SpanItem>> & group,const ParagraphStyle & paraStyle,const std::map<int32_t,AISpan> & aiSpanMap)174 RefPtr<Paragraph> RichEditorLayoutAlgorithm::GetOrCreateParagraph(const std::list<RefPtr<SpanItem>>& group,
175 const ParagraphStyle& paraStyle, const std::map<int32_t, AISpan>& aiSpanMap)
176 {
177 if (!paraMapPtr_) {
178 useParagraphCache_ = false;
179 return Paragraph::CreateRichEditorParagraph(paraStyle, FontCollection::Current());
180 }
181 uint64_t hash = Hash(group);
182 paragraphKeySet_.insert(hash);
183 auto it = paraMapPtr_->Get(hash);
184 bool findCache = it != paraMapPtr_->End() && it->second != nullptr;
185 bool directionChanged = findCache && it->second->GetParagraphStyle().direction != paraStyle.direction;
186 bool fontLocaleChanged = findCache && it->second->GetParagraphStyle().fontLocale != paraStyle.fontLocale;
187 bool useCache = findCache && !directionChanged && !fontLocaleChanged && paraStyle.maxLines == UINT32_MAX;
188 auto paragraph = useCache ?
189 it->second : Paragraph::CreateRichEditorParagraph(paraStyle, FontCollection::Current());
190
191 // caching paragraph
192 paraMapPtr_->Put(hash, paragraph);
193 useParagraphCache_ = useCache;
194 IF_TRUE(useParagraphCache_, ++cacheHitCount_);
195 CHECK_NULL_RETURN(!useCache, paragraph);
196 for (const auto& child : group) {
197 if (!child) {
198 continue;
199 }
200 child->needReLayout = true;
201 break;
202 }
203 return paragraph;
204 }
205
AppendNewLineSpan()206 void RichEditorLayoutAlgorithm::AppendNewLineSpan()
207 {
208 RefPtr<SpanItem> lastSpan = allSpans_.empty() ? nullptr : allSpans_.back();
209 bool afterNewLine = !allSpans_.empty() && lastSpan && lastSpan->content.back() == u'\n';
210 bool emptyAndNoPlaceholder = allSpans_.empty() && !needShowPlaceholder_;
211 bool needNewLineSpan = afterNewLine || emptyAndNoPlaceholder;
212 if (needNewLineSpan) {
213 std::list<RefPtr<SpanItem>> newGroup;
214 auto tailNewLineSpan = AceType::MakeRefPtr<SpanItem>();
215 tailNewLineSpan->content = u"\n";
216 tailNewLineSpan->SetNeedRemoveNewLine(true);
217 tailNewLineSpan->MarkDirty();
218 CopySpanStyle(lastSpan, tailNewLineSpan);
219 newGroup.push_back(tailNewLineSpan);
220 spans_.push_back(std::move(newGroup));
221 }
222 }
223
CopySpanStyle(RefPtr<SpanItem> source,RefPtr<SpanItem> target)224 void RichEditorLayoutAlgorithm::CopySpanStyle(RefPtr<SpanItem> source, RefPtr<SpanItem> target)
225 {
226 if (source) {
227 target->fontStyle->UpdateFontSize(source->fontStyle->GetFontSize());
228 target->textLineStyle->UpdateLineHeight(source->textLineStyle->GetLineHeight());
229 if (source->textLineStyle->HasLeadingMargin()) {
230 auto leadingMargin = source->textLineStyle->GetLeadingMarginValue();
231 leadingMargin.pixmap.Reset();
232 target->textLineStyle->UpdateLeadingMargin(leadingMargin);
233 }
234 target->textLineStyle->UpdateTextAlign(source->textLineStyle->GetTextAlign());
235 }
236 styleManager_->UpdateTextStyleByTypingStyle(target);
237 styleManager_->UpdateStyleByTypingParagraphStyle(target);
238 }
239
MeasureContentSize(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)240 std::optional<SizeF> RichEditorLayoutAlgorithm::MeasureContentSize(
241 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
242 {
243 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::MeasureContentSize");
244 auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
245 CHECK_NULL_RETURN(layoutProperty, {});
246 TextStyle textStyle;
247 ConstructTextStyles(contentConstraint, layoutWrapper, textStyle);
248 MeasureChildren(layoutWrapper, textStyle);
249 CHECK_NULL_RETURN(BuildParagraph(textStyle, layoutProperty, contentConstraint, layoutWrapper), {});
250 pManager_->SetParagraphs(GetParagraphs());
251 return SizeF(pManager_->GetMaxWidth(), pManager_->GetHeight());
252 }
253
ReMeasureContent(SizeF & textSize,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)254 LayoutConstraintF RichEditorLayoutAlgorithm::ReMeasureContent(
255 SizeF& textSize, const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
256 {
257 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::ReMeasureContent");
258 auto newContentConstraint = contentConstraint;
259 auto pattern = GetRichEditorPattern(layoutWrapper);
260 CHECK_NULL_RETURN(pattern, newContentConstraint);
261 auto layoutProperty = DynamicCast<TextLayoutProperty>(layoutWrapper->GetLayoutProperty());
262 CHECK_NULL_RETURN(layoutProperty, newContentConstraint);
263 if (pattern->GetMaxLinesHeight() != FLT_MAX && layoutProperty->GetMaxLinesValue(INT32_MAX) == INT32_MAX) {
264 newContentConstraint.maxSize.SetHeight(pattern->GetMaxLinesHeight());
265 return newContentConstraint;
266 }
267 if (pattern->GetMaxLines() == INT32_MAX || pManager_->GetHeight() <= 0.0f) {
268 UpdateConstraintByLayoutPolicy(textSize, newContentConstraint, layoutWrapper);
269 return newContentConstraint;
270 }
271 pattern->SetMaxLinesHeight(pManager_->GetHeight());
272 newContentConstraint.maxSize.SetHeight(pattern->GetMaxLinesHeight());
273 layoutProperty->UpdateMaxLines(INT32_MAX);
274 TextStyle textStyle;
275 ConstructTextStyles(newContentConstraint, layoutWrapper, textStyle);
276 layoutProperty->UpdateMaxLines(pattern->GetMaxLines());
277 CHECK_NULL_RETURN(BuildParagraph(textStyle, layoutProperty, newContentConstraint, layoutWrapper), {});
278 pManager_->SetParagraphs(GetParagraphs());
279 textSize = SizeF(pManager_->GetMaxWidth(), pManager_->GetHeight());
280 return newContentConstraint;
281 }
282
UpdateConstraintByLayoutPolicy(const SizeF & textSize,LayoutConstraintF & constraint,LayoutWrapper * layoutWrapper)283 void RichEditorLayoutAlgorithm::UpdateConstraintByLayoutPolicy(
284 const SizeF& textSize, LayoutConstraintF& constraint, LayoutWrapper* layoutWrapper)
285 {
286 CHECK_NULL_VOID(layoutWrapper);
287 auto layoutProperty = layoutWrapper->GetLayoutProperty();
288 CHECK_NULL_VOID(layoutProperty);
289 auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
290 CHECK_NULL_VOID(layoutPolicy.has_value() && layoutPolicy->IsHeightFix());
291 const auto& calcLayoutConstraint = layoutProperty->GetCalcLayoutConstraint();
292 CHECK_NULL_VOID(calcLayoutConstraint);
293 const auto& layoutConstraint = layoutProperty->GetLayoutConstraint();
294 CHECK_NULL_VOID(layoutConstraint.has_value());
295 const auto& percentReference = layoutConstraint->percentReference;
296 auto finalSize = UpdateOptionSizeByCalcLayoutConstraint(OptionalSizeF(textSize), calcLayoutConstraint,
297 percentReference);
298 if (calcLayoutConstraint->maxSize.has_value() && calcLayoutConstraint->maxSize->Height().has_value()) {
299 const auto& padding = layoutProperty->CreatePaddingAndBorder();
300 MinusPaddingToSize(padding, finalSize);
301 }
302 IF_TRUE(finalSize.Height().has_value(), constraint.maxSize.SetHeight(finalSize.Height().value()));
303 }
304
HandleTextSizeWhenEmpty(LayoutWrapper * layoutWrapper,SizeF & textSize)305 void RichEditorLayoutAlgorithm::HandleTextSizeWhenEmpty(LayoutWrapper* layoutWrapper, SizeF& textSize)
306 {
307 CHECK_NULL_VOID(layoutWrapper && allSpans_.empty());
308 auto pattern = GetRichEditorPattern(layoutWrapper);
309 CHECK_NULL_VOID(pattern);
310 float minWidth = std::ceil(pattern->GetCaretWidth());
311 textSize.SetWidth(std::max(textSize.Width(), minWidth));
312 }
313
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)314 std::optional<SizeF> RichEditorLayoutAlgorithm::MeasureContent(
315 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
316 {
317 ACE_SCOPED_TRACE("RichEditorMeasureContent");
318 pManager_->Reset();
319 SetPlaceholder(layoutWrapper);
320 auto optionalTextSize = MeasureContentSize(contentConstraint, layoutWrapper);
321 CHECK_NULL_RETURN(optionalTextSize.has_value(), {});
322 auto newContentConstraint = ReMeasureContent(optionalTextSize.value(), contentConstraint, layoutWrapper);
323 HandleTextSizeWhenEmpty(layoutWrapper, optionalTextSize.value());
324 SizeF res = optionalTextSize.value();
325 res.AddHeight(spans_.empty() ? 0 : shadowOffset_);
326 CHECK_NULL_RETURN(res.IsNonNegative(), {});
327 UpdateRichTextRect(optionalTextSize.value(), layoutWrapper);
328 auto maxHeight = newContentConstraint.selfIdealSize.Height().value_or(newContentConstraint.maxSize.Height());
329 auto contentHeight = std::min(res.Height(), maxHeight);
330 return SizeF(res.Width(), contentHeight);
331 }
332
BuildParagraph(TextStyle & textStyle,const RefPtr<TextLayoutProperty> & layoutProperty,const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)333 bool RichEditorLayoutAlgorithm::BuildParagraph(TextStyle& textStyle, const RefPtr<TextLayoutProperty>& layoutProperty,
334 const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
335 {
336 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::BuildParagraph");
337 auto maxSize = MultipleParagraphLayoutAlgorithm::GetMaxMeasureSize(contentConstraint);
338 UpdateMaxSizeByLayoutPolicy(contentConstraint, layoutWrapper, maxSize);
339 cacheHitCount_ = 0;
340 paragraphKeySet_.clear();
341 if (!CreateParagraph(textStyle, layoutProperty->GetContent().value_or(u""), layoutWrapper, maxSize.Width())) {
342 return false;
343 }
344 CHECK_NULL_RETURN(paragraphManager_, false);
345 if (paragraphKeySet_.size() != spans_.size()) {
346 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "paragraph hash collision, %{public}zu, %{public}zu",
347 paragraphKeySet_.size(), spans_.size());
348 }
349 AceScopedTrace scopedTrace("LayoutParagraph[cacheHit=%d][hitRate=%.4f]",
350 cacheHitCount_, (float) cacheHitCount_ / spans_.size());
351 auto& paragraphInfo = paragraphManager_->GetParagraphs();
352
353 if (paragraphInfo.size() != spans_.size()) {
354 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "paragraph size mismatch, %{public}zu vs. %{public}zu",
355 paragraphInfo.size(), spans_.size());
356 }
357 auto pIter = paragraphInfo.begin();
358 auto groupIter = spans_.begin();
359 while (pIter != paragraphInfo.end() && groupIter != spans_.end()) {
360 ACE_SCOPED_TRACE("LayoutOrReLayoutParagraph[range:%d,%d]", pIter->start, pIter->end);
361 auto& paragraph = pIter->paragraph;
362 CHECK_NULL_CONTINUE(paragraph);
363 std::vector<TextStyle> textStyles;
364 auto& group = *groupIter;
365 bool needReLayout = false;
366 bool needReLayoutParagraph = false;
367 ReLayoutParagraphBySpan(layoutWrapper, textStyles, group, needReLayout, needReLayoutParagraph);
368 if (!needReLayout && needReLayoutParagraph) {
369 ACE_SCOPED_TRACE("ReLayoutParagraph");
370 paragraph->ReLayout(maxSize.Width(), pIter->paragraphStyle, textStyles);
371 } else {
372 ACE_SCOPED_TRACE("LayoutParagraph");
373 paragraph->Layout(maxSize.Width());
374 }
375 ++pIter;
376 ++groupIter;
377 }
378 ReLayoutParagraphByLayoutPolicy(layoutWrapper, maxSize.Width());
379
380 if (paraMapPtr_) {
381 paraMapPtr_->SetCapacity(paragraphInfo.size());
382 paraMapPtr_->SetCapacity(SIZE_MAX);
383 }
384 return ParagraphReLayout(contentConstraint);
385 }
386
UpdateMaxSizeByLayoutPolicy(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper,SizeF & maxSize)387 void RichEditorLayoutAlgorithm::UpdateMaxSizeByLayoutPolicy(const LayoutConstraintF& contentConstraint,
388 LayoutWrapper* layoutWrapper, SizeF& maxSize)
389 {
390 CHECK_NULL_VOID(layoutWrapper);
391 auto layoutProperty = layoutWrapper->GetLayoutProperty();
392 CHECK_NULL_VOID(layoutProperty);
393 auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
394 CHECK_NULL_VOID(layoutPolicy.has_value() && layoutPolicy->IsWidthFix());
395 auto parentIdealWidth = contentConstraint.parentIdealSize.Width();
396 CHECK_NULL_VOID(parentIdealWidth.has_value() && NearEqual(maxSize.Width(), parentIdealWidth.value()));
397 maxSize.SetWidth(std::numeric_limits<float>::max());
398 }
399
ReLayoutParagraphByLayoutPolicy(LayoutWrapper * layoutWrapper,float maxWidth)400 void RichEditorLayoutAlgorithm::ReLayoutParagraphByLayoutPolicy(LayoutWrapper* layoutWrapper, float maxWidth)
401 {
402 CHECK_NULL_VOID(layoutWrapper);
403 auto layoutProperty = layoutWrapper->GetLayoutProperty();
404 CHECK_NULL_VOID(layoutProperty);
405 auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
406 CHECK_NULL_VOID(layoutPolicy.has_value() && layoutPolicy->IsWidthAdaptive());
407 CHECK_NULL_VOID(paragraphManager_);
408 auto maxParagraphWidth = paragraphManager_->GetLongestLineWithIndent();
409 CHECK_NULL_VOID(GreatNotEqual(maxWidth, maxParagraphWidth));
410 paragraphManager_->LayoutParagraphs(maxParagraphWidth);
411 }
412
ReLayoutParagraphBySpan(LayoutWrapper * layoutWrapper,std::vector<TextStyle> & textStyles,std::list<RefPtr<SpanItem>> & group,bool & needReLayout,bool & needReLayoutParagraph)413 void RichEditorLayoutAlgorithm::ReLayoutParagraphBySpan(LayoutWrapper* layoutWrapper,
414 std::vector<TextStyle>& textStyles, std::list<RefPtr<SpanItem>>& group,
415 bool& needReLayout, bool& needReLayoutParagraph)
416 {
417 auto frameNode = layoutWrapper->GetHostNode();
418 CHECK_NULL_VOID(frameNode);
419 textStyles.reserve(group.size());
420 for (const auto& child : group) {
421 if (!child) {
422 continue;
423 }
424 needReLayout |= child->needReLayout;
425 needReLayoutParagraph |= child->needReLayoutParagraph;
426 child->ResetReLayout();
427 CHECK_NULL_CONTINUE(!needReLayout);
428 child->UpdateSpanTextStyle(inheritTextStyle_, frameNode);
429 if (child->GetTextStyle().has_value()) {
430 textStyles.emplace_back(child->GetTextStyle().value());
431 } else {
432 textStyles.emplace_back(TextStyle());
433 }
434 }
435 }
436
CreateParagraph(const TextStyle & textStyle,std::u16string content,LayoutWrapper * layoutWrapper,double maxWidth)437 bool RichEditorLayoutAlgorithm::CreateParagraph(
438 const TextStyle& textStyle, std::u16string content, LayoutWrapper* layoutWrapper, double maxWidth)
439 {
440 CHECK_NULL_RETURN(!spans_.empty(), false);
441 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::CreateParagraph[groupSize:%zu]", spans_.size());
442 if (!paragraphManager_) {
443 paragraphManager_ = AceType::MakeRefPtr<ParagraphManager>();
444 }
445 paragraphManager_->Reset();
446
447 // default paragraph style
448 auto paraStyle = GetEditorParagraphStyle(textStyle, content, layoutWrapper);
449 return UpdateParagraphBySpan(layoutWrapper, paraStyle, maxWidth, textStyle);
450 }
451
GetRichEditorPattern(LayoutWrapper * layoutWrapper)452 RefPtr<RichEditorPattern> RichEditorLayoutAlgorithm::GetRichEditorPattern(LayoutWrapper* layoutWrapper)
453 {
454 CHECK_NULL_RETURN(layoutWrapper, nullptr);
455 auto host = layoutWrapper->GetHostNode();
456 CHECK_NULL_RETURN(host, nullptr);
457 return host->GetPattern<RichEditorPattern>();
458 }
459
UpdateRichTextRect(const SizeF & textSize,LayoutWrapper * layoutWrapper)460 void RichEditorLayoutAlgorithm::UpdateRichTextRect(const SizeF& textSize, LayoutWrapper* layoutWrapper)
461 {
462 auto pattern = GetRichEditorPattern(layoutWrapper);
463 CHECK_NULL_VOID(pattern);
464 IF_TRUE(!richTextRect_.has_value(), richTextRect_ = std::make_optional<RectF>());
465 richTextRect_->SetSize(pattern->IsShowPlaceholder() ? SizeF() : textSize);
466 }
467
SetPlaceholder(LayoutWrapper * layoutWrapper)468 bool RichEditorLayoutAlgorithm::SetPlaceholder(LayoutWrapper* layoutWrapper)
469 {
470 auto pattern = GetRichEditorPattern(layoutWrapper);
471 CHECK_NULL_RETURN(pattern, false);
472 return pattern->SetPlaceholder(spans_);
473 }
474
GetShadowOffset(const std::list<RefPtr<SpanItem>> & group)475 float RichEditorLayoutAlgorithm::GetShadowOffset(const std::list<RefPtr<SpanItem>>& group)
476 {
477 float shadowOffset = 0.0f;
478 for (auto& span: group) {
479 if (!span->fontStyle || !span->fontStyle->HasTextShadow()) {
480 continue;
481 }
482 auto shadows = span->fontStyle->GetTextShadowValue();
483 float upOffsetY = 0.0f;
484 float downOffsetY = 0.0f;
485 for (const auto& shadow : shadows) {
486 auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
487 auto shadowOffsetY = shadow.GetOffset().GetY();
488 if (LessOrEqual(shadowOffsetY, 0.0f) &&
489 LessNotEqual(shadowOffsetY, upOffsetY)) {
490 upOffsetY = shadowOffsetY - shadowBlurRadius;
491 } else if (GreatOrEqual(shadowOffsetY, 0.0f) &&
492 GreatNotEqual(shadowOffsetY + shadowBlurRadius, downOffsetY)) {
493 downOffsetY = shadowOffsetY + shadowBlurRadius;
494 }
495 }
496 shadowOffset = std::max(shadowOffset, downOffsetY - upOffsetY);
497 }
498 return shadowOffset;
499 }
500
Measure(LayoutWrapper * layoutWrapper)501 void RichEditorLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
502 {
503 MultipleParagraphLayoutAlgorithm::Measure(layoutWrapper);
504 const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
505 CHECK_NULL_VOID(layoutConstraint.has_value());
506 OptionalSizeF idealSize =
507 CreateIdealSize(layoutConstraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT_MAIN_AXIS);
508 if (layoutConstraint->maxSize.Width() < layoutConstraint->minSize.Width()) {
509 idealSize.SetWidth(layoutConstraint->minSize.Width());
510 }
511 auto frameSize = layoutWrapper->GetGeometryNode()->GetFrameSize();
512 frameSize.SetWidth(idealSize.ConvertToSizeT().Width());
513 UpdateFrameSizeWithLayoutPolicy(layoutWrapper, frameSize);
514 layoutWrapper->GetGeometryNode()->SetFrameSize(frameSize);
515
516 auto children = layoutWrapper->GetAllChildrenWithBuild();
517 if (children.size() != CHILDREN_SIZE) {
518 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Measure, children size error, size=%{public}zu", children.size());
519 return;
520 }
521 auto contentLayoutWrapper = children.front();
522 if (!contentLayoutWrapper) {
523 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Measure, contentLayoutWrapper is null");
524 return;
525 }
526 auto contentNode = contentLayoutWrapper->GetHostNode();
527 if (!contentNode || contentNode->GetTag() != V2::RICH_EDITOR_CONTENT_ETS_TAG) {
528 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Measure, contentNode is %{public}s",
529 contentNode ? contentNode->GetTag().c_str() : "null");
530 return;
531 }
532 contentLayoutWrapper->Measure(layoutConstraint);
533 }
534
UpdateFrameSizeWithLayoutPolicy(LayoutWrapper * layoutWrapper,SizeF & frameSize)535 void RichEditorLayoutAlgorithm::UpdateFrameSizeWithLayoutPolicy(LayoutWrapper* layoutWrapper, SizeF& frameSize)
536 {
537 CHECK_NULL_VOID(layoutWrapper);
538 auto layoutProperty = layoutWrapper->GetLayoutProperty();
539 CHECK_NULL_VOID(layoutProperty);
540 const auto& layoutConstraint = layoutProperty->GetLayoutConstraint();
541 CHECK_NULL_VOID(layoutConstraint.has_value());
542 const auto& percentReference = layoutConstraint->percentReference;
543 auto layoutPolicy = layoutProperty->GetLayoutPolicyProperty();
544 CHECK_NULL_VOID(layoutPolicy.has_value() && layoutPolicy->IsAdaptive());
545 const auto& content = layoutWrapper->GetGeometryNode()->GetContent();
546 CHECK_NULL_VOID(content);
547 auto contentSize = content->GetRect().GetSize();
548 const auto& padding = layoutProperty->CreatePaddingAndBorder();
549 AddPaddingToSize(padding, contentSize);
550 auto fixIdealSize = UpdateOptionSizeByCalcLayoutConstraint(OptionalSizeF(contentSize),
551 layoutProperty->GetCalcLayoutConstraint(), percentReference);
552 bool widthAdaptive = layoutPolicy->IsWidthAdaptive() && fixIdealSize.Width().has_value();
553 bool heightAdaptive = layoutPolicy->IsHeightAdaptive() && fixIdealSize.Height().has_value();
554 IF_TRUE(widthAdaptive, frameSize.SetWidth(fixIdealSize.Width().value()));
555 IF_TRUE(heightAdaptive, frameSize.SetHeight(fixIdealSize.Height().value()));
556 }
557
Layout(LayoutWrapper * layoutWrapper)558 void RichEditorLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
559 {
560 ACE_SCOPED_TRACE("RichEditorLayoutAlgorithm::Layout");
561 auto context = layoutWrapper->GetHostNode()->GetContext();
562 CHECK_NULL_VOID(context);
563 parentGlobalOffset_ = layoutWrapper->GetHostNode()->GetPaintRectOffsetNG() - context->GetRootRect().GetOffset();
564 MultipleParagraphLayoutAlgorithm::Layout(layoutWrapper);
565
566 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
567 if (children.size() != CHILDREN_SIZE) {
568 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Layout, children size error, size=%{public}zu", children.size());
569 return;
570 }
571 auto contentLayoutWrapper = children.front();
572 if (!contentLayoutWrapper) {
573 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Measure, contentLayoutWrapper is null");
574 return;
575 }
576 auto contentNode = contentLayoutWrapper->GetHostNode();
577 if (!contentNode || contentNode->GetTag() != V2::RICH_EDITOR_CONTENT_ETS_TAG) {
578 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "Layout, contentNode is %{public}s",
579 contentNode ? contentNode->GetTag().c_str() : "null");
580 return;
581 }
582 contentLayoutWrapper->SetActive(true);
583 contentLayoutWrapper->Layout();
584 }
585
GetAllChildrenWithBuild(LayoutWrapper * layoutWrapper)586 ChildrenListWithGuard RichEditorLayoutAlgorithm::GetAllChildrenWithBuild(LayoutWrapper* layoutWrapper)
587 {
588 const auto& children = layoutWrapper->GetAllChildrenWithBuild();
589 if (children.size() != CHILDREN_SIZE) {
590 TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "GetAllChildrenWithBuild, children size=%{public}zu", children.size());
591 return children;
592 }
593 auto& contentLayoutWrapper = *(children.begin());
594 CHECK_NULL_RETURN(contentLayoutWrapper, children);
595 return contentLayoutWrapper->GetAllChildrenWithBuild();
596 }
597
GetContentOffset(LayoutWrapper * layoutWrapper)598 OffsetF RichEditorLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
599 {
600 auto contentOffset = SetContentOffset(layoutWrapper);
601 auto host = layoutWrapper->GetHostNode();
602 CHECK_NULL_RETURN(host, contentOffset);
603 auto pattern = host->GetPattern<RichEditorPattern>();
604 CHECK_NULL_RETURN(pattern, contentOffset);
605 IF_TRUE(!richTextRect_.has_value(), richTextRect_ = std::make_optional<RectF>());
606 richTextRect_->SetOffset(OffsetF(contentOffset.GetX(), pattern->GetTextRect().GetY()));
607 return richTextRect_->GetOffset();
608 }
609
GetEditorParagraphStyle(const TextStyle & textStyle,const std::u16string & content,LayoutWrapper * layoutWrapper) const610 ParagraphStyle RichEditorLayoutAlgorithm::GetEditorParagraphStyle(
611 const TextStyle& textStyle, const std::u16string& content, LayoutWrapper* layoutWrapper) const
612 {
613 auto style = ParagraphUtil::GetParagraphStyle(textStyle);
614 style.fontSize = textStyle.GetFontSize().ConvertToPx();
615 style.maxLines = textStyle.GetMaxLines();
616 if (!pManager_->minParagraphFontSize.has_value() ||
617 GreatNotEqual(pManager_->minParagraphFontSize.value(), style.fontSize)) {
618 pManager_->minParagraphFontSize = style.fontSize;
619 }
620
621 return style;
622 }
623
HandleEmptyParagraph(RefPtr<Paragraph> paragraph,const std::list<RefPtr<SpanItem>> & spanGroup)624 void RichEditorLayoutAlgorithm::HandleEmptyParagraph(RefPtr<Paragraph> paragraph,
625 const std::list<RefPtr<SpanItem>>& spanGroup)
626 {
627 CHECK_NULL_VOID(paragraph && spanGroup.size() == 1);
628 auto spanItem = spanGroup.front();
629 CHECK_NULL_VOID(spanItem);
630 auto content = spanItem->GetSpanContent(spanItem->GetSpanContent());
631 CHECK_NULL_VOID(content.empty());
632 auto textStyle = spanItem->GetTextStyle();
633 CHECK_NULL_VOID(textStyle.has_value());
634 paragraph->PushStyle(textStyle.value());
635 }
636
GetParagraphStyleSpanItem(const std::list<RefPtr<SpanItem>> & spanGroup)637 RefPtr<SpanItem> RichEditorLayoutAlgorithm::GetParagraphStyleSpanItem(const std::list<RefPtr<SpanItem>>& spanGroup)
638 {
639 CHECK_NULL_RETURN(!spanGroup.empty(), nullptr);
640 auto it = spanGroup.begin();
641 while (it != spanGroup.end()) {
642 if (!AceType::DynamicCast<PlaceholderSpanItem>(*it)) {
643 return *it;
644 }
645 ++it;
646 }
647 return *spanGroup.begin();
648 }
649
SpansToString()650 std::string RichEditorLayoutAlgorithm::SpansToString()
651 {
652 std::stringstream ss;
653 for (const auto& list : spans_) {
654 ss << "[";
655 for_each(list.begin(), list.end(), [&ss](const RefPtr<SpanItem>& item) {
656 #ifndef IS_RELEASE_VERSION
657 ss << "(" << StringUtils::RestoreEscape(UtfUtils::Str16DebugToStr8(item->content)) << ")";
658 #endif
659 ss << "[" << item->rangeStart << ":" << item->position << "],";
660 });
661 ss << "], ";
662 }
663 return ss.str();
664 }
665
AddTextSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)666 void RichEditorLayoutAlgorithm::AddTextSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
667 const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
668 {
669 if (!useParagraphCache_) {
670 MultipleParagraphLayoutAlgorithm::AddTextSpanToParagraph(child, spanTextLength, frameNode, paragraph);
671 return;
672 }
673 spanTextLength += static_cast<int32_t>(child->content.length());
674 }
675
AddImageToParagraph(RefPtr<ImageSpanItem> & child,const RefPtr<LayoutWrapper> & iterItem,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength)676 void RichEditorLayoutAlgorithm::AddImageToParagraph(RefPtr<ImageSpanItem>& child, const RefPtr<LayoutWrapper>& iterItem,
677 const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength)
678 {
679 if (!useParagraphCache_) {
680 MultipleParagraphLayoutAlgorithm::AddImageToParagraph(child, iterItem, paragraph, spanTextLength);
681 return;
682 }
683 spanTextLength += static_cast<int32_t>(child->content.length());
684 child->placeholderIndex = currentParagraphPlaceholderCount_++;
685 child->placeholderIndex += preParagraphsPlaceholderCount_;
686 }
687
AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem> & child,const RefPtr<LayoutWrapper> & layoutWrapper,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength)688 void RichEditorLayoutAlgorithm::AddPlaceHolderToParagraph(RefPtr<PlaceholderSpanItem>& child,
689 const RefPtr<LayoutWrapper>& layoutWrapper, const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength)
690 {
691 if (!useParagraphCache_) {
692 MultipleParagraphLayoutAlgorithm::AddPlaceHolderToParagraph(child, layoutWrapper, paragraph, spanTextLength);
693 return;
694 }
695 spanTextLength += static_cast<int32_t>(child->content.length());
696 child->placeholderIndex = currentParagraphPlaceholderCount_++;
697 child->placeholderIndex += preParagraphsPlaceholderCount_;
698 }
699
UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem> & child,const RefPtr<Paragraph> & paragraph,int32_t & spanTextLength,CustomSpanPlaceholderInfo & customSpanPlaceholder)700 void RichEditorLayoutAlgorithm::UpdateParagraphByCustomSpan(RefPtr<CustomSpanItem>& child,
701 const RefPtr<Paragraph>& paragraph, int32_t& spanTextLength, CustomSpanPlaceholderInfo& customSpanPlaceholder)
702 {
703 if (!useParagraphCache_) {
704 MultipleParagraphLayoutAlgorithm::UpdateParagraphByCustomSpan(
705 child, paragraph, spanTextLength, customSpanPlaceholder);
706 return;
707 }
708 spanTextLength += static_cast<int32_t>(child->content.length());
709 child->placeholderIndex = currentParagraphPlaceholderCount_++;
710 child->placeholderIndex += preParagraphsPlaceholderCount_;
711 if (child->onDraw.has_value()) {
712 customSpanPlaceholder.onDraw = child->onDraw.value();
713 }
714 customSpanPlaceholder.customSpanIndex = child->placeholderIndex;
715 }
716
AddSymbolSpanToParagraph(const RefPtr<SpanItem> & child,int32_t & spanTextLength,const RefPtr<FrameNode> & frameNode,const RefPtr<Paragraph> & paragraph)717 void RichEditorLayoutAlgorithm::AddSymbolSpanToParagraph(const RefPtr<SpanItem>& child, int32_t& spanTextLength,
718 const RefPtr<FrameNode>& frameNode, const RefPtr<Paragraph>& paragraph)
719 {
720 if (!useParagraphCache_) {
721 MultipleParagraphLayoutAlgorithm::AddSymbolSpanToParagraph(child, spanTextLength, frameNode, paragraph);
722 return;
723 }
724 spanTextLength += static_cast<int32_t>(child->content.length());
725 }
726 } // namespace OHOS::Ace::NG
727