• 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/text/text_layout_algorithm.h"
22 #include "core/components_ng/render/paragraph.h"
23 
24 namespace OHOS::Ace::NG {
RichEditorLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans,ParagraphManager * paragraphs)25 RichEditorLayoutAlgorithm::RichEditorLayoutAlgorithm(std::list<RefPtr<SpanItem>> spans, ParagraphManager* paragraphs)
26     : pManager_(paragraphs)
27 {
28     // split spans into groups by \newline
29     auto it = spans.begin();
30     while (it != spans.end()) {
31         auto span = *it;
32         // only checking the last char
33         if (StringUtils::ToWstring(span->content).back() == L'\n') {
34             if (std::next(it) != spans.end()) {
35                 span->MarkNeedRemoveNewLine(true);
36             } else {
37                 span->MarkNeedRemoveNewLine(false);
38             }
39             std::list<RefPtr<SpanItem>> newGroup;
40             newGroup.splice(newGroup.begin(), spans, spans.begin(), std::next(it));
41             spans_.push_back(std::move(newGroup));
42 
43             it = spans.begin();
44             continue;
45         }
46         span->MarkNeedRemoveNewLine(false);
47         // clear placeholder textstyle,it should be modified by text line
48         auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(span);
49         if (placeholderSpanItem) {
50             TextStyle textStyle;
51             placeholderSpanItem->textStyle = textStyle;
52         }
53         ++it;
54     }
55     if (!spans.empty()) {
56         spans_.push_back(std::move(spans));
57     }
58     TAG_LOGD(AceLogTag::ACE_RICH_TEXT, "spans=%{public}s", SpansToString().c_str());
59 }
60 
MeasureContent(const LayoutConstraintF & contentConstraint,LayoutWrapper * layoutWrapper)61 std::optional<SizeF> RichEditorLayoutAlgorithm::MeasureContent(
62     const LayoutConstraintF& contentConstraint, LayoutWrapper* layoutWrapper)
63 {
64     pManager_->Reset();
65     if (spans_.empty()) {
66         auto pipeline = PipelineContext::GetCurrentContext();
67         CHECK_NULL_RETURN(pipeline, std::nullopt);
68         auto richEditorTheme = pipeline->GetTheme<RichEditorTheme>();
69         CHECK_NULL_RETURN(richEditorTheme, std::nullopt);
70         auto defaultCaretHeight = richEditorTheme->GetDefaultCaretHeight().ConvertToPx();
71         auto width = contentConstraint.selfIdealSize.Width().value_or(contentConstraint.maxSize.Width());
72         return SizeF(width, defaultCaretHeight);
73     }
74 
75     SizeF res;
76     int32_t lastPlaceholderIndex = 0;
77     float textHeight = 0.0f;
78     for (auto&& group : spans_) {
79         // layout each paragraph
80         SetSpans(group);
81         SetParagraph(nullptr);
82         auto size = TextLayoutAlgorithm::MeasureContent(contentConstraint, layoutWrapper);
83         if (size) {
84             res.SetWidth(std::max(size->Width(), res.Width()));
85             res.AddHeight(size->Height());
86         }
87 
88         // record generated paragraph
89         auto&& paragraph = GetParagraph();
90         if (!paragraph) {
91             continue;
92         }
93         float shadowOffset = GetShadowOffset(group);
94         res.AddHeight(shadowOffset);
95         textHeight += paragraph->GetHeight();
96         auto firstSpan = *group.begin();
97         auto lastSpan = *group.rbegin();
98         if (!firstSpan || !lastSpan) {
99             TAG_LOGE(AceLogTag::ACE_RICH_TEXT, "MeasureContent failed. firstSpan or lastSpan is null.");
100             continue;
101         }
102         pManager_->AddParagraph({ .paragraph = paragraph,
103             .start = firstSpan->position - StringUtils::ToWstring(firstSpan->content).length(),
104             .end = lastSpan->position });
105         std::for_each(group.begin(), group.end(), [&](RefPtr<SpanItem>& item) {
106             auto imageSpanItem = AceType::DynamicCast<ImageSpanItem>(item);
107             if (imageSpanItem && imageSpanItem->placeholderIndex >= 0) {
108                 imageSpanItem->placeholderIndex = lastPlaceholderIndex;
109                 lastPlaceholderIndex++;
110             } else if (auto placeholderSpanItem = AceType::DynamicCast<PlaceholderSpanItem>(item);
111                        placeholderSpanItem) {
112                 placeholderSpanItem->placeholderIndex = lastPlaceholderIndex;
113                 lastPlaceholderIndex++;
114             }
115         });
116     }
117     if (!res.IsPositive()) {
118         return std::nullopt;
119     }
120     richTextRect_.SetSize(SizeF(res.Width(), textHeight));
121     auto contentHeight = res.Height();
122     if (contentConstraint.selfIdealSize.Height().has_value()) {
123         contentHeight = std::min(contentHeight, contentConstraint.selfIdealSize.Height().value());
124     } else {
125         contentHeight = std::min(contentHeight, contentConstraint.maxSize.Height());
126     }
127     return SizeF(res.Width(), contentHeight);
128 }
129 
GetShadowOffset(const std::list<RefPtr<SpanItem>> & group)130 float RichEditorLayoutAlgorithm::GetShadowOffset(const std::list<RefPtr<SpanItem>>& group)
131 {
132     float shadowOffset = 0.0f;
133     for (auto& span: group) {
134         if (!span->fontStyle || !span->fontStyle->HasTextShadow()) {
135             continue;
136         }
137         auto shadows = span->fontStyle->GetTextShadowValue();
138         float upOffsetY = 0.0f;
139         float downOffsetY = 0.0f;
140         for (const auto& shadow : shadows) {
141             auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
142             auto shadowOffsetY = shadow.GetOffset().GetY();
143             if (LessOrEqual(shadowOffsetY, 0.0f) &&
144                 LessNotEqual(shadowOffsetY, upOffsetY)) {
145                 upOffsetY = shadowOffsetY - shadowBlurRadius;
146             } else if (GreatOrEqual(shadowOffsetY, 0.0f) &&
147                 GreatNotEqual(shadowOffsetY + shadowBlurRadius, downOffsetY)) {
148                 downOffsetY = shadowOffsetY + shadowBlurRadius;
149             }
150         }
151         shadowOffset = std::max(shadowOffset, downOffsetY - upOffsetY);
152     }
153     return shadowOffset;
154 }
155 
Measure(LayoutWrapper * layoutWrapper)156 void RichEditorLayoutAlgorithm::Measure(LayoutWrapper* layoutWrapper)
157 {
158     const auto& layoutConstraint = layoutWrapper->GetLayoutProperty()->GetLayoutConstraint();
159     OptionalSizeF frameSize =
160         CreateIdealSize(layoutConstraint.value(), Axis::HORIZONTAL, MeasureType::MATCH_PARENT_MAIN_AXIS);
161     if (layoutConstraint->maxSize.Width() < layoutConstraint->minSize.Width()) {
162         frameSize.SetWidth(layoutConstraint->minSize.Width());
163     }
164     TextLayoutAlgorithm::Measure(layoutWrapper);
165 }
166 
Layout(LayoutWrapper * layoutWrapper)167 void RichEditorLayoutAlgorithm::Layout(LayoutWrapper* layoutWrapper)
168 {
169     auto context = layoutWrapper->GetHostNode()->GetContext();
170     CHECK_NULL_VOID(context);
171     parentGlobalOffset_ = layoutWrapper->GetHostNode()->GetPaintRectOffset() - context->GetRootRect().GetOffset();
172 
173     // merge spans
174     std::list<RefPtr<SpanItem>> allSpans;
175     for (auto&& group : spans_) {
176         allSpans.splice(allSpans.end(), group);
177     }
178     SetSpans(allSpans);
179     TextLayoutAlgorithm::Layout(layoutWrapper);
180 }
181 
GetContentOffset(LayoutWrapper * layoutWrapper)182 OffsetF RichEditorLayoutAlgorithm::GetContentOffset(LayoutWrapper* layoutWrapper)
183 {
184     auto contentOffset = TextLayoutAlgorithm::GetContentOffset(layoutWrapper);
185     auto host = layoutWrapper->GetHostNode();
186     CHECK_NULL_RETURN(host, contentOffset);
187     auto pattern = host->GetPattern<RichEditorPattern>();
188     CHECK_NULL_RETURN(pattern, contentOffset);
189     richTextRect_.SetOffset(OffsetF(contentOffset.GetX(), pattern->GetTextRect().GetY()));
190     return richTextRect_.GetOffset();
191 }
192 
GetPlaceholderRects(std::vector<RectF> & rects)193 void RichEditorLayoutAlgorithm::GetPlaceholderRects(std::vector<RectF>& rects)
194 {
195     rects = pManager_->GetPlaceholderRects();
196 }
197 
GetParagraphStyle(const TextStyle & textStyle,const std::string & content,LayoutWrapper * layoutWrapper) const198 ParagraphStyle RichEditorLayoutAlgorithm::GetParagraphStyle(
199     const TextStyle& textStyle, const std::string& content, LayoutWrapper* layoutWrapper) const
200 {
201     auto style = TextLayoutAlgorithm::GetParagraphStyle(textStyle, content, layoutWrapper);
202     style.fontSize = textStyle.GetFontSize().ConvertToPx();
203     if (!pManager_->minParagraphFontSize.has_value() ||
204         GreatNotEqual(pManager_->minParagraphFontSize.value(), style.fontSize)) {
205         pManager_->minParagraphFontSize = style.fontSize;
206     }
207     const auto& spanItem = GetFirstTextSpanItem();
208     CHECK_NULL_RETURN(spanItem, style);
209     auto& lineStyle = spanItem->textLineStyle;
210     CHECK_NULL_RETURN(lineStyle, style);
211     if (lineStyle->propTextAlign) {
212         style.align = *(lineStyle->propTextAlign);
213     }
214     if (lineStyle->propTextIndent) {
215         style.leadingMargin = std::make_optional<LeadingMargin>();
216         style.leadingMargin->size = SizeF(lineStyle->propTextIndent->ConvertToPx(), 0.0f);
217     }
218     if (lineStyle->propLeadingMargin) {
219         if (!style.leadingMargin) {
220             style.leadingMargin = std::make_optional<LeadingMargin>();
221         }
222         style.leadingMargin->size = lineStyle->propLeadingMargin->size;
223         style.leadingMargin->pixmap = lineStyle->propLeadingMargin->pixmap;
224     }
225 
226     return style;
227 }
228 
GetFirstTextSpanItem() const229 RefPtr<SpanItem> RichEditorLayoutAlgorithm::GetFirstTextSpanItem() const
230 {
231     auto& spanGroup = GetSpans();
232     auto it = spanGroup.begin();
233     while (it != spanGroup.end()) {
234         if (!DynamicCast<PlaceholderSpanItem>(*it)) {
235             return *it;
236         }
237         ++it;
238     }
239     return *spanGroup.begin();
240 }
241 
GetPreviousLength() const242 int32_t RichEditorLayoutAlgorithm::GetPreviousLength() const
243 {
244     auto&& paragraphs = pManager_->GetParagraphs();
245     if (paragraphs.empty()) {
246         return 0;
247     }
248     return paragraphs.back().end;
249 }
250 } // namespace OHOS::Ace::NG
251