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 namespace OHOS::Ace::NG {
19 namespace {
20 constexpr float MIN_RECT_TOP = -0.4f;
21 constexpr float MIN_RECT_PRECISION = -0.5f;
22 } // namespace
23
GetHeight() const24 float ParagraphManager::GetHeight() const
25 {
26 float res = 0.0f;
27 for (auto&& info : paragraphs_) {
28 auto paragraph = info.paragraph;
29 CHECK_NULL_RETURN(paragraph, 0.0f);
30 res += paragraph->GetHeight();
31 }
32 return res;
33 }
34
GetMaxIntrinsicWidth() const35 float ParagraphManager::GetMaxIntrinsicWidth() const
36 {
37 float res = 0.0f;
38 for (auto &&info : paragraphs_) {
39 auto paragraph = info.paragraph;
40 CHECK_NULL_RETURN(paragraph, 0.0f);
41 res = std::max(res, paragraph->GetMaxIntrinsicWidth());
42 }
43 return res;
44 }
DidExceedMaxLines() const45 bool ParagraphManager::DidExceedMaxLines() const
46 {
47 bool res = false;
48 for (auto &&info : paragraphs_) {
49 auto paragraph = info.paragraph;
50 CHECK_NULL_RETURN(paragraph, false);
51 res |= paragraph->DidExceedMaxLines();
52 }
53 return res;
54 }
55
DidExceedMaxLinesInner() const56 bool ParagraphManager::DidExceedMaxLinesInner() const
57 {
58 bool res = false;
59 for (auto&& info : paragraphs_) {
60 auto paragraph = info.paragraph;
61 CHECK_NULL_RETURN(paragraph, false);
62 res |= paragraph->DidExceedMaxLinesInner();
63 }
64 return res;
65 }
66
GetDumpInfo() const67 std::string ParagraphManager::GetDumpInfo() const
68 {
69 std::string dumpInfo = "";
70 for (auto&& info : paragraphs_) {
71 dumpInfo += "[";
72 auto paragraph = info.paragraph;
73 CHECK_NULL_RETURN(paragraph, dumpInfo);
74 dumpInfo += paragraph->GetDumpInfo();
75 dumpInfo += "]";
76 }
77 return dumpInfo;
78 }
79
GetLongestLine() const80 float ParagraphManager::GetLongestLine() const
81 {
82 float res = 0.0f;
83 for (auto &&info : paragraphs_) {
84 auto paragraph = info.paragraph;
85 CHECK_NULL_RETURN(paragraph, 0.0f);
86 res = std::max(res, paragraph->GetLongestLine());
87 }
88 return res;
89 }
GetMaxWidth() const90 float ParagraphManager::GetMaxWidth() const
91 {
92 float res = 0.0f;
93 for (auto &&info : paragraphs_) {
94 auto paragraph = info.paragraph;
95 CHECK_NULL_RETURN(paragraph, 0.0f);
96 res = std::max(res, paragraph->GetMaxWidth());
97 }
98 return res;
99 }
GetTextWidth() const100 float ParagraphManager::GetTextWidth() const
101 {
102 float res = 0.0f;
103 for (auto &&info : paragraphs_) {
104 auto paragraph = info.paragraph;
105 CHECK_NULL_RETURN(paragraph, 0.0f);
106 res = std::max(res, paragraph->GetTextWidth());
107 }
108 return res;
109 }
110
GetTextWidthIncludeIndent() const111 float ParagraphManager::GetTextWidthIncludeIndent() const
112 {
113 float res = 0.0f;
114 for (auto &&info : paragraphs_) {
115 auto paragraph = info.paragraph;
116 CHECK_NULL_RETURN(paragraph, 0.0f);
117 auto width = paragraph->GetTextWidth();
118 res = std::max(res, width);
119 }
120 return res;
121 }
122
GetLongestLineWithIndent() const123 float ParagraphManager::GetLongestLineWithIndent() const
124 {
125 float res = 0.0f;
126 for (auto &&info : paragraphs_) {
127 auto paragraph = info.paragraph;
128 CHECK_NULL_RETURN(paragraph, 0.0f);
129 auto width = paragraph->GetLongestLineWithIndent();
130 res = std::max(res, width);
131 }
132 return res;
133 }
134
GetLineCount() const135 size_t ParagraphManager::GetLineCount() const
136 {
137 size_t count = 0;
138 for (auto &&info : paragraphs_) {
139 auto paragraph = info.paragraph;
140 CHECK_NULL_RETURN(paragraph, 0);
141 count += paragraph->GetLineCount();
142 }
143 return count;
144 }
145
GetIndex(Offset offset,bool clamp) const146 int32_t ParagraphManager::GetIndex(Offset offset, bool clamp) const
147 {
148 CHECK_NULL_RETURN(!paragraphs_.empty(), 0);
149 if (clamp && LessNotEqual(offset.GetY(), 0.0)) {
150 return 0;
151 }
152 int idx = 0;
153 for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it, ++idx) {
154 auto&& info = *it;
155 if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight()) ||
156 (!clamp && idx == static_cast<int>(paragraphs_.size()) - 1)) {
157 return info.paragraph->GetGlyphIndexByCoordinate(offset) + info.start;
158 }
159 // get offset relative to each paragraph
160 offset.SetY(offset.GetY() - info.paragraph->GetHeight());
161 }
162 return paragraphs_.back().end;
163 }
164
GetGlyphPositionAtCoordinate(Offset offset)165 PositionWithAffinity ParagraphManager::GetGlyphPositionAtCoordinate(Offset offset)
166 {
167 TAG_LOGI(AceLogTag::ACE_TEXT,
168 "Get Glyph Position, coordinate = [%{public}.2f %{public}.2f]", offset.GetX(), offset.GetY());
169 PositionWithAffinity finalResult(0, TextAffinity::UPSTREAM);
170 CHECK_NULL_RETURN(!paragraphs_.empty(), finalResult);
171 if (LessNotEqual(offset.GetY(), 0.0)) {
172 return finalResult;
173 }
174 int idx = 0;
175 for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it, ++idx) {
176 auto& info = *it;
177 if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight()) ||
178 (idx == static_cast<int>(paragraphs_.size()) - 1)) {
179 auto result = info.paragraph->GetGlyphPositionAtCoordinate(offset);
180 finalResult.position_ = result.position_ + static_cast<size_t>(info.start);
181 TAG_LOGI(AceLogTag::ACE_TEXT,
182 "Current paragraph, originPos = %{public}zu, finalPos =%{public}zu and affinity = %{public}d",
183 result.position_, finalResult.position_, result.affinity_);
184 finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
185 return finalResult;
186 }
187 // get offset relative to each paragraph
188 offset.SetY(offset.GetY() - info.paragraph->GetHeight());
189 }
190 auto info = paragraphs_.back();
191 auto result = info.paragraph->GetGlyphPositionAtCoordinate(offset);
192 finalResult.position_ = static_cast<size_t>(info.end);
193 finalResult.affinity_ = static_cast<TextAffinity>(result.affinity_);
194 TAG_LOGI(AceLogTag::ACE_TEXT,
195 "Current paragraph, final position = %{public}zu and affinity = %{public}d", finalResult.position_,
196 finalResult.affinity_);
197 return finalResult;
198 }
199
GetGlyphIndexByCoordinate(Offset offset,bool isSelectionPos) const200 int32_t ParagraphManager::GetGlyphIndexByCoordinate(Offset offset, bool isSelectionPos) const
201 {
202 CHECK_NULL_RETURN(!paragraphs_.empty(), 0);
203 for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
204 auto &&info = *it;
205 if (LessOrEqual(offset.GetY(), info.paragraph->GetHeight())) {
206 return info.paragraph->GetGlyphIndexByCoordinate(offset, isSelectionPos) + info.start;
207 }
208 // get offset relative to each paragraph
209 offset.SetY(offset.GetY() - info.paragraph->GetHeight());
210 }
211 offset.SetY(offset.GetY() + paragraphs_.back().paragraph->GetHeight());
212 return paragraphs_.back().paragraph->GetGlyphIndexByCoordinate(offset, isSelectionPos) + paragraphs_.back().start;
213 }
214
GetWordBoundary(int32_t offset,int32_t & start,int32_t & end) const215 bool ParagraphManager::GetWordBoundary(int32_t offset, int32_t& start, int32_t& end) const
216 {
217 CHECK_NULL_RETURN(!paragraphs_.empty(), false);
218 auto offsetIndex = offset;
219 auto startIndex = 0;
220 auto endIndex = 0;
221 for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
222 auto &&info = *it;
223 if (LessNotEqual(offset, info.end)) {
224 auto flag = info.paragraph->GetWordBoundary(offsetIndex, start, end);
225 start += startIndex;
226 end += endIndex;
227 return flag;
228 }
229 // get offset relative to each paragraph
230 offsetIndex = offset - info.end;
231 startIndex = info.end;
232 endIndex = info.end;
233 }
234 return false;
235 }
236
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity) const237 bool ParagraphManager::CalcCaretMetricsByPosition(
238 int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity) const
239 {
240 CHECK_NULL_RETURN(!paragraphs_.empty(), false);
241 auto offsetIndex = extent;
242 auto offsetY = 0.0f;
243 auto result = false;
244 for (auto it = paragraphs_.begin(); it != paragraphs_.end(); ++it) {
245 auto &&info = *it;
246 if (textAffinity == TextAffinity::UPSTREAM || std::next(it) == paragraphs_.end()) {
247 if (LessOrEqual(extent, info.end)) {
248 result = info.paragraph->CalcCaretMetricsByPosition(offsetIndex, caretCaretMetric, textAffinity);
249 break;
250 }
251 } else {
252 if (LessNotEqual(extent, info.end)) {
253 result = info.paragraph->CalcCaretMetricsByPosition(offsetIndex, caretCaretMetric, textAffinity);
254 break;
255 }
256 }
257 // get offset relative to each paragraph
258 offsetIndex = extent - info.end;
259 offsetY += info.paragraph->GetHeight();
260 }
261 caretCaretMetric.offset += OffsetF(0.0f, offsetY);
262 return result;
263 }
264
GetLineMetricsByRectF(RectF rect,int32_t paragraphIndex) const265 LineMetrics ParagraphManager::GetLineMetricsByRectF(RectF rect, int32_t paragraphIndex) const
266 {
267 auto index = 0;
268 float height = 0;
269 auto iter = paragraphs_.begin();
270 while (index < paragraphIndex) {
271 auto paragraphInfo = *iter;
272 height += paragraphInfo.paragraph->GetHeight();
273 iter++;
274 index++;
275 }
276 auto paragraphInfo = *iter;
277 rect.SetTop(rect.GetY() - height);
278 auto lineMetrics = paragraphInfo.paragraph->GetLineMetricsByRectF(rect);
279 lineMetrics.y += height;
280 return lineMetrics;
281 }
282
GetLineMetrics(size_t lineNumber)283 TextLineMetrics ParagraphManager::GetLineMetrics(size_t lineNumber)
284 {
285 if (GetLineCount() == 0 || lineNumber > GetLineCount() - 1) {
286 TAG_LOGE(AceLogTag::ACE_TEXT,
287 "GetLineMetrics failed, lineNumber is greater than max lines:%{public}zu", lineNumber);
288 return TextLineMetrics();
289 }
290 size_t endIndex = 0;
291 double paragraphsHeight = 0.0;
292 size_t lineNumberParam = lineNumber;
293 for (auto &&info : paragraphs_) {
294 auto lineCount = info.paragraph->GetLineCount();
295 if (lineCount > 0 && lineNumber > lineCount - 1) {
296 lineNumber -= lineCount;
297 paragraphsHeight += info.paragraph->GetHeight();
298 auto lastLineMetrics = info.paragraph->GetLineMetrics(lineCount - 1);
299 endIndex += lastLineMetrics.endIndex + 1;
300 continue;
301 }
302 auto lineMetrics = info.paragraph->GetLineMetrics(lineNumber);
303 lineMetrics.startIndex += endIndex;
304 lineMetrics.endIndex += endIndex;
305 lineMetrics.lineNumber = lineNumberParam;
306 lineMetrics.y += paragraphsHeight;
307 lineMetrics.baseline += paragraphsHeight;
308 return lineMetrics;
309 }
310 return TextLineMetrics();
311 }
312
IsIndexAtParagraphEnd(int32_t index)313 bool ParagraphManager::IsIndexAtParagraphEnd(int32_t index)
314 {
315 for (const auto& info : paragraphs_) {
316 if (info.end == index) {
317 return true;
318 }
319 }
320 return false;
321 }
322
GetPaintRegion(RectF & boundsRect,float x,float y) const323 void ParagraphManager::GetPaintRegion(RectF& boundsRect, float x, float y) const
324 {
325 if (paragraphs_.empty()) {
326 return;
327 }
328 for (const auto& info : paragraphs_) {
329 CHECK_NULL_VOID(info.paragraph);
330 auto rect = info.paragraph->GetPaintRegion(x, y);
331 boundsRect = boundsRect.CombineRectT(rect);
332 y += info.paragraph->GetHeight();
333 }
334 }
335
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)336 std::vector<ParagraphManager::TextBox> ParagraphManager::GetRectsForRange(
337 int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
338 {
339 std::vector<TextBox> resultTextBoxes;
340 float y = 0.0f;
341 for (const auto& info : paragraphs_) {
342 if (info.start >= end) {
343 break;
344 }
345 int32_t relativeStart = std::max(static_cast<int32_t>(0), start - info.start);
346 int32_t relativeEnd = std::min(info.end - info.start, end - info.start);
347 if (relativeStart >= relativeEnd) {
348 y += info.paragraph->GetHeight();
349 continue;
350 }
351 std::vector<RectF> tempRects;
352 std::vector<TextDirection> tempTextDirections;
353 info.paragraph->TxtGetRectsForRange(
354 relativeStart, relativeEnd, heightStyle, widthStyle, tempRects, tempTextDirections);
355 if (tempTextDirections.size() < tempRects.size()) {
356 TAG_LOGE(AceLogTag::ACE_TEXT, "TxtGetRectsForRange failed, tempTextDirections size=%{public}zu is less "\
357 "than tempRects size=%{public}zu", tempTextDirections.size(), tempRects.size());
358 continue;
359 }
360 for (size_t i = 0; i < tempRects.size(); ++i) {
361 tempRects[i].SetTop(tempRects[i].Top() + y);
362 resultTextBoxes.emplace_back(TextBox(tempRects[i], tempTextDirections[i]));
363 }
364 y += info.paragraph->GetHeight();
365 }
366 return resultTextBoxes;
367 }
368
GetEllipsisTextRange()369 std::pair<size_t, size_t> ParagraphManager::GetEllipsisTextRange()
370 {
371 std::pair<size_t, size_t> range = {std::numeric_limits<size_t>::max(), 0};
372 for (auto&& info : paragraphs_) {
373 const auto& ellipsisTextRange = info.paragraph->GetEllipsisTextRange();
374 range.first = std::min(range.first, ellipsisTextRange.first);
375 range.second = std::max(range.second, ellipsisTextRange.second);
376 }
377 return range;
378 }
379
GetRects(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const380 std::vector<RectF> ParagraphManager::GetRects(int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
381 {
382 std::vector<RectF> res;
383 float y = 0.0f;
384 for (auto&& info : paragraphs_) {
385 std::vector<RectF> rects;
386 if (info.start > end) {
387 break;
388 }
389 if (info.end > start) {
390 auto relativeStart = (start < info.start) ? 0 : start - info.start;
391 if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
392 info.paragraph->GetTightRectsForRange(relativeStart, end - info.start, rects);
393 } else {
394 info.paragraph->GetRectsForRange(relativeStart, end - info.start, rects);
395 }
396
397 for (auto&& rect : rects) {
398 rect.SetTop(rect.Top() + y);
399 }
400 res.insert(res.end(), rects.begin(), rects.end());
401 }
402 y += info.paragraph->GetHeight();
403 }
404 return res;
405 }
406
GetParagraphInfo(int32_t position) const407 ParagraphManager::ParagraphInfo ParagraphManager::GetParagraphInfo(int32_t position) const
408 {
409 CHECK_EQUAL_RETURN(paragraphs_.empty(), true, {});
410 auto it = std::find_if(paragraphs_.begin(), paragraphs_.end(), [position](const ParagraphInfo& info) {
411 return (info.start <= position) && (position < info.end);
412 });
413 if (position == paragraphs_.back().end) {
414 --it;
415 }
416 CHECK_EQUAL_RETURN(it == paragraphs_.end(), true, {});
417 return (*it);
418 }
419
GetParagraphsRects(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const420 std::vector<std::pair<std::vector<RectF>, TextDirection>> ParagraphManager::GetParagraphsRects(
421 int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
422 {
423 std::vector<std::pair<std::vector<RectF>, TextDirection>> paragraphsRects;
424 float y = 0.0f;
425 for (auto&& info : paragraphs_) {
426 if (info.start > end) {
427 break;
428 }
429 if (info.end > start) {
430 std::vector<RectF> rects;
431 auto relativeStart = (start < info.start) ? 0 : start - info.start;
432 if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
433 info.paragraph->GetTightRectsForRange(relativeStart, end - info.start, rects);
434 } else {
435 info.paragraph->GetRectsForRange(relativeStart, end - info.start, rects);
436 }
437 std::pair<std::vector<RectF>, TextDirection> paragraphRects;
438 for (auto&& rect : rects) {
439 rect.SetTop(rect.Top() + y);
440 }
441 paragraphRects.first = rects;
442 paragraphRects.second = info.paragraphStyle.direction;
443 paragraphsRects.emplace_back(paragraphRects);
444 }
445 y += info.paragraph->GetHeight();
446 }
447 return paragraphsRects;
448 }
449
GetTextBoxesForSelect(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const450 std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> ParagraphManager::GetTextBoxesForSelect(
451 int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
452 {
453 SelectData selectData;
454 selectData.secondResult = CalcCaretMetricsByPosition(end, selectData.secondMetrics, TextAffinity::UPSTREAM);
455 std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> paragraphsRects;
456 selectData.y = 0.0f;
457 for (auto&& info : paragraphs_) {
458 if (info.start > end) {
459 break;
460 }
461 CHECK_NULL_BREAK(info.paragraph);
462 if (info.end > start) {
463 std::vector<RectF> rects;
464 selectData.relativeStart = std::max(0, start - info.start);
465 selectData.relativeEnd = end - info.start;
466 selectData.paragraphSpacing = info.paragraphStyle.paragraphSpacing.ConvertToPx();
467 if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
468 info.paragraph->GetTightRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
469 } else {
470 info.paragraph->GetRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
471 }
472 MakeBlankLineRectsInParagraph(rects, info, selectData);
473 for (auto&& rect : rects) {
474 rect.SetTop(rect.Top() + selectData.y);
475 }
476 paragraphsRects.emplace_back(std::make_pair(rects, info.paragraphStyle));
477 }
478 selectData.y += info.paragraph->GetHeight();
479 }
480 if (!paragraphsRects.empty()) {
481 selectData.y = 0.0f;
482 RemoveBlankLineRectByHandler(paragraphsRects.back().first, selectData);
483 }
484 return paragraphsRects;
485 }
486
MakeBlankLineRectsInParagraph(std::vector<RectF> & result,const ParagraphInfo & info,const SelectData & selectData)487 void ParagraphManager::MakeBlankLineRectsInParagraph(std::vector<RectF>& result, const ParagraphInfo& info,
488 const SelectData& selectData)
489 {
490 const int32_t realEnd = info.end - info.start;
491 const bool isLastParagraph = (selectData.relativeEnd == 0) || (selectData.relativeEnd <= realEnd);
492 AppendParagraphSpacingBlankRect(result, selectData);
493 if (isLastParagraph && !result.empty() && IsRectOutByHandler(result.back(), selectData)) {
494 auto lastRect = result.back();
495 result.pop_back();
496 AddParagraphSpacingBlankRect(result, lastRect, selectData);
497 return;
498 }
499 CHECK_NULL_VOID(info.paragraph);
500 float height = info.paragraph->GetHeight();
501 if (Positive(selectData.paragraphSpacing) && !isLastParagraph && !result.empty() &&
502 NearZero(result.back().Width()) && selectData.relativeEnd != realEnd) {
503 result.emplace_back(RectF(0.0f, height - selectData.paragraphSpacing, 0.0f, selectData.paragraphSpacing));
504 }
505 const float lastBottom = result.empty() ? MIN_RECT_TOP : result.back().Bottom();
506 int32_t loopStart = std::min(realEnd, selectData.relativeEnd);
507 int32_t loopEnd = std::max(0, selectData.relativeStart);
508 std::vector<RectF> rects;
509 for (int32_t index = loopStart; index >= loopEnd; index--) {
510 if (GreatOrEqualCustomPrecision(lastBottom, height, MIN_RECT_PRECISION)) {
511 break;
512 }
513 CaretMetricsF caretMetrics;
514 bool res = info.paragraph->CalcCaretMetricsByPosition(index, caretMetrics, TextAffinity::UPSTREAM);
515 CHECK_NULL_BREAK(res)
516 RectF rect(caretMetrics.offset.GetX(), caretMetrics.offset.GetY(), 0.0f, caretMetrics.height);
517 if (GreatNotEqual(rect.Bottom(), height) || (isLastParagraph && IsRectOutByHandler(rect, selectData))) {
518 continue;
519 }
520 if (LessNotEqual(rect.Top(), lastBottom)) {
521 break;
522 }
523 height = rect.Top();
524 rects.emplace_back(rect);
525 }
526 std::reverse(rects.begin(), rects.end());
527 result.insert(result.end(), rects.begin(), rects.end());
528 }
529
AppendParagraphSpacingBlankRect(std::vector<RectF> & rects,const SelectData & selectData)530 void ParagraphManager::AppendParagraphSpacingBlankRect(std::vector<RectF>& rects, const SelectData& selectData)
531 {
532 if (!Positive(selectData.paragraphSpacing) || rects.empty()) {
533 return;
534 }
535 std::vector<RectF> selectedRects = std::move(rects);
536 for (auto it = selectedRects.begin(); it != selectedRects.end(); it++) {
537 auto rect = *it;
538 if (NearZero(rect.Width()) && (it == selectedRects.begin() || !NearEqual(rect.Top(), std::prev(it)->Top()))) {
539 rect.SetHeight(rect.Height() + selectData.paragraphSpacing);
540 }
541 rects.push_back(rect);
542 }
543 }
544
AddParagraphSpacingBlankRect(std::vector<RectF> & rects,const RectF & lastRect,const SelectData & selectData)545 void ParagraphManager::AddParagraphSpacingBlankRect(
546 std::vector<RectF>& rects, const RectF& lastRect, const SelectData& selectData)
547 {
548 if (!Positive(selectData.paragraphSpacing) || rects.empty() || NearEqual(rects.back().Top(), lastRect.Top())) {
549 return;
550 }
551 rects.emplace_back(
552 RectF(lastRect.Left(), lastRect.Top() - selectData.paragraphSpacing, 0.0f, selectData.paragraphSpacing));
553 }
554
GetRichEditorBoxesForSelect(int32_t start,int32_t end,RectHeightPolicy rectHeightPolicy) const555 std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> ParagraphManager::GetRichEditorBoxesForSelect(
556 int32_t start, int32_t end, RectHeightPolicy rectHeightPolicy) const
557 {
558 SelectData selectData;
559 selectData.secondResult = CalcCaretMetricsByPosition(end, selectData.secondMetrics, TextAffinity::DOWNSTREAM);
560 std::vector<std::pair<std::vector<RectF>, ParagraphStyle>> paragraphsRects;
561 selectData.y = 0.0f;
562 for (auto&& info : paragraphs_) {
563 if (info.start > end) {
564 break;
565 }
566 CHECK_NULL_BREAK(info.paragraph);
567 if (info.end > start) {
568 std::vector<RectF> rects;
569 selectData.relativeStart = std::max(0, start - info.start);
570 selectData.relativeEnd = end - info.start;
571 if (rectHeightPolicy == RectHeightPolicy::COVER_TEXT) {
572 info.paragraph->GetTightRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
573 } else {
574 info.paragraph->GetRectsForRange(selectData.relativeStart, selectData.relativeEnd, rects);
575 }
576 MakeBlankRectsInRichEditor(rects, info, selectData);
577 for (auto&& rect : rects) {
578 rect.SetTop(rect.Top() + selectData.y);
579 }
580 paragraphsRects.emplace_back(std::make_pair(rects, info.paragraphStyle));
581 selectData.paragraphSpacing = info.paragraphStyle.paragraphSpacing.ConvertToPx();
582 }
583 selectData.y += info.paragraph->GetHeight();
584 }
585 if (!paragraphsRects.empty()) {
586 selectData.y = 0.0f;
587 RemoveBlankLineRectByHandler(paragraphsRects.back().first, selectData);
588 }
589 return paragraphsRects;
590 }
591
MakeBlankRectsInRichEditor(std::vector<RectF> & result,const ParagraphInfo & info,const SelectData & selectData)592 void ParagraphManager::MakeBlankRectsInRichEditor(std::vector<RectF>& result, const ParagraphInfo& info,
593 const SelectData& selectData)
594 {
595 const int32_t realEnd = info.end - info.start;
596 const bool isLastParagraph = (selectData.relativeEnd == 0) || (selectData.relativeEnd < realEnd);
597 if (isLastParagraph && !result.empty() && IsRectOutByHandler(result.back(), selectData)) {
598 result.pop_back();
599 return;
600 }
601 CHECK_NULL_VOID(info.paragraph);
602 float height = info.paragraph->GetHeight();
603 const float lastBottom = result.empty() ? MIN_RECT_TOP : result.back().Bottom();
604 int32_t loopStart = std::min(realEnd, selectData.relativeEnd);
605 int32_t loopEnd = std::max(0, selectData.relativeStart);
606 std::vector<RectF> rects;
607 for (int32_t index = loopStart; index >= loopEnd; index--) {
608 if (GreatOrEqualCustomPrecision(lastBottom, height, MIN_RECT_PRECISION)) {
609 break;
610 }
611 CaretMetricsF caretMetrics;
612 bool res = info.paragraph->CalcCaretMetricsByPosition(index, caretMetrics, TextAffinity::UPSTREAM);
613 CHECK_NULL_BREAK(res)
614 RectF rect(caretMetrics.offset.GetX(), caretMetrics.offset.GetY(), 0.0f, caretMetrics.height);
615 height = rect.Top();
616 rects.emplace_back(rect);
617 if (auto spacing = selectData.paragraphSpacing; index == 0 && !NearZero(spacing)) {
618 rects.emplace_back(caretMetrics.offset.GetX(), caretMetrics.offset.GetY() - spacing, 0, spacing);
619 }
620 }
621 std::reverse(rects.begin(), rects.end());
622 result.insert(result.end(), rects.begin(), rects.end());
623 }
624
RemoveBlankLineRectByHandler(std::vector<RectF> & rects,const SelectData & selectData)625 void ParagraphManager::RemoveBlankLineRectByHandler(std::vector<RectF>& rects, const SelectData& selectData)
626 {
627 while (!rects.empty()) {
628 CHECK_EQUAL_VOID(IsRectOutByHandler(rects.back(), selectData), false);
629 rects.pop_back();
630 }
631 }
632
IsRectOutByHandler(const RectF & rect,const SelectData & selectData)633 bool ParagraphManager::IsRectOutByHandler(const RectF& rect, const SelectData& selectData)
634 {
635 CHECK_EQUAL_RETURN(NearZero(rect.Width()), false, false);
636 CHECK_EQUAL_RETURN(selectData.secondResult, false, false);
637 return GreatOrEqual(rect.Top() + selectData.y, selectData.secondMetrics.offset.GetY());
638 }
639
IsSelectLineHeadAndUseLeadingMargin(int32_t start) const640 bool ParagraphManager::IsSelectLineHeadAndUseLeadingMargin(int32_t start) const
641 {
642 for (auto iter = paragraphs_.begin(); iter != paragraphs_.end(); iter++) {
643 auto curParagraph = *iter;
644 if (auto paragraph = curParagraph.paragraph; curParagraph.start == start && paragraph) {
645 auto leadingMargin = paragraph->GetParagraphStyle().leadingMargin;
646 CHECK_EQUAL_RETURN(leadingMargin && leadingMargin.value().IsValid(), true, true);
647 }
648 auto next = std::next(iter);
649 if (next != paragraphs_.end()) {
650 auto nextParagraph = *next;
651 if (auto paragraph = nextParagraph.paragraph; nextParagraph.start == start + 1) {
652 auto leadingMargin = paragraph->GetParagraphStyle().leadingMargin;
653 CHECK_EQUAL_RETURN(leadingMargin && leadingMargin.value().IsValid(), true, true);
654 }
655 }
656 }
657 return false;
658 }
659
LayoutParagraphs(float maxWidth)660 void ParagraphManager::LayoutParagraphs(float maxWidth)
661 {
662 for (auto&& info : paragraphs_) {
663 auto paragraph = info.paragraph;
664 CHECK_NULL_CONTINUE(paragraph);
665 paragraph->Layout(maxWidth);
666 }
667 }
668
GetPlaceholderRects() const669 std::vector<RectF> ParagraphManager::GetPlaceholderRects() const
670 {
671 std::vector<RectF> res;
672 float y = 0.0f;
673 for (auto&& info : paragraphs_) {
674 std::vector<RectF> rects;
675 info.paragraph->GetRectsForPlaceholders(rects);
676 for (auto& rect : rects) {
677 rect.SetTop(rect.Top() + y);
678 }
679 y += info.paragraph->GetHeight();
680
681 res.insert(res.end(), rects.begin(), rects.end());
682 }
683 return res;
684 }
685
ComputeCursorOffset(int32_t index,float & selectLineHeight,bool downStreamFirst,bool needLineHighest) const686 OffsetF ParagraphManager::ComputeCursorOffset(
687 int32_t index, float& selectLineHeight, bool downStreamFirst, bool needLineHighest) const
688 {
689 CHECK_NULL_RETURN(!paragraphs_.empty(), {});
690 auto it = paragraphs_.begin();
691 float y = 0.0f;
692 while (it != paragraphs_.end()) {
693 if (index >= it->start && index < it->end) {
694 break;
695 }
696 y += it->paragraph->GetHeight();
697 ++it;
698 }
699
700 if (index == paragraphs_.back().end) {
701 --it;
702 y -= it->paragraph->GetHeight();
703 }
704
705 CHECK_NULL_RETURN(it != paragraphs_.end(), OffsetF(0.0f, y));
706
707 int32_t relativeIndex = index - it->start;
708 auto&& paragraph = it->paragraph;
709 CaretMetricsF metrics;
710 auto computeSuccess = false;
711 if (downStreamFirst) {
712 computeSuccess = paragraph->ComputeOffsetForCaretDownstream(relativeIndex, metrics, needLineHighest) ||
713 paragraph->ComputeOffsetForCaretUpstream(relativeIndex, metrics, needLineHighest);
714 } else {
715 computeSuccess = paragraph->ComputeOffsetForCaretUpstream(relativeIndex, metrics, needLineHighest) ||
716 paragraph->ComputeOffsetForCaretDownstream(relativeIndex, metrics, needLineHighest);
717 }
718 CHECK_NULL_RETURN(computeSuccess, OffsetF(0.0f, y));
719 selectLineHeight = metrics.height;
720 return { static_cast<float>(metrics.offset.GetX()), static_cast<float>(metrics.offset.GetY() + y) };
721 }
722
ComputeCursorInfoByClick(int32_t index,float & selectLineHeight,const OffsetF & lastTouchOffset) const723 OffsetF ParagraphManager::ComputeCursorInfoByClick(
724 int32_t index, float& selectLineHeight, const OffsetF& lastTouchOffset) const
725 {
726 CHECK_NULL_RETURN(!paragraphs_.empty(), {});
727 auto it = paragraphs_.begin();
728 float y = 0.0f;
729 while (it != paragraphs_.end()) {
730 if (index >= it->start && index < it->end) {
731 break;
732 }
733 y += it->paragraph->GetHeight();
734 ++it;
735 }
736
737 if (index == paragraphs_.back().end) {
738 --it;
739 y -= it->paragraph->GetHeight();
740 }
741
742 CHECK_NULL_RETURN(it != paragraphs_.end(), OffsetF(0.0f, y));
743
744 int32_t relativeIndex = index - it->start;
745 auto&& paragraph = it->paragraph;
746
747 CaretMetricsF caretCaretMetric;
748 auto touchOffsetInCurrentParagraph = OffsetF(static_cast<float>(lastTouchOffset.GetX()),
749 static_cast<float>(lastTouchOffset.GetY() - y));
750 TextAffinity textAffinity;
751 paragraph->CalcCaretMetricsByPosition(relativeIndex, caretCaretMetric, touchOffsetInCurrentParagraph, textAffinity);
752 selectLineHeight = caretCaretMetric.height;
753 return { static_cast<float>(caretCaretMetric.offset.GetX()),
754 static_cast<float>(caretCaretMetric.offset.GetY() + y) };
755 }
756
Reset()757 void ParagraphManager::Reset()
758 {
759 paragraphs_.clear();
760 }
761
ToString() const762 std::string ParagraphManager::ParagraphInfo::ToString() const
763 {
764 return "Paragraph start: " + std::to_string(start) + ", end: " + std::to_string(end);
765 }
766 } // namespace OHOS::Ace::NG
767