1 /*
2 * Copyright (c) 2022 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/text_overlay/text_overlay_manager.h"
17
18 #include <cmath>
19 #include <string>
20
21 #include "core/components/font/constants_converter.h"
22 #include "core/components/stack/stack_element.h"
23 #include "core/components/text_overlay/text_overlay_component.h"
24 #ifndef USE_GRAPHIC_TEXT_GINE
25 #include "txt/paragraph_txt.h"
26 #else
27 #include "rosen_text/typography.h"
28 #include "unicode/uchar.h"
29 #endif
30 #ifndef USE_ROSEN_DRAWING
31 #include "include/core/SkCanvas.h"
32 #else
33 #include "core/components_ng/render/drawing.h"
34 #endif
35
36 namespace OHOS::Ace {
37
38 namespace {
39 constexpr char16_t NEWLINE_CODE = u'\n';
40 constexpr double FIFTY_PERCENT = 0.5;
41 constexpr int32_t SHOW_HANDLE_DURATION = 250;
42 } // namespace
43
44 TextOverlayBase::~TextOverlayBase() = default;
45
MakeEmptyOffset() const46 Offset TextOverlayBase::MakeEmptyOffset() const
47 {
48 if (realTextDirection_ == TextDirection::RTL) {
49 return Offset(textOverlayPaintRect_.Width(), 0.0);
50 }
51
52 switch (textAlign_) {
53 case TextAlign::LEFT: {
54 return Offset::Zero();
55 }
56 case TextAlign::RIGHT: {
57 return Offset(textOverlayPaintRect_.Width(), 0.0);
58 }
59 case TextAlign::JUSTIFY:
60 case TextAlign::CENTER: {
61 return Offset(textOverlayPaintRect_.Width() / 2.0, 0.0);
62 }
63 case TextAlign::END: {
64 switch (defaultTextDirection_) {
65 case TextDirection::RTL: {
66 return Offset::Zero();
67 }
68 case TextDirection::LTR:
69 default: {
70 return Offset(textOverlayPaintRect_.Width(), 0.0);
71 }
72 }
73 }
74 case TextAlign::START:
75 default: {
76 // Default to start.
77 switch (defaultTextDirection_) {
78 case TextDirection::RTL: {
79 return Offset(textOverlayPaintRect_.Width(), 0.0);
80 }
81 case TextDirection::LTR:
82 default: {
83 return Offset::Zero();
84 }
85 }
86 }
87 }
88 }
89
GetBoundaryOfParagraph(bool isLeftBoundary) const90 double TextOverlayBase::GetBoundaryOfParagraph(bool isLeftBoundary) const
91 {
92 if (!paragraph_ || textValue_.text.empty()) {
93 return 0.0;
94 }
95 #ifndef USE_GRAPHIC_TEXT_GINE
96 auto boxes = paragraph_->GetRectsForRange(0, textValue_.GetWideText().length(),
97 txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
98 #else
99 auto boxes = paragraph_->GetTextRectsByBoundary(0, textValue_.GetWideText().length(),
100 Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
101 #endif
102 if (boxes.empty()) {
103 return 0.0;
104 }
105 #ifndef USE_GRAPHIC_TEXT_GINE
106 double leftBoundaryOfParagraph = boxes.front().rect.fLeft;
107 double rightBoundaryOfParagraph = boxes.front().rect.fLeft;
108 double bottomBoundaryOfParagraph = boxes.front().rect.fBottom;
109 #else
110 double leftBoundaryOfParagraph = boxes.front().rect.GetLeft();
111 double rightBoundaryOfParagraph = boxes.front().rect.GetLeft();
112 double bottomBoundaryOfParagraph = boxes.front().rect.GetBottom();
113 #endif
114 for (const auto& box : boxes) {
115 #ifndef USE_GRAPHIC_TEXT_GINE
116 if (cursorPositionType_ == CursorPositionType::END && !NearEqual(box.rect.fBottom, bottomBoundaryOfParagraph)) {
117 bottomBoundaryOfParagraph = box.rect.fBottom;
118 leftBoundaryOfParagraph = box.rect.fLeft;
119 rightBoundaryOfParagraph = box.rect.fRight;
120 #else
121 if (cursorPositionType_ == CursorPositionType::END &&
122 !NearEqual(box.rect.GetBottom(), bottomBoundaryOfParagraph)) {
123 bottomBoundaryOfParagraph = box.rect.GetBottom();
124 leftBoundaryOfParagraph = box.rect.GetLeft();
125 rightBoundaryOfParagraph = box.rect.GetRight();
126 #endif
127 continue;
128 }
129 #ifndef USE_GRAPHIC_TEXT_GINE
130 leftBoundaryOfParagraph = std::min(static_cast<double>(box.rect.fLeft), leftBoundaryOfParagraph);
131 rightBoundaryOfParagraph = std::max(static_cast<double>(box.rect.fRight), rightBoundaryOfParagraph);
132 #else
133 leftBoundaryOfParagraph = std::min(static_cast<double>(box.rect.GetLeft()), leftBoundaryOfParagraph);
134 rightBoundaryOfParagraph = std::max(static_cast<double>(box.rect.GetRight()), rightBoundaryOfParagraph);
135 #endif
136 }
137 return isLeftBoundary ? leftBoundaryOfParagraph : rightBoundaryOfParagraph;
138 }
139
140 bool TextOverlayBase::ComputeOffsetForCaretUpstream(int32_t extent, CaretMetrics& result) const
141 {
142 auto text = StringUtils::Str8ToStr16(textForDisplay_);
143 if (!paragraph_ || text.empty()) {
144 return false;
145 }
146
147 char16_t prevChar = 0;
148 if (static_cast<size_t>(extent) <= text.length()) {
149 prevChar = text[std::max(0, extent - 1)];
150 }
151
152 result.Reset();
153 int32_t graphemeClusterLength = StringUtils::NotInUtf16Bmp(prevChar) ? 2 : 1;
154 int32_t prev = extent - graphemeClusterLength;
155 #ifndef USE_GRAPHIC_TEXT_GINE
156 auto boxes = paragraph_->GetRectsForRange(
157 prev, extent, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
158 #else
159 auto boxes = paragraph_->GetTextRectsByBoundary(
160 prev, extent, Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
161 #endif
162 while (boxes.empty() && !textValue_.text.empty()) {
163 graphemeClusterLength *= 2;
164 prev = extent - graphemeClusterLength;
165 if (prev < 0) {
166 #ifndef USE_GRAPHIC_TEXT_GINE
167 boxes = paragraph_->GetRectsForRange(
168 0, extent, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
169 #else
170 boxes = paragraph_->GetTextRectsByBoundary(
171 0, extent, Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
172 #endif
173 break;
174 }
175 #ifndef USE_GRAPHIC_TEXT_GINE
176 boxes = paragraph_->GetRectsForRange(
177 prev, extent, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
178 #else
179 boxes = paragraph_->GetTextRectsByBoundary(
180 prev, extent, Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
181 #endif
182 }
183 if (boxes.empty()) {
184 return false;
185 }
186
187 const auto& textBox = *boxes.begin();
188
189 if (prevChar == NEWLINE_CODE) {
190 // Return the start of next line.
191 auto emptyOffset = MakeEmptyOffset();
192 result.offset.SetX(emptyOffset.GetX());
193 #ifndef USE_GRAPHIC_TEXT_GINE
194 result.offset.SetY(textBox.rect.fBottom);
195 #else
196 result.offset.SetY(textBox.rect.GetBottom());
197 #endif
198 result.height = caretProto_.Height();
199 return true;
200 }
201
202 #ifndef USE_GRAPHIC_TEXT_GINE
203 bool isLtr = textBox.direction == txt::TextDirection::ltr;
204 #else
205 bool isLtr = textBox.direction == Rosen::TextDirection::LTR;
206 #endif
207 // Caret is within width of the upstream glyphs.
208 #ifndef USE_GRAPHIC_TEXT_GINE
209 double caretEnd = isLtr ? textBox.rect.fRight : textBox.rect.fLeft;
210 #else
211 double caretEnd = isLtr ? textBox.rect.GetRight() : textBox.rect.GetLeft();
212 #endif
213 if (cursorPositionType_ == CursorPositionType::END) {
214 caretEnd = GetBoundaryOfParagraph(realTextDirection_ != TextDirection::LTR);
215 }
216 double dx = isLtr ? caretEnd : caretEnd - caretProto_.Width();
217 double offsetX = std::min(dx, paragraph_->GetMaxWidth());
218 result.offset.SetX(offsetX);
219 #ifndef USE_GRAPHIC_TEXT_GINE
220 result.offset.SetY(textBox.rect.fTop);
221 result.height = textBox.rect.fBottom - textBox.rect.fTop;
222 #else
223 result.offset.SetY(textBox.rect.GetTop());
224 result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
225 #endif
226
227 return true;
228 }
229
230 bool TextOverlayBase::ComputeOffsetForCaretDownstream(int32_t extent, CaretMetrics& result) const
231 {
232 if (!paragraph_ || static_cast<size_t>(extent) >= textValue_.GetWideText().length()) {
233 return false;
234 }
235
236 result.Reset();
237 const int32_t graphemeClusterLength = 1;
238 const int32_t next = extent + graphemeClusterLength;
239 #ifndef USE_GRAPHIC_TEXT_GINE
240 auto boxes = paragraph_->GetRectsForRange(
241 extent, next, txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
242 #else
243 auto boxes = paragraph_->GetTextRectsByBoundary(
244 extent, next, Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
245 #endif
246 if (boxes.empty()) {
247 return false;
248 }
249
250 const auto& textBox = *boxes.begin();
251 #ifndef USE_GRAPHIC_TEXT_GINE
252 bool isLtr = textBox.direction == txt::TextDirection::ltr;
253 #else
254 bool isLtr = textBox.direction == Rosen::TextDirection::LTR;
255 #endif
256 // Caret is within width of the downstream glyphs.
257 #ifndef USE_GRAPHIC_TEXT_GINE
258 double caretStart = isLtr ? textBox.rect.fLeft : textBox.rect.fRight;
259 #else
260 double caretStart = isLtr ? textBox.rect.GetLeft() : textBox.rect.GetRight();
261 #endif
262 if (cursorPositionType_ == CursorPositionType::END) {
263 caretStart = GetBoundaryOfParagraph(realTextDirection_ != TextDirection::LTR);
264 }
265 double dx = isLtr ? caretStart : caretStart - caretProto_.Width();
266 double offsetX = std::min(dx, paragraph_->GetMaxWidth());
267 result.offset.SetX(offsetX);
268 #ifndef USE_GRAPHIC_TEXT_GINE
269 result.offset.SetY(textBox.rect.fTop);
270 result.height = textBox.rect.fBottom - textBox.rect.fTop;
271 #else
272 result.offset.SetY(textBox.rect.GetTop());
273 result.height = textBox.rect.GetBottom() - textBox.rect.GetTop();
274 #endif
275
276 return true;
277 }
278
279 bool TextOverlayBase::ComputeOffsetForCaretCloserToClick(int32_t extent, CaretMetrics& result) const
280 {
281 CaretMetrics upStreamMetrics;
282 bool upStreamSuccess = ComputeOffsetForCaretUpstream(extent, upStreamMetrics);
283 CaretMetrics downStreamMetrics;
284 bool downStreamSuccess = ComputeOffsetForCaretDownstream(extent, downStreamMetrics);
285 bool nearToUpStream = LessOrEqual(std::abs(upStreamMetrics.offset.GetX() - clickOffset_.GetX()),
286 std::abs(downStreamMetrics.offset.GetX() - clickOffset_.GetX()));
287 result = nearToUpStream ? upStreamMetrics : downStreamMetrics;
288 return upStreamSuccess || downStreamSuccess;
289 }
290
291 DirectionStatus TextOverlayBase::GetDirectionStatusOfPosition(int32_t position) const
292 {
293 const char mark = ' ';
294 std::string tempBefore = textValue_.GetSelectedText(TextSelection(0, position));
295 StringUtils::DeleteAllMark(tempBefore, mark);
296 const auto& textBeforeCursor = StringUtils::ToWstring(tempBefore);
297
298 std::string tempAfter = textValue_.GetSelectedText(TextSelection(position, textValue_.GetWideText().length()));
299 StringUtils::DeleteAllMark(tempAfter, mark);
300 const auto& textAfterCursor = StringUtils::ToWstring(tempAfter);
301
302 bool isBeforeCharRtl = false;
303 if (!textBeforeCursor.empty()) {
304 const auto& charBefore = textBeforeCursor.back();
305 isBeforeCharRtl = (u_charDirection(charBefore) == UCharDirection::U_RIGHT_TO_LEFT ||
306 u_charDirection(charBefore) == UCharDirection::U_RIGHT_TO_LEFT_ARABIC);
307 }
308
309 bool isAfterCharRtl = false;
310 if (!textAfterCursor.empty()) {
311 const auto& charAfter = textAfterCursor.front();
312 isAfterCharRtl = (u_charDirection(charAfter) == UCharDirection::U_RIGHT_TO_LEFT ||
313 u_charDirection(charAfter) == UCharDirection::U_RIGHT_TO_LEFT_ARABIC);
314 }
315 return static_cast<DirectionStatus>(
316 (static_cast<uint8_t>(isBeforeCharRtl) << 1) | static_cast<uint8_t>(isAfterCharRtl));
317 }
318
319 bool TextOverlayBase::GetCaretRect(int32_t extent, Rect& caretRect, double caretHeightOffset) const
320 {
321 CaretMetrics metrics;
322 bool computeSuccess = false;
323 DirectionStatus directionStatus = GetDirectionStatusOfPosition(extent);
324 if (extent != 0 && extent != static_cast<int32_t>(textValue_.GetWideText().length()) &&
325 (directionStatus == DirectionStatus::LEFT_RIGHT || directionStatus == DirectionStatus::RIGHT_LEFT) &&
326 cursorPositionType_ != CursorPositionType::NONE &&
327 LessOrEqual(clickOffset_.GetX(), textOverlayPaintRect_.Width())) {
328 computeSuccess = ComputeOffsetForCaretCloserToClick(cursorPositionForShow_, metrics);
329 } else {
330 if (textAffinity_ == TextAffinity::DOWNSTREAM) {
331 computeSuccess =
332 ComputeOffsetForCaretDownstream(extent, metrics) || ComputeOffsetForCaretUpstream(extent, metrics);
333 } else {
334 computeSuccess =
335 ComputeOffsetForCaretUpstream(extent, metrics) || ComputeOffsetForCaretDownstream(extent, metrics);
336 }
337 }
338 if (computeSuccess && !textValue_.text.empty()) {
339 if (metrics.height <= 0 || std::isnan(metrics.height)) {
340 // The reason may be text lines is exceed the paragraph maxline.
341 return false;
342 }
343 caretRect.SetRect(metrics.offset.GetX(), metrics.offset.GetY() + caretHeightOffset, cursorWidth_,
344 metrics.height - caretHeightOffset * 2.0);
345 } else {
346 // Use proto caret.
347 caretRect = caretProto_ + MakeEmptyOffset();
348 }
349
350 return true;
351 }
352
353 int32_t TextOverlayBase::GetCursorPositionForClick(const Offset& offset, const Offset& globalOffset)
354 {
355 if (!paragraph_) {
356 return 0;
357 }
358 cursorPositionType_ = CursorPositionType::NORMAL;
359 clickOffset_ = offset - globalOffset - textOffsetForShowCaret_;
360 // Solve can't select right boundary of RTL language.
361 double rightBoundary = GetBoundaryOfParagraph(false);
362 if (GreatOrEqual(clickOffset_.GetX(), rightBoundary)) {
363 int32_t rightBoundaryPosition = static_cast<int32_t>(
364 paragraph_->GetGlyphIndexByCoordinate(rightBoundary - cursorWidth_, clickOffset_.GetY()).index);
365
366 return realTextDirection_ == TextDirection::RTL ? 0 : rightBoundaryPosition;
367 }
368
369 #ifndef USE_GRAPHIC_TEXT_GINE
370 return static_cast<int32_t>(
371 paragraph_->GetGlyphPositionAtCoordinate(clickOffset_.GetX(), clickOffset_.GetY()).position);
372 #else
373 return static_cast<int32_t>(paragraph_->GetGlyphIndexByCoordinate(clickOffset_.GetX(), clickOffset_.GetY()).index);
374 #endif
375 }
376
377 int32_t TextOverlayBase::GetGraphemeClusterLength(int32_t extend, bool isPrefix) const
378 {
379 auto text = textForDisplay_;
380 char16_t aroundChar = 0;
381 if (isPrefix) {
382 if (static_cast<size_t>(extend) <= text.length()) {
383 aroundChar = text[std::max(0, extend - 1)];
384 }
385 } else {
386 if (static_cast<size_t>(extend) < (text.length())) {
387 aroundChar = text[std::min(text.length() ? static_cast<int32_t>(text.length()) - 1 : 0, extend)];
388 }
389 }
390 return StringUtils::NotInUtf16Bmp(aroundChar) ? 2 : 1;
391 }
392
393 void TextOverlayBase::InitAnimation(const WeakPtr<PipelineContext>& pipelineContext)
394 {
395 auto context = pipelineContext.Upgrade();
396 if (!context) {
397 LOGE("Context is null.");
398 return;
399 }
400
401 if (!textOverlay_) {
402 LOGE("InitAnimation error, textOverlay is nullptr");
403 return;
404 }
405
406 // Get the handleDiameter in theme, textoverlay is not nullptr
407 double initHandleDiameter = textOverlay_->GetHandleDiameter().Value();
408 double initHandleDiameterInner = textOverlay_->GetHandleDiameterInner().Value();
409
410 // Add the animation for handleDiameter
411 auto diameterAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(
412 initHandleDiameter * FIFTY_PERCENT, initHandleDiameter, Curves::ELASTICS);
413 diameterAnimation->AddListener([weak = AceType::WeakClaim(this)](double value) {
414 auto renderNode = weak.Upgrade();
415 if (renderNode && renderNode->updateHandleDiameter_) {
416 renderNode->updateHandleDiameter_(value);
417 }
418 });
419
420 // Add the animation for handleDiameterinner
421 auto diameterInnerAnimation = AceType::MakeRefPtr<CurveAnimation<double>>(
422 initHandleDiameterInner * FIFTY_PERCENT, initHandleDiameterInner, Curves::ELASTICS);
423 diameterInnerAnimation->AddListener([weak = AceType::WeakClaim(this)](double value) {
424 auto renderNode = weak.Upgrade();
425 if (renderNode && renderNode->updateHandleDiameterInner_) {
426 renderNode->updateHandleDiameterInner_(value);
427 }
428 });
429
430 // Add the animation
431 animator_ = CREATE_ANIMATOR(context);
432 animator_->AddInterpolator(diameterAnimation);
433 animator_->AddInterpolator(diameterInnerAnimation);
434 animator_->SetDuration(SHOW_HANDLE_DURATION);
435 animator_->Play();
436 }
437
438 #ifndef USE_ROSEN_DRAWING
439 void TextOverlayBase::PaintSelection(SkCanvas* canvas, const Offset& globalOffset)
440 #else
441 void TextOverlayBase::PaintSelection(RSCanvas* canvas, const Offset& globalOffset)
442 #endif
443 {
444 selectedRect_.clear();
445 if (!IsSelectiveDevice()) {
446 return;
447 }
448 using namespace Constants;
449
450 if (!paragraph_ || (canvas == nullptr)) {
451 return;
452 }
453 const auto& selection = textValue_.selection;
454 if (textValue_.text.empty() || selection.GetStart() == selection.GetEnd()) {
455 return;
456 }
457 #ifndef USE_GRAPHIC_TEXT_GINE
458 const auto& boxes = paragraph_->GetRectsForRange(selection.GetStart(), selection.GetEnd(),
459 txt::Paragraph::RectHeightStyle::kMax, txt::Paragraph::RectWidthStyle::kTight);
460 #else
461 const auto& boxes = paragraph_->GetTextRectsByBoundary(selection.GetStart(), selection.GetEnd(),
462 Rosen::TextRectHeightStyle::COVER_TOP_AND_BOTTOM, Rosen::TextRectWidthStyle::TIGHT);
463 #endif
464 if (boxes.empty()) {
465 return;
466 }
467 #ifndef USE_ROSEN_DRAWING
468 canvas->save();
469 SkPaint paint;
470 paint.setColor(selectedColor_.GetValue());
471 Offset effectiveOffset = textOffsetForShowCaret_;
472 for (const auto& box : boxes) {
473 auto selectionRect = ConvertSkRect(box.rect) + effectiveOffset;
474 selectedRect_.emplace_back(selectionRect + globalOffset);
475 #ifndef USE_GRAPHIC_TEXT_GINE
476 if (box.direction == txt::TextDirection::ltr) {
477 #else
478 if (box.direction == Rosen::TextDirection::LTR) {
479 #endif
480 canvas->drawRect(SkRect::MakeLTRB(selectionRect.Left(), selectionRect.Top(), selectionRect.Right(),
481 selectionRect.Bottom()),
482 paint);
483 } else {
484 canvas->drawRect(SkRect::MakeLTRB(selectionRect.Right(), selectionRect.Top(), selectionRect.Left(),
485 selectionRect.Bottom()),
486 paint);
487 }
488 }
489 canvas->restore();
490 #else
491 canvas->Save();
492 RSPen pen;
493 pen.SetColor(selectedColor_.GetValue());
494 Offset effectiveOffset = textOffsetForShowCaret_;
495 canvas->AttachPen(pen);
496 for (const auto& box : boxes) {
497 auto selectionRect = ConvertSkRect(box.rect) + effectiveOffset;
498 selectedRect_.emplace_back(selectionRect + globalOffset);
499 #ifndef USE_GRAPHIC_TEXT_GINE
500 if (box.direction == txt::TextDirection::ltr) {
501 #else
502 if (box.direction == Rosen::TextDirection::LTR) {
503 #endif
504 canvas->DrawRect(
505 RSRect(selectionRect.Left(), selectionRect.Top(), selectionRect.Right(), selectionRect.Bottom()));
506 } else {
507 canvas->DrawRect(
508 RSRect(selectionRect.Right(), selectionRect.Top(), selectionRect.Left(), selectionRect.Bottom()));
509 }
510 }
511 canvas->DetachPen();
512 canvas->Restore();
513 #endif
514 }
515
516 void TextOverlayBase::InitSelection(const Offset& pos, const Offset& globalOffset)
517 {
518 int32_t extend = GetCursorPositionForClick(pos, globalOffset);
519 int32_t extendEnd = extend + GetGraphemeClusterLength(extend, false);
520 textValue_.UpdateSelection(extend, extendEnd);
521 }
522
523 void TextOverlayBase::UpdateStartSelection(int32_t end, const Offset& pos, const Offset& globalOffset)
524 {
525 int32_t extend = GetCursorPositionForClick(pos, globalOffset);
526 textValue_.UpdateSelection(extend, end);
527 }
528
529 void TextOverlayBase::UpdateEndSelection(int32_t start, const Offset& pos, const Offset& globalOffset)
530 {
531 int32_t extend = GetCursorPositionForClick(pos, globalOffset);
532 textValue_.UpdateSelection(start, extend);
533 }
534
535 void TextOverlayBase::ChangeSelection(int32_t start, int32_t end)
536 {
537 textValue_.UpdateSelection(start, end);
538 }
539
540 RefPtr<TextOverlayManager> TextOverlayBase::GetTextOverlayManager(const WeakPtr<PipelineContext>& pipelineContext)
541 {
542 auto context = pipelineContext.Upgrade();
543 if (!context) {
544 return nullptr;
545 }
546
547 return context->GetTextOverlayManager();
548 }
549
550 TextOverlayManager::TextOverlayManager(const WeakPtr<PipelineContext>& context)
551 {
552 context_ = context;
553 }
554
555 TextOverlayManager::~TextOverlayManager() = default;
556
557 const RefPtr<RenderNode> TextOverlayManager::GetTargetNode() const
558 {
559 auto textOverlayBase = textOverlayBase_.Upgrade();
560 if (!textOverlayBase) {
561 return nullptr;
562 }
563
564 auto targetNode = AceType::DynamicCast<RenderNode>(textOverlayBase);
565 if (!targetNode) {
566 return nullptr;
567 }
568 return targetNode;
569 }
570
571 void TextOverlayManager::PopTextOverlay()
572 {
573 coordinateOffset_ = Offset();
574 const auto& stackElement = stackElement_.Upgrade();
575 if (stackElement) {
576 stackElement->PopTextOverlay();
577 }
578 }
579
580 void TextOverlayManager::PushTextOverlayToStack(
581 const RefPtr<TextOverlayComponent>& textOverlay, const WeakPtr<PipelineContext>& pipelineContext)
582 {
583 if (!textOverlay) {
584 LOGE("TextOverlay is null");
585 return;
586 }
587
588 auto context = pipelineContext.Upgrade();
589 if (!context) {
590 LOGE("Context is null");
591 return;
592 }
593
594 auto lastStack = context->GetLastStack();
595 if (!lastStack) {
596 LOGE("LastStack is null");
597 return;
598 }
599
600 lastStack->PushComponent(textOverlay, false);
601 stackElement_ = WeakClaim(RawPtr(lastStack));
602 }
603
604 void TextOverlayManager::HandleCtrlC() const
605 {
606 auto context = context_.Upgrade();
607 if (!context) {
608 LOGE("get context fail");
609 return;
610 }
611 auto clipboard = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
612 if (!clipboard) {
613 LOGE("get clipboard fail");
614 return;
615 }
616 auto textOverlayBase = textOverlayBase_.Upgrade();
617 if (!textOverlayBase) {
618 LOGE("get textOverlayBase fail");
619 return;
620 }
621 clipboard->SetData(textOverlayBase->GetSelectedContent());
622 }
623
624 bool TextOverlayBase::IsSelectedText(const Offset& pos, const Offset& globalOffset)
625 {
626 int32_t tempText = GetCursorPositionForClick(pos, globalOffset);
627 return (tempText >= textValue_.selection.GetStart() && tempText <= textValue_.selection.GetEnd());
628 }
629
630 } // namespace OHOS::Ace
631