• 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/paragraph_manager.h"
17 
18 #include <iterator>
19 #include <ostream>
20 
21 #include "base/utils/utils.h"
22 #include "core/components/common/properties/text_layout_info.h"
23 
24 namespace OHOS::Ace::NG {
GetHeight() const25 float ParagraphManager::GetHeight() const
26 {
27     float res = 0.0f;
28     for (auto&& info : paragraphs_) {
29         res += info.paragraph->GetHeight();
30     }
31     return res;
32 }
33 
GetMaxIntrinsicWidth() const34 float ParagraphManager::GetMaxIntrinsicWidth() const
35 {
36     float res = 0.0f;
37     for (auto &&info : paragraphs_) {
38         res = std::max(res, info.paragraph->GetMaxIntrinsicWidth());
39     }
40     return res;
41 }
DidExceedMaxLines() const42 bool ParagraphManager::DidExceedMaxLines() const
43 {
44     bool res = false;
45     for (auto &&info : paragraphs_) {
46         res |= info.paragraph->DidExceedMaxLines();
47     }
48     return res;
49 }
GetLongestLine() const50 float ParagraphManager::GetLongestLine() const
51 {
52     float res = 0.0f;
53     for (auto &&info : paragraphs_) {
54         res = std::max(res, info.paragraph->GetLongestLine());
55     }
56     return res;
57 }
GetMaxWidth() const58 float ParagraphManager::GetMaxWidth() const
59 {
60     float res = 0.0f;
61     for (auto &&info : paragraphs_) {
62         res = std::max(res, info.paragraph->GetMaxWidth());
63     }
64     return res;
65 }
GetTextWidth() const66 float ParagraphManager::GetTextWidth() const
67 {
68     float res = 0.0f;
69     for (auto &&info : paragraphs_) {
70         res = std::max(res, info.paragraph->GetTextWidth());
71     }
72     return res;
73 }
74 
GetTextWidthIncludeIndent() const75 float ParagraphManager::GetTextWidthIncludeIndent() const
76 {
77     float res = 0.0f;
78     for (auto &&info : paragraphs_) {
79         auto paragraph = info.paragraph;
80         CHECK_NULL_RETURN(paragraph, 0.0f);
81         auto width = paragraph->GetTextWidth();
82         res = std::max(res, width);
83     }
84     return res;
85 }
86 
GetLongestLineWithIndent() const87 float ParagraphManager::GetLongestLineWithIndent() const
88 {
89     float res = 0.0f;
90     for (auto &&info : paragraphs_) {
91         auto paragraph = info.paragraph;
92         CHECK_NULL_RETURN(paragraph, 0.0f);
93         auto width = paragraph->GetLongestLineWithIndent();
94         res = std::max(res, width);
95     }
96     return res;
97 }
98 
GetLineCount() const99 size_t ParagraphManager::GetLineCount() const
100 {
101     size_t count = 0;
102     for (auto &&info : paragraphs_) {
103         count += info.paragraph->GetLineCount();
104     }
105     return count;
106 }
107 
GetIndex(Offset offset,bool clamp) const108 int32_t ParagraphManager::GetIndex(Offset offset, bool clamp) const
109 {
110     CHECK_NULL_RETURN(!paragraphs_.empty(), 0);
111     if (clamp && LessNotEqual(offset.GetY(), 0.0)) {
112         return 0;
113     }
114     int idx = 0;
115     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it, ++idx) {
116         auto&& info = *it;
117         if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight()) ||
118             (!clamp && idx == static_cast<int>(paragraphs_.size()) - 1)) {
119             return info.paragraph->GetGlyphIndexByCoordinate(offset) + info.start;
120         }
121         // get offset relative to each paragraph
122         offset.SetY(offset.GetY() - info.paragraph->GetHeight());
123     }
124     return paragraphs_.back().end;
125 }
126 
GetGlyphPositionAtCoordinate(Offset offset)127 PositionWithAffinity ParagraphManager::GetGlyphPositionAtCoordinate(Offset offset)
128 {
129     TAG_LOGI(AceLogTag::ACE_TEXT,
130         "Get Glyph Position, coordinate = [%{public}.2f %{public}.2f]", offset.GetX(), offset.GetY());
131     PositionWithAffinity finalResult(0, TextAffinity::UPSTREAM);
132     CHECK_NULL_RETURN(!paragraphs_.empty(), finalResult);
133     if (LessNotEqual(offset.GetY(), 0.0)) {
134         return finalResult;
135     }
136     int idx = 0;
137     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it, ++idx) {
138         auto& info = *it;
139         if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight()) ||
140             (idx == static_cast<int>(paragraphs_.size()) - 1)) {
141             auto result = info.paragraph->GetGlyphPositionAtCoordinate(offset);
142             finalResult.position_ = result.position_ + static_cast<size_t>(info.start);
143             TAG_LOGI(AceLogTag::ACE_TEXT,
144                 "Current paragraph, originPos = %{public}zu, finalPos =%{public}zu and affinity = %{public}d",
145                 result.position_, finalResult.position_, result.affinity_);
146             finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
147             return finalResult;
148         }
149         // get offset relative to each paragraph
150         offset.SetY(offset.GetY() - info.paragraph->GetHeight());
151     }
152     auto info = paragraphs_.back();
153     auto result = info.paragraph->GetGlyphPositionAtCoordinate(offset);
154     finalResult.position_ = static_cast<size_t>(info.end);
155     finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
156     TAG_LOGI(AceLogTag::ACE_TEXT,
157         "Current paragraph, final position = %{public}zu and affinity = %{public}d", finalResult.position_,
158         finalResult.affinity_);
159     return finalResult;
160 }
161 
GetGlyphIndexByCoordinate(Offset offset,bool isSelectionPos) const162 int32_t ParagraphManager::GetGlyphIndexByCoordinate(Offset offset, bool isSelectionPos) const
163 {
164     CHECK_NULL_RETURN(!paragraphs_.empty(), 0);
165     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
166         auto &&info = *it;
167         if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight())) {
168             return info.paragraph->GetGlyphIndexByCoordinate(offset, isSelectionPos) + info.start;
169         }
170         // get offset relative to each paragraph
171         offset.SetY(offset.GetY() - info.paragraph->GetHeight());
172     }
173     return paragraphs_.back().end;
174 }
175 
GetWordBoundary(int32_t offset,int32_t & start,int32_t & end) const176 bool ParagraphManager::GetWordBoundary(int32_t offset, int32_t& start, int32_t& end) const
177 {
178     CHECK_NULL_RETURN(!paragraphs_.empty(), false);
179     auto offsetIndex = offset;
180     auto startIndex = 0;
181     auto endIndex = 0;
182     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
183         auto &&info = *it;
184         if (LessNotEqual(offset, info.end)) {
185             auto flag = info.paragraph->GetWordBoundary(offsetIndex, start, end);
186             start += startIndex;
187             end += endIndex;
188             return flag;
189         }
190         // get offset relative to each paragraph
191         offsetIndex = offset - info.end;
192         startIndex = info.end;
193         endIndex = info.end;
194     }
195     return false;
196 }
197 
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity) const198 bool ParagraphManager::CalcCaretMetricsByPosition(
199     int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity) const
200 {
201     CHECK_NULL_RETURN(!paragraphs_.empty(), false);
202     auto offsetIndex = extent;
203     auto offsetY = 0.0f;
204     auto result = false;
205     for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
206         auto &&info = *it;
207         if (textAffinity == TextAffinity::UPSTREAM || std::next(it) == paragraphs_.end()) {
208             if (LessOrEqual(extent, info.end)) {
209                 result = info.paragraph->CalcCaretMetricsByPosition(offsetIndex, caretCaretMetric, textAffinity);
210                 break;
211             }
212         } else {
213             if (LessNotEqual(extent, info.end)) {
214                 result = info.paragraph->CalcCaretMetricsByPosition(offsetIndex, caretCaretMetric, textAffinity);
215                 break;
216             }
217         }
218         // get offset relative to each paragraph
219         offsetIndex = extent - info.end;
220         offsetY += info.paragraph->GetHeight();
221     }
222     caretCaretMetric.offset += OffsetF(0.0f, offsetY);
223     return result;
224 }
225 
GetLineMetricsByRectF(RectF rect,int32_t paragraphIndex) const226 LineMetrics ParagraphManager::GetLineMetricsByRectF(RectF rect, int32_t paragraphIndex) const
227 {
228     auto index = 0;
229     float height = 0;
230     auto iter = paragraphs_.begin();
231     while (index < paragraphIndex) {
232         auto paragraphInfo = *iter;
233         height += paragraphInfo.paragraph->GetHeight();
234         iter++;
235         index++;
236     }
237     auto paragraphInfo = *iter;
238     rect.SetTop(rect.GetY() - height);
239     auto lineMetrics = paragraphInfo.paragraph->GetLineMetricsByRectF(rect);
240     lineMetrics.y += height;
241     return lineMetrics;
242 }
243 
GetLineMetrics(size_t lineNumber)244 TextLineMetrics ParagraphManager::GetLineMetrics(size_t lineNumber)
245 {
246     if (GetLineCount() == 0 || lineNumber > GetLineCount() - 1) {
247         TAG_LOGE(AceLogTag::ACE_TEXT,
248             "GetLineMetrics failed, lineNumber is greater than max lines:%{public}zu", lineNumber);
249         return TextLineMetrics();
250     }
251     size_t endIndex = 0;
252     double paragraphsHeight = 0.0;
253     size_t lineNumberParam = lineNumber;
254     for (auto &&info : paragraphs_) {
255         auto lineCount = info.paragraph->GetLineCount();
256         if (lineCount > 0 && lineNumber > lineCount - 1) {
257             lineNumber -= lineCount;
258             paragraphsHeight += info.paragraph->GetHeight();
259             auto lastLineMetrics = info.paragraph->GetLineMetrics(lineCount - 1);
260             endIndex += lastLineMetrics.endIndex + 1;
261             continue;
262         }
263         auto lineMetrics = info.paragraph->GetLineMetrics(lineNumber);
264         lineMetrics.startIndex += endIndex;
265         lineMetrics.endIndex += endIndex;
266         lineMetrics.lineNumber = lineNumberParam;
267         lineMetrics.y += paragraphsHeight;
268         lineMetrics.baseline += paragraphsHeight;
269         return lineMetrics;
270     }
271     return TextLineMetrics();
272 }
273 
GetRects(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const274 std::vector<RectF> ParagraphManager::GetRects(int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
275 {
276     std::vector<RectF> res;
277     float y = 0.0f;
278     for (auto&& info : paragraphs_) {
279         std::vector<RectF> rects;
280         if (info.start > end) {
281             break;
282         }
283         if (info.end > start) {
284             auto relativeStart = (start < info.start) ? 0 : start - info.start;
285             if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
286                 info.paragraph->GetTightRectsForRange(relativeStart, end - info.start, rects);
287             } else {
288                 info.paragraph->GetRectsForRange(relativeStart, end - info.start, rects);
289             }
290 
291             for (auto&& rect : rects) {
292                 rect.SetTop(rect.Top() + y);
293             }
294             res.insert(res.end(), rects.begin(), rects.end());
295         }
296         y += info.paragraph->GetHeight();
297     }
298     return res;
299 }
300 
GetParagraphsRects(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const301 std::vector<std::pair<std::vector<RectF>, TextDirection>> ParagraphManager::GetParagraphsRects(
302     int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
303 {
304     std::vector<std::pair<std::vector<RectF>, TextDirection>> paragraphsRects;
305     float y = 0.0f;
306     for (auto&& info : paragraphs_) {
307         if (info.start > end) {
308             break;
309         }
310         if (info.end > start) {
311             std::vector<RectF> rects;
312             auto relativeStart = (start < info.start) ? 0 : start - info.start;
313             if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
314                 info.paragraph->GetTightRectsForRange(relativeStart, end - info.start, rects);
315             } else {
316                 info.paragraph->GetRectsForRange(relativeStart, end - info.start, rects);
317             }
318             std::pair<std::vector<RectF>, TextDirection> paragraphRects;
319             for (auto&& rect : rects) {
320                 rect.SetTop(rect.Top() + y);
321             }
322             paragraphRects.first = rects;
323             paragraphRects.second = info.paragraphStyle.direction;
324             paragraphsRects.emplace_back(paragraphRects);
325         }
326         y += info.paragraph->GetHeight();
327     }
328     return paragraphsRects;
329 }
330 
IsSelectLineHeadAndUseLeadingMargin(int32_t start) const331 bool ParagraphManager::IsSelectLineHeadAndUseLeadingMargin(int32_t start) const
332 {
333     for (auto iter = paragraphs_.begin(); iter != paragraphs_.end(); iter++) {
334         auto curParagraph = *iter;
335         if (curParagraph.paragraph && curParagraph.paragraph->GetParagraphStyle().leadingMargin &&
336             curParagraph.start == start) {
337             return true;
338         }
339         auto next = std::next(iter);
340         if (next != paragraphs_.end()) {
341             auto nextParagraph = *next;
342             if (nextParagraph.paragraph && nextParagraph.paragraph->GetParagraphStyle().leadingMargin &&
343                 nextParagraph.start == start + 1) {
344                 return true;
345             }
346         }
347     }
348     return false;
349 }
350 
GetPlaceholderRects() const351 std::vector<RectF> ParagraphManager::GetPlaceholderRects() const
352 {
353     std::vector<RectF> res;
354     float y = 0.0f;
355     for (auto&& info : paragraphs_) {
356         std::vector<RectF> rects;
357         info.paragraph->GetRectsForPlaceholders(rects);
358         for (auto& rect : rects) {
359             rect.SetTop(rect.Top() + y);
360         }
361         y += info.paragraph->GetHeight();
362 
363         res.insert(res.end(), rects.begin(), rects.end());
364     }
365     return res;
366 }
367 
ComputeCursorOffset(int32_t index,float & selectLineHeight,bool downStreamFirst,bool needLineHighest) const368 OffsetF ParagraphManager::ComputeCursorOffset(
369     int32_t index, float& selectLineHeight, bool downStreamFirst, bool needLineHighest) const
370 {
371     CHECK_NULL_RETURN(!paragraphs_.empty(), {});
372     auto it = paragraphs_.begin();
373     float y = 0.0f;
374     while (it != paragraphs_.end()) {
375         if (index >= it->start && index < it->end) {
376             break;
377         }
378         y += it->paragraph->GetHeight();
379         ++it;
380     }
381 
382     if (index == paragraphs_.back().end) {
383         --it;
384         y -= it->paragraph->GetHeight();
385     }
386 
387     CHECK_NULL_RETURN(it != paragraphs_.end(), OffsetF(0.0f, y));
388 
389     int32_t relativeIndex = index - it->start;
390     auto&& paragraph = it->paragraph;
391     CaretMetricsF metrics;
392     auto computeSuccess = false;
393     if (downStreamFirst) {
394         computeSuccess = paragraph->ComputeOffsetForCaretDownstream(relativeIndex, metrics, needLineHighest) ||
395                          paragraph->ComputeOffsetForCaretUpstream(relativeIndex, metrics, needLineHighest);
396     } else {
397         computeSuccess = paragraph->ComputeOffsetForCaretUpstream(relativeIndex, metrics, needLineHighest) ||
398                          paragraph->ComputeOffsetForCaretDownstream(relativeIndex, metrics, needLineHighest);
399     }
400     CHECK_NULL_RETURN(computeSuccess, OffsetF(0.0f, y));
401     selectLineHeight = metrics.height;
402     return { static_cast<float>(metrics.offset.GetX()), static_cast<float>(metrics.offset.GetY() + y) };
403 }
404 
ComputeCursorInfoByClick(int32_t index,float & selectLineHeight,const OffsetF & lastTouchOffset) const405 OffsetF ParagraphManager::ComputeCursorInfoByClick(
406     int32_t index, float& selectLineHeight, const OffsetF& lastTouchOffset) const
407 {
408     CHECK_NULL_RETURN(!paragraphs_.empty(), {});
409     auto it = paragraphs_.begin();
410     float y = 0.0f;
411     while (it != paragraphs_.end()) {
412         if (index >= it->start && index < it->end) {
413             break;
414         }
415         y += it->paragraph->GetHeight();
416         ++it;
417     }
418 
419     if (index == paragraphs_.back().end) {
420         --it;
421         y -= it->paragraph->GetHeight();
422     }
423 
424     CHECK_NULL_RETURN(it != paragraphs_.end(), OffsetF(0.0f, y));
425 
426     int32_t relativeIndex = index - it->start;
427     auto&& paragraph = it->paragraph;
428 
429     CaretMetricsF caretCaretMetric;
430     auto touchOffsetInCurrentParagraph = OffsetF(static_cast<float>(lastTouchOffset.GetX()),
431         static_cast<float>(lastTouchOffset.GetY() - y));
432     TextAffinity textAffinity;
433     paragraph->CalcCaretMetricsByPosition(relativeIndex, caretCaretMetric, touchOffsetInCurrentParagraph, textAffinity);
434     selectLineHeight = caretCaretMetric.height;
435     return { static_cast<float>(caretCaretMetric.offset.GetX()),
436         static_cast<float>(caretCaretMetric.offset.GetY() + y) };
437 }
438 
Reset()439 void ParagraphManager::Reset()
440 {
441     paragraphs_.clear();
442 }
443 
ToString() const444 std::string ParagraphManager::ParagraphInfo::ToString() const
445 {
446     return "Paragraph start: " + std::to_string(start) + ", end: " + std::to_string(end);
447 }
448 } // namespace OHOS::Ace::NG