• 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/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