1 /*
2 * Copyright (c) 2022-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/text/text_pattern.h"
17
18 #include <cstdint>
19 #include <iterator>
20 #include <stack>
21 #include <string>
22
23 #include "base/geometry/ng/offset_t.h"
24 #include "base/geometry/ng/rect_t.h"
25 #include "base/geometry/offset.h"
26 #include "base/log/dump_log.h"
27 #include "base/log/log_wrapper.h"
28 #include "base/utils/string_utils.h"
29 #include "base/utils/utils.h"
30 #include "base/window/drag_window.h"
31 #include "core/common/ace_engine_ext.h"
32 #include "core/common/ai/data_detector_mgr.h"
33 #include "core/common/container.h"
34 #include "core/common/container_scope.h"
35 #include "core/common/font_manager.h"
36 #include "core/common/recorder/event_recorder.h"
37 #include "core/common/recorder/node_data_cache.h"
38 #include "core/common/udmf/udmf_client.h"
39 #include "core/common/vibrator/vibrator_utils.h"
40 #include "core/components/common/properties/text_style_parser.h"
41 #include "core/components/text_overlay/text_overlay_theme.h"
42 #include "core/components_ng/base/frame_node.h"
43 #include "core/components_ng/base/inspector_filter.h"
44 #include "core/components_ng/base/ui_node.h"
45 #include "core/components_ng/base/view_stack_processor.h"
46 #include "core/components_ng/event/gesture_event_hub.h"
47 #include "core/components_ng/event/long_press_event.h"
48 #include "core/components_ng/manager/select_overlay/select_overlay_manager.h"
49 #include "core/components_ng/pattern/image/image_layout_property.h"
50 #include "core/components_ng/pattern/rich_editor/paragraph_manager.h"
51 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_info.h"
52 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
53 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
54 #include "core/components_ng/pattern/text/span_node.h"
55 #include "core/components_ng/pattern/text/text_event_hub.h"
56 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
57 #include "core/components_ng/pattern/text/text_layout_property.h"
58 #include "core/components_ng/pattern/text_drag/text_drag_pattern.h"
59 #include "core/components_ng/pattern/text/text_styles.h"
60 #include "core/components_ng/property/property.h"
61 #include "core/event/ace_events.h"
62 #include "core/text/text_emoji_processor.h"
63 #ifdef ENABLE_ROSEN_BACKEND
64 #include "core/components/custom_paint/rosen_render_custom_paint.h"
65 #endif
66
67 namespace OHOS::Ace::NG {
68 namespace {
69 constexpr double DIMENSION_VALUE = 16.0;
70 constexpr char COPY[] = "copy";
71 constexpr char SELECT_TEXT[] = "selectText";
72 constexpr const char SYMBOL_COLOR[] = "BLACK";
73 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
74 const std::u16string SYMBOL_TRANS = u"\uF0001";
75 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
76 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
77 }; // namespace
78
~TextPattern()79 TextPattern::~TextPattern()
80 {
81 // node destruct, need to stop text race animation
82 CHECK_NULL_VOID(contentMod_);
83 contentMod_->StopTextRace();
84 }
85
OnWindowHide()86 void TextPattern::OnWindowHide()
87 {
88 if (magnifierController_) {
89 magnifierController_->RemoveMagnifierFrameNode();
90 }
91 CHECK_NULL_VOID(contentMod_);
92 contentMod_->PauseAnimation();
93 auto host = GetHost();
94 CHECK_NULL_VOID(host);
95 TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowHide [%{public}d]", host->GetId());
96 }
97
OnWindowShow()98 void TextPattern::OnWindowShow()
99 {
100 CHECK_NULL_VOID(contentMod_);
101 contentMod_->ResumeAnimation();
102 auto host = GetHost();
103 CHECK_NULL_VOID(host);
104 TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowShow [%{public}d]", host->GetId());
105 }
106
OnAttachToFrameNode()107 void TextPattern::OnAttachToFrameNode()
108 {
109 auto pipeline = PipelineContext::GetCurrentContextSafely();
110 CHECK_NULL_VOID(pipeline);
111 pipeline_ = pipeline;
112 auto host = GetHost();
113 CHECK_NULL_VOID(host);
114 auto fontManager = pipeline->GetFontManager();
115 if (fontManager) {
116 fontManager->AddFontNodeNG(host);
117 }
118 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
119 if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
120 host->GetRenderContext()->UpdateClipEdge(true);
121 host->GetRenderContext()->SetClipToFrame(true);
122 }
123 }
124 InitSurfaceChangedCallback();
125 InitSurfacePositionChangedCallback();
126 pipeline->AddWindowStateChangedCallback(host->GetId());
127 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
128 CHECK_NULL_VOID(textLayoutProperty);
129 textLayoutProperty->UpdateTextAlign(TextAlign::START);
130 textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
131 }
132
OnDetachFromFrameNode(FrameNode * node)133 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
134 {
135 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
136 CloseSelectOverlay();
137 auto pipeline = pipeline_.Upgrade();
138 CHECK_NULL_VOID(pipeline);
139 if (HasSurfaceChangedCallback()) {
140 pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
141 }
142 if (HasSurfacePositionChangedCallback()) {
143 pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
144 }
145 auto frameNode = WeakClaim(node);
146 pipeline->RemoveFontNodeNG(frameNode);
147 auto fontManager = pipeline->GetFontManager();
148 if (fontManager) {
149 fontManager->UnRegisterCallbackNG(frameNode);
150 fontManager->RemoveVariationNodeNG(frameNode);
151 }
152 pipeline->RemoveOnAreaChangeNode(node->GetId());
153 pipeline->RemoveWindowStateChangedCallback(node->GetId());
154 pipeline->RemoveVisibleAreaChangeNode(node->GetId());
155 }
156
CloseSelectOverlay()157 void TextPattern::CloseSelectOverlay()
158 {
159 CloseSelectOverlay(false);
160 }
161
CloseSelectOverlay(bool animation)162 void TextPattern::CloseSelectOverlay(bool animation)
163 {
164 // Deprecated use selectOverlay_ instead.
165 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
166 selectOverlayProxy_->Close(animation);
167 RemoveAreaChangeInner();
168 }
169 selectOverlay_->CloseOverlay(animation, CloseReason::CLOSE_REASON_NORMAL);
170 }
171
ResetSelection()172 void TextPattern::ResetSelection()
173 {
174 if (textSelector_.IsValid()) {
175 HandleSelectionChange(-1, -1);
176 auto host = GetHost();
177 CHECK_NULL_VOID(host);
178 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
179 }
180 }
181
InitSelection(const Offset & pos)182 void TextPattern::InitSelection(const Offset& pos)
183 {
184 CHECK_NULL_VOID(pManager_);
185 int32_t extend = pManager_->GetGlyphIndexByCoordinate(pos, true);
186 int32_t start = 0;
187 int32_t end = 0;
188 if (!pManager_->GetWordBoundary(extend, start, end)) {
189 start = extend;
190 end = std::min(static_cast<int32_t>(GetWideText().length()) + placeholderCount_,
191 extend + GetGraphemeClusterLength(GetWideText(), extend));
192 }
193 HandleSelectionChange(start, end);
194 }
195
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)196 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
197 {
198 auto host = GetHost();
199 CHECK_NULL_VOID(host);
200 auto rect = host->GetGeometryNode()->GetFrameRect();
201 CHECK_NULL_VOID(pManager_);
202 auto computeSuccess = pManager_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
203 if (!computeSuccess) {
204 caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Height()), 0.0f);
205 }
206 }
207
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)208 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
209 {
210 parentGlobalOffset_ = GetParentGlobalOffset();
211 auto textContentGlobalOffset = selectOverlay_->GetHandleGlobalOffset() + contentRect_.GetOffset();
212 auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
213
214 // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
215 CaretMetricsF firstHandleMetrics;
216 CaretMetricsF secondHandleMetrics;
217 CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
218 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
219 OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
220 OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
221
222 textSelector_.selectionBaseOffset = firstHandleOffset;
223 textSelector_.selectionDestinationOffset = secondHandleOffset;
224
225 RectF firstHandle;
226 firstHandle.SetOffset(firstHandleOffset);
227 firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
228 firstHandle.SetOffset(OffsetF(firstHandle.GetX() - firstHandle.Width() / 2.0f, firstHandle.GetY()));
229 textSelector_.firstHandle = firstHandle;
230
231 RectF secondHandle;
232 secondHandle.SetOffset(secondHandleOffset);
233 secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
234 secondHandle.SetHeight(secondHandleMetrics.height);
235 secondHandle.SetOffset(OffsetF(secondHandle.GetX() - secondHandle.Width() / 2.0f, secondHandle.GetY()));
236 textSelector_.secondHandle = secondHandle;
237 }
238
GetSpansInfoInStyledString(int32_t start,int32_t end)239 std::list<ResultObject> TextPattern::GetSpansInfoInStyledString(int32_t start, int32_t end)
240 {
241 std::list<ResultObject> resultObjects;
242 int32_t imageIndex = 0;
243 for (const auto& item : spans_) {
244 auto obj = item->GetSpanResultObject(start, end);
245 if (obj.type == SelectSpanType::TYPEIMAGE) {
246 obj.spanPosition.spanIndex = imageIndex;
247 ++imageIndex;
248 }
249 if (obj.isInit) {
250 resultObjects.emplace_back(obj);
251 }
252 }
253 return resultObjects;
254 }
255
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)256 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
257 {
258 int32_t index = 0;
259 std::int32_t realEnd = 0;
260 std::int32_t realStart = 0;
261 SelectionInfo selection;
262 std::list<ResultObject> resultObjects;
263 auto length = GetTextContentLength();
264 if (method == GetSpansMethod::GETSPANS) {
265 realStart = (start == -1) ? 0 : start;
266 realEnd = (end == -1) ? length : end;
267 if (realStart > realEnd) {
268 std::swap(realStart, realEnd);
269 }
270 realStart = std::max(0, realStart);
271 realEnd = std::min(length, realEnd);
272 } else if (method == GetSpansMethod::ONSELECT) {
273 realEnd = std::min(length, end);
274 realStart = std::min(length, start);
275 }
276 selection.SetSelectionEnd(realEnd);
277 selection.SetSelectionStart(realStart);
278 if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
279 (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
280 selection.SetResultObjectList(resultObjects);
281 return selection;
282 }
283 if (isSpanStringMode_) {
284 auto result = GetSpansInfoInStyledString(realStart, realEnd);
285 selection.SetResultObjectList(result);
286 return selection;
287 }
288 const auto& children = GetAllChildren();
289 for (const auto& uinode : children) {
290 if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
291 ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
292 if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
293 resultObjects.emplace_back(resultObject);
294 }
295 } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
296 ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
297 if (!resultObject.valueString.empty()) {
298 resultObjects.emplace_back(resultObject);
299 }
300 } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
301 ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
302 if (!resultObject.valueString.empty()) {
303 resultObjects.emplace_back(resultObject);
304 }
305 } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG ||
306 uinode->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
307 ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
308 if (!resultObject.valueString.empty()) {
309 resultObjects.emplace_back(resultObject);
310 }
311 }
312 index++;
313 }
314 selection.SetResultObjectList(resultObjects);
315 return selection;
316 }
317
GetTextContentLength()318 int32_t TextPattern::GetTextContentLength()
319 {
320 if (!spans_.empty()) {
321 return static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
322 }
323 return 0;
324 }
325
StartVibratorByLongPress()326 void TextPattern::StartVibratorByLongPress()
327 {
328 CHECK_NULL_VOID(isEnableHapticFeedback_);
329 VibratorUtils::StartVibraFeedback("longPress.light");
330 }
331
HandleLongPress(GestureEvent & info)332 void TextPattern::HandleLongPress(GestureEvent& info)
333 {
334 HandleSpanLongPressEvent(info);
335 if (!IsSelectableAndCopy() || isMousePressed_ || selectOverlay_->GetIsHandleDragging()) {
336 return;
337 }
338 auto host = GetHost();
339 CHECK_NULL_VOID(host);
340 auto hub = host->GetEventHub<EventHub>();
341 CHECK_NULL_VOID(hub);
342 auto gestureHub = hub->GetOrCreateGestureEventHub();
343 CHECK_NULL_VOID(gestureHub);
344 auto localOffset = info.GetLocalLocation();
345 if (selectOverlay_->HasRenderTransform()) {
346 localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
347 }
348
349 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
350 if ((textLayoutProperty && textLayoutProperty->GetMaxLines() != 0) && GetWideText().length() != 0) {
351 StartVibratorByLongPress();
352 }
353
354 if (IsDraggable(localOffset)) {
355 dragBoxes_ = GetTextBoxes();
356 // prevent long press event from being triggered when dragging
357 gestureHub->SetIsTextDraggable(true);
358 return;
359 }
360 gestureHub->SetIsTextDraggable(false);
361 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
362 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
363 InitSelection(textOffset);
364 textResponseType_ = TextResponseType::LONG_PRESS;
365 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
366 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
367 oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
368 parentGlobalOffset_ = GetParentGlobalOffset();
369 CalculateHandleOffsetAndShowOverlay();
370 CloseSelectOverlay(true);
371 if (magnifierController_) {
372 magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
373 }
374 StartGestureSelection(textSelector_.GetStart(), textSelector_.GetEnd(), localOffset);
375 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
376 }
377
ShowShadow(const PointF & textOffset,const Color & color)378 bool TextPattern::ShowShadow(const PointF& textOffset, const Color& color)
379 {
380 CHECK_NULL_RETURN(overlayMod_, false);
381 CHECK_NULL_RETURN(hasUrlSpan_, false);
382 CHECK_NULL_RETURN(!spans_.empty() && pManager_, false);
383 int32_t start = 0;
384 for (const auto& item : spans_) {
385 if (!item) {
386 continue;
387 }
388 auto selectedRects = GetSelectedRects(start, item->position);
389 for (auto&& rect : selectedRects) {
390 if (!rect.IsInRegion(textOffset)) {
391 continue;
392 }
393 if (!item->urlOnRelease) {
394 overlayMod_->ClearSelectedForegroundColorAndRects();
395 MarkDirtySelf();
396 return false;
397 }
398 auto inter = GetStartAndEnd(start);
399 auto rects = GetSelectedRects(inter.first, inter.second);
400 overlayMod_->SetSelectedForegroundColorAndRects(rects, color.GetValue());
401 MarkDirtySelf();
402 return true;
403 }
404 start = item->position;
405 }
406 overlayMod_->ClearSelectedForegroundColorAndRects();
407 MarkDirtySelf();
408 return false;
409 }
410
GetStartAndEnd(int32_t start)411 std::pair<int32_t, int32_t> TextPattern::GetStartAndEnd(int32_t start)
412 {
413 auto spanBases = styledString_->GetSpans(0, styledString_->GetLength(), SpanType::Url);
414 for (const auto& spanBase : spanBases) {
415 if (start >= spanBase->GetStartIndex() && start < spanBase->GetEndIndex()) {
416 return {spanBase->GetStartIndex(), spanBase->GetEndIndex()};
417 }
418 }
419 return {0, 0};
420 }
421
HandleSpanLongPressEvent(GestureEvent & info)422 void TextPattern::HandleSpanLongPressEvent(GestureEvent& info)
423 {
424 RectF textContentRect = contentRect_;
425 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
426 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
427
428 auto localLocation = info.GetLocalLocation();
429 if (selectOverlay_->HasRenderTransform()) {
430 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
431 }
432
433 auto host = GetHost();
434 CHECK_NULL_VOID(host);
435 auto renderContext = host->GetRenderContext();
436 CHECK_NULL_VOID(renderContext);
437 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
438 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
439 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
440 textContentRect = overlayMod_->GetBoundsRect();
441 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
442 }
443 auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info, const RectF& rect,
444 const PointF& textOffset) -> bool {
445 if (rect.IsInRegion(textOffset)) {
446 if (item && item->onLongPress) {
447 item->onLongPress(info);
448 }
449 return true;
450 }
451 return false;
452 };
453
454 if (textContentRect.IsInRegion(
455 PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
456 !spans_.empty() && pManager_) {
457 int32_t start = 0;
458 for (const auto& item : spans_) {
459 if (!item) {
460 continue;
461 }
462 auto selectedRects = GetSelectedRects(start, item->position);
463 for (auto&& rect : selectedRects) {
464 CHECK_NULL_VOID(!longPressFunc(item, info, rect, textOffset));
465 }
466 start = item->position;
467 }
468 }
469 }
470
471 // Deprecated: Use the TextSelectOverlay::OnHandleMove() instead.
472 // It is currently used by RichEditorPattern.
OnHandleMove(const RectF & handleRect,bool isFirstHandle)473 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
474 {
475 auto host = GetHost();
476 CHECK_NULL_VOID(host);
477 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
478 auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
479
480 auto localOffset = handleRect.GetOffset();
481
482 auto renderContext = host->GetRenderContext();
483 CHECK_NULL_VOID(renderContext);
484 auto clip = false;
485 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
486 clip = true;
487 }
488 if (renderContext->GetClipEdge().value_or(clip)) {
489 if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
490 localOffset.SetX(textContentGlobalOffset.GetX());
491 } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
492 localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
493 }
494
495 if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
496 localOffset.SetY(textContentGlobalOffset.GetY());
497 } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
498 localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
499 }
500 }
501
502 localOffset -= textPaintOffset;
503
504 CHECK_NULL_VOID(pManager_);
505 // the handle position is calculated based on the middle of the handle height.
506 if (isFirstHandle) {
507 auto start = GetHandleIndex(Offset(localOffset.GetX(), localOffset.GetY() +
508 (selectOverlayProxy_->IsHandleReverse() ? handleRect.Height() : 0)));
509 HandleSelectionChange(start, textSelector_.destinationOffset);
510 } else {
511 auto end = GetHandleIndex(Offset(localOffset.GetX(),
512 localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0)
513 ? 0
514 : handleRect.Height())));
515 HandleSelectionChange(textSelector_.baseOffset, end);
516 }
517 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
518
519 CHECK_NULL_VOID(selectOverlayProxy_);
520 auto start = textSelector_.GetTextStart();
521 auto end = textSelector_.GetTextEnd();
522 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
523 }
524
IsSelectAll()525 bool TextPattern::IsSelectAll()
526 {
527 return textSelector_.GetTextStart() == 0 &&
528 textSelector_.GetTextEnd() == static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
529 }
GetWideText() const530 std::wstring TextPattern::GetWideText() const
531 {
532 return StringUtils::ToWstring(textForDisplay_);
533 }
534
GetSelectedText(int32_t start,int32_t end) const535 std::string TextPattern::GetSelectedText(int32_t start, int32_t end) const
536 {
537 if (spans_.empty()) {
538 auto wideText = GetWideText();
539 auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(wideText.length()));
540 auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(wideText.length())), 0,
541 static_cast<int32_t>(wideText.length()));
542 return StringUtils::ToString(TextEmojiProcessor::SubWstring(min, max - min, wideText));
543 }
544 std::string value;
545 int32_t tag = 0;
546 for (const auto& span : spans_) {
547 if (span->GetSymbolUnicode() != 0) {
548 tag = span->position == -1 ? tag + 1 : span->position;
549 continue;
550 }
551 if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
552 auto wideString = StringUtils::ToWstring(span->GetSpanContent());
553 auto max = std::min(span->position, end);
554 auto min = std::max(start, tag);
555 value += StringUtils::ToString(
556 wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
557 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length()))));
558 } else if (span->position - 1 >= start && span->position != -1) {
559 // image span or custom span (span->placeholderIndex != -1)
560 value += " ";
561 }
562 tag = span->position == -1 ? tag + 1 : span->position;
563 if (span->position >= end) {
564 break;
565 }
566 }
567 return value;
568 }
569
HandleOnCopy()570 void TextPattern::HandleOnCopy()
571 {
572 CHECK_NULL_VOID(clipboard_);
573 if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
574 HandleSelectionChange(-1, -1);
575 return;
576 }
577 auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
578 if (IsSelectableAndCopy() || dataDetectorAdapter_->hasClickedMenuOption_) {
579 if (isSpanStringMode_ && !externalParagraph_) {
580 HandleOnCopySpanString();
581 } else if (!value.empty()) {
582 clipboard_->SetData(value, copyOption_);
583 }
584 }
585 HiddenMenu();
586 CHECK_NULL_VOID(!value.empty());
587 auto host = GetHost();
588 CHECK_NULL_VOID(host);
589 auto eventHub = host->GetEventHub<TextEventHub>();
590 CHECK_NULL_VOID(eventHub);
591 eventHub->FireOnCopy(value);
592 }
593
HandleOnCopySpanString()594 void TextPattern::HandleOnCopySpanString()
595 {
596 auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
597 textSelector_.GetTextEnd() - textSelector_.GetTextStart());
598 #if defined(PREVIEW)
599 clipboard_->SetData(subSpanString->GetString(), copyOption_);
600 return;
601 #endif
602 RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
603 std::vector<uint8_t> tlvData;
604 subSpanString->EncodeTlv(tlvData);
605 clipboard_->AddSpanStringRecord(pasteData, tlvData);
606 clipboard_->AddTextRecord(pasteData, subSpanString->GetString());
607 clipboard_->SetData(pasteData, copyOption_);
608 }
609
HiddenMenu()610 void TextPattern::HiddenMenu()
611 {
612 if (IsUsingMouse()) {
613 CloseSelectOverlay();
614 } else {
615 selectOverlay_->HideMenu();
616 }
617 }
618
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)619 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
620 {
621 auto host = GetHost();
622 CHECK_NULL_VOID(host);
623 auto eventHub = host->GetEventHub<EventHub>();
624 CHECK_NULL_VOID(eventHub);
625 auto context = PipelineContext::GetCurrentContextSafely();
626 if (context) {
627 context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
628 auto textPattern = weak.Upgrade();
629 CHECK_NULL_VOID(textPattern);
630 auto renderContext = textPattern->GetRenderContext();
631 CHECK_NULL_VOID(renderContext);
632 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
633 bool ifHaveObscured = textPattern->GetSpanItemChildren().empty() &&
634 std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
635 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
636 auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
637 CHECK_NULL_VOID(textLayoutProperty);
638 if (textLayoutProperty->GetCalcLayoutConstraint() &&
639 textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize.has_value()) {
640 auto selfIdealSizeWidth = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Width();
641 auto selfIdealSizeHeight = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Height();
642 auto constraint = textLayoutProperty->GetLayoutConstraint();
643 if ((selfIdealSizeWidth.has_value() && NearZero(selfIdealSizeWidth->GetDimension().ConvertToPxWithSize(
644 constraint->percentReference.Width()))) ||
645 (selfIdealSizeHeight.has_value() &&
646 NearZero(selfIdealSizeHeight->GetDimension().ConvertToPxWithSize(
647 constraint->percentReference.Height())))) {
648 return;
649 }
650 }
651
652 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
653 if (mode == TextSelectableMode::UNSELECTABLE ||
654 textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
655 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
656 return;
657 }
658 if (!ifHaveObscured && eventHub->IsEnabled()) {
659 textPattern->ActSetSelection(selectionStart, selectionEnd);
660 }
661 });
662 }
663 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
664 }
665
GetRenderContext()666 RefPtr<RenderContext> TextPattern::GetRenderContext()
667 {
668 auto frameNode = GetHost();
669 CHECK_NULL_RETURN(frameNode, nullptr);
670 return frameNode->GetRenderContext();
671 }
672
MaxLinesZero()673 bool TextPattern::MaxLinesZero()
674 {
675 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
676 CHECK_NULL_RETURN(textLayoutProperty, false);
677 if (textLayoutProperty->GetMaxLines() == 0) {
678 CloseSelectOverlay();
679 ResetSelection();
680 return true;
681 }
682 return false;
683 }
684
ShowSelectOverlay(const OverlayRequest & request)685 void TextPattern::ShowSelectOverlay(const OverlayRequest& request)
686 {
687 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
688 CHECK_NULL_VOID(textLayoutProperty);
689 if (textLayoutProperty->GetMaxLines() == 0) {
690 CloseSelectOverlay();
691 ResetSelection();
692 return;
693 }
694 selectOverlay_->ProcessOverlay(request);
695 }
696
HandleOnSelectAll()697 void TextPattern::HandleOnSelectAll()
698 {
699 auto textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
700 HandleSelectionChange(0, textSize);
701 CalculateHandleOffsetAndShowOverlay();
702 CloseSelectOverlay(true);
703 if (IsUsingMouse()) {
704 if (IsSelected()) {
705 selectOverlay_->SetSelectionHoldCallback();
706 }
707 } else {
708 ShowSelectOverlay({ .animation = true });
709 }
710 auto host = GetHost();
711 CHECK_NULL_VOID(host);
712 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
713 }
714
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)715 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
716 {
717 constexpr int32_t longPressDelay = 600;
718 if (longPressEvent_) {
719 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
720 return;
721 }
722 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
723 auto pattern = weak.Upgrade();
724 CHECK_NULL_VOID(pattern);
725 pattern->sourceType_ = info.GetSourceDevice();
726 pattern->HandleLongPress(info);
727 };
728 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
729
730 // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
731 // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
732 // be slightly longer to ensure that order.
733 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
734
735 auto onTextSelectorChange = [weak = WeakClaim(this)]() {
736 auto pattern = weak.Upgrade();
737 CHECK_NULL_VOID(pattern);
738 auto frameNode = pattern->GetHost();
739 CHECK_NULL_VOID(frameNode);
740 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
741 };
742 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
743 }
744
OnHandleTouchUp()745 void TextPattern::OnHandleTouchUp()
746 {
747 CloseSelectOverlay();
748 ResetSelection();
749 }
750
HandleClickEvent(GestureEvent & info)751 void TextPattern::HandleClickEvent(GestureEvent& info)
752 {
753 if ((selectOverlay_->IsClickAtHandle(info) && !multipleClickRecognizer_->IsRunning()) ||
754 selectOverlay_->GetIsHandleDragging()) {
755 return;
756 }
757 if (dataDetectorAdapter_->hasClickedAISpan_) {
758 dataDetectorAdapter_->hasClickedAISpan_ = false;
759 }
760 multipleClickRecognizer_->Start(info);
761 if (multipleClickRecognizer_->IsDoubleClick()) {
762 HandleDoubleClickEvent(info);
763 } else {
764 HandleSingleClickEvent(info);
765 }
766 }
767
HandleUrlClick()768 bool TextPattern::HandleUrlClick()
769 {
770 if (LessNotEqual(clickedSpanPosition_, 0)) {
771 return false;
772 }
773 auto iter = spans_.begin();
774 std::advance(iter, clickedSpanPosition_);
775 RefPtr<SpanItem> span;
776 if (iter == spans_.end()) {
777 span = spans_.back();
778 } else {
779 span = *iter;
780 }
781 if (span && span->urlOnRelease) {
782 span->urlOnRelease();
783 return true;
784 }
785 return false;
786 }
787
HandleSingleClickEvent(GestureEvent & info)788 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
789 {
790 RectF textContentRect = contentRect_;
791 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
792 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
793 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
794 info.GetLocalLocation().GetY() - textContentRect.GetY() };
795 if (IsSelectableAndCopy()) {
796 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
797 }
798 if (HandleUrlClick()) {
799 return;
800 }
801 if (selectOverlay_->SelectOverlayIsOn() && !selectOverlay_->IsUsingMouse() &&
802 BetweenSelectedPosition(info.GetGlobalLocation())) {
803 if (dataDetectorAdapter_->GetCloseMenuForAISpanFlag()) {
804 selectOverlay_->EnableMenu();
805 dataDetectorAdapter_->SetCloseMenuForAISpanFlag(false);
806 return;
807 }
808 selectOverlay_->ToggleMenu();
809 selectOverlay_->SwitchToOverlayMode();
810 return;
811 }
812 if (!isMousePressed_) {
813 HandleClickAISpanEvent(textOffset);
814 }
815 if (dataDetectorAdapter_->hasClickedAISpan_) {
816 selectOverlay_->DisableMenu();
817 dataDetectorAdapter_->SetCloseMenuForAISpanFlag(true);
818 return;
819 }
820 HandleClickOnTextAndSpan(info);
821 }
822
HandleClickOnTextAndSpan(GestureEvent & info)823 void TextPattern::HandleClickOnTextAndSpan(GestureEvent& info)
824 {
825 if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE) {
826 CloseSelectOverlay(true);
827 ResetSelection();
828 }
829 if (clickedSpanPosition_ == -1) {
830 ActTextOnClick(info);
831 return;
832 }
833 auto iter = spans_.begin();
834 std::advance(iter, clickedSpanPosition_);
835 RefPtr<SpanItem> span;
836 if (iter == spans_.end()) {
837 span = spans_.back();
838 } else {
839 span = *iter;
840 }
841 if (span && span->onClick) {
842 GestureEvent spanClickinfo = info;
843 EventTarget target = info.GetTarget();
844 target.area.SetWidth(Dimension(0.0f));
845 target.area.SetHeight(Dimension(0.0f));
846 spanClickinfo.SetTarget(target);
847 span->onClick(spanClickinfo);
848 RecordSpanClickEvent(span);
849 } else {
850 ActTextOnClick(info);
851 }
852 }
853
ActTextOnClick(GestureEvent & info)854 void TextPattern::ActTextOnClick(GestureEvent& info)
855 {
856 if (onClick_) {
857 auto onClick = onClick_;
858 onClick(info);
859 RecordClickEvent();
860 }
861 }
862
RecordClickEvent()863 void TextPattern::RecordClickEvent()
864 {
865 if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
866 auto host = GetHost();
867 CHECK_NULL_VOID(host);
868 auto text = host->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetText();
869 Recorder::EventParamsBuilder builder;
870 builder.SetId(host->GetInspectorIdValue(""))
871 .SetType(host->GetTag())
872 .SetText(text)
873 .SetDescription(host->GetAutoEventParamValue(""));
874 Recorder::EventRecorder::Get().OnClick(std::move(builder));
875 }
876 }
877
RecordSpanClickEvent(const RefPtr<SpanItem> & span)878 void TextPattern::RecordSpanClickEvent(const RefPtr<SpanItem>& span)
879 {
880 if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
881 Recorder::EventParamsBuilder builder;
882 builder.SetId(span->inspectId).SetText(span->content).SetDescription(span->description);
883 Recorder::EventRecorder::Get().OnClick(std::move(builder));
884 }
885 }
886
HandleClickAISpanEvent(const PointF & textOffset)887 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
888 {
889 dataDetectorAdapter_->hasClickedAISpan_ = false;
890 if (!NeedShowAIDetect() || mouseStatus_ == MouseStatus::MOVE || IsDragging()) {
891 return;
892 }
893
894 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
895 auto& aiSpan = kv.second;
896 ClickAISpan(textOffset, aiSpan);
897 if (dataDetectorAdapter_->hasClickedAISpan_) {
898 return;
899 }
900 }
901 }
902
CheckClickedOnSpanOrText(RectF textContentRect,const Offset & localLocation)903 bool TextPattern::CheckClickedOnSpanOrText(RectF textContentRect, const Offset& localLocation)
904 {
905 clickedSpanPosition_ = -1;
906 auto host = GetHost();
907 CHECK_NULL_RETURN(host, false);
908 auto renderContext = host->GetRenderContext();
909 CHECK_NULL_RETURN(host, false);
910 PointF textOffset = GetTextOffset(localLocation, textContentRect);
911 auto clip = false;
912 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
913 clip = true;
914 }
915 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value_or(clip) && overlayMod_) {
916 textContentRect = overlayMod_->GetBoundsRect();
917 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
918 }
919 if (textContentRect.IsInRegion(
920 PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
921 !spans_.empty() && pManager_) {
922 if (CalculateClickedSpanPosition(textOffset)) {
923 return true;
924 }
925 }
926 if (onClick_) {
927 return true;
928 }
929 return false;
930 }
931
GetTextOffset(const Offset & localLocation,const RectF & contentRect)932 PointF TextPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
933 {
934 PointF textOffset = {static_cast<float>(localLocation.GetX()) - contentRect.GetX(),
935 static_cast<float>(localLocation.GetY()) - contentRect.GetY()};
936 return textOffset;
937 }
938
CalculateClickedSpanPosition(const PointF & textOffset)939 bool TextPattern::CalculateClickedSpanPosition(const PointF& textOffset)
940 {
941 int32_t start = 0;
942 for (const auto& item : spans_) {
943 clickedSpanPosition_++;
944 if (!item) {
945 continue;
946 }
947 auto selectedRects = GetSelectedRects(start, item->position);
948 start = item->position;
949 for (auto&& rect : selectedRects) {
950 if (!rect.IsInRegion(textOffset)) {
951 continue;
952 }
953 return CheckAndClick(item);
954 }
955 }
956 clickedSpanPosition_ = -1;
957 return false;
958 }
959
CheckAndClick(const RefPtr<SpanItem> & item)960 bool TextPattern::CheckAndClick(const RefPtr<SpanItem>& item)
961 {
962 if (item->onClick || item->urlOnRelease) {
963 return true;
964 }
965 clickedSpanPosition_ = -1;
966 return false;
967 }
968
GetSelectedRects(int32_t start,int32_t end)969 std::vector<RectF> TextPattern::GetSelectedRects(int32_t start, int32_t end)
970 {
971 return pManager_->GetRects(start, end);
972 }
973
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)974 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
975 {
976 auto aiRects = pManager_->GetRects(aiSpan.start, aiSpan.end);
977 for (auto&& rect : aiRects) {
978 if (rect.IsInRegion(textOffset)) {
979 dataDetectorAdapter_->hasClickedAISpan_ = true;
980 if (leftMousePressed_) {
981 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
982 return true;
983 }
984 return ShowAIEntityMenu(aiSpan);
985 }
986 }
987 return false;
988 }
989
InitUrlMouseEvent()990 void TextPattern::InitUrlMouseEvent()
991 {
992 CHECK_NULL_VOID(!urlMouseEventInitialized_);
993 auto host = GetHost();
994 CHECK_NULL_VOID(host);
995 auto eventHub = host->GetEventHub<EventHub>();
996 CHECK_NULL_VOID(eventHub);
997 auto inputHub = eventHub->GetOrCreateInputEventHub();
998 CHECK_NULL_VOID(inputHub);
999 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1000 auto pattern = weak.Upgrade();
1001 if (pattern) {
1002 pattern->HandleUrlMouseEvent(info);
1003 }
1004 };
1005 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1006 inputHub->AddOnMouseEvent(mouseEvent);
1007 auto mouseHoverTask = [weak = WeakClaim(this)](bool isHover) {
1008 auto pattern = weak.Upgrade();
1009 CHECK_NULL_VOID(pattern);
1010 pattern->URLOnHover(isHover);
1011 };
1012 auto mouseHoverEvent = MakeRefPtr<InputEvent>(std::move(mouseHoverTask));
1013 inputHub->AddOnHoverEvent(mouseHoverEvent);
1014 urlMouseEventInitialized_ = true;
1015 }
1016
URLOnHover(bool isHover)1017 void TextPattern::URLOnHover(bool isHover)
1018 {
1019 CHECK_NULL_VOID(!isHover);
1020 auto host = GetHost();
1021 CHECK_NULL_VOID(host);
1022 auto nodeId = host->GetId();
1023 auto pipelineContext = PipelineContext::GetCurrentContextSafely();
1024 CHECK_NULL_VOID(pipelineContext);
1025 pipelineContext->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1026 pipelineContext->FreeMouseStyleHoldNode(nodeId);
1027 CHECK_NULL_VOID(overlayMod_);
1028 overlayMod_->ClearSelectedForegroundColorAndRects();
1029 MarkDirtySelf();
1030 }
1031
HandleUrlMouseEvent(const MouseInfo & info)1032 void TextPattern::HandleUrlMouseEvent(const MouseInfo& info)
1033 {
1034 RectF textContentRect = contentRect_;
1035 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1036 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1037 auto localLocation = info.GetLocalLocation();
1038 if (selectOverlay_->HasRenderTransform()) {
1039 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
1040 }
1041 auto host = GetHost();
1042 CHECK_NULL_VOID(host);
1043 auto hostId = host->GetId();
1044 auto renderContext = host->GetRenderContext();
1045 CHECK_NULL_VOID(renderContext);
1046 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
1047 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
1048 auto show = ShowShadow(textOffset, GetUrlHoverColor());
1049 auto pipelineContext = PipelineContext::GetCurrentContextSafely();
1050 CHECK_NULL_VOID(pipelineContext);
1051 if (show) {
1052 pipelineContext->SetMouseStyleHoldNode(hostId);
1053 pipelineContext->ChangeMouseStyle(hostId, MouseFormat::HAND_POINTING);
1054 } else {
1055 pipelineContext->ChangeMouseStyle(hostId, MouseFormat::DEFAULT);
1056 pipelineContext->FreeMouseStyleHoldNode(hostId);
1057 }
1058 }
1059
HandleUrlTouchEvent(const TouchEventInfo & info)1060 void TextPattern::HandleUrlTouchEvent(const TouchEventInfo& info)
1061 {
1062 CHECK_NULL_VOID(overlayMod_);
1063 CHECK_NULL_VOID(!IsDragging());
1064 if (selectOverlay_->IsTouchAtHandle(info)) {
1065 return;
1066 }
1067 auto touchType = info.GetTouches().front().GetTouchType();
1068 if (touchType != TouchType::DOWN && touchType != TouchType::UP) {
1069 return;
1070 }
1071 if (touchType == TouchType::DOWN) {
1072 RectF textContentRect = contentRect_;
1073 auto touchOffset = info.GetTouches().front().GetLocalLocation();
1074 PointF textOffset = { static_cast<float>(touchOffset.GetX()) - textContentRect.GetX(),
1075 static_cast<float>(touchOffset.GetY()) - textContentRect.GetY() };
1076 ShowShadow(textOffset, GetUrlPressColor());
1077 } else {
1078 overlayMod_->ClearSelectedForegroundColorAndRects();
1079 MarkDirtySelf();
1080 }
1081 }
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1082 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1083 const ShowSelectOverlayFunc& showSelectOverlayFunc)
1084
1085 {
1086 dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc](
1087 const std::string& action) {
1088 auto pattern = weak.Upgrade();
1089 CHECK_NULL_VOID(pattern);
1090 pattern->CloseSelectOverlay();
1091 pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
1092 if (action == COPY) {
1093 pattern->HandleOnCopy();
1094 pattern->ResetSelection();
1095 } else if (action == SELECT_TEXT) {
1096 if (calculateHandleFunc == nullptr) {
1097 pattern->CalculateHandleOffsetAndShowOverlay();
1098 } else {
1099 calculateHandleFunc();
1100 }
1101 if (showSelectOverlayFunc == nullptr) {
1102 pattern->ShowSelectOverlay({ .animation = true });
1103 } else {
1104 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
1105 }
1106 auto frameNode = pattern->GetHost();
1107 CHECK_NULL_VOID(frameNode);
1108 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1109 }
1110 };
1111 }
1112
ShowAIEntityMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1113 bool TextPattern::ShowAIEntityMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1114 const ShowSelectOverlayFunc& showSelectOverlayFunc)
1115 {
1116 auto host = GetHost();
1117 CHECK_NULL_RETURN(host, false);
1118 SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
1119 auto baseOffset = textSelector_.baseOffset;
1120 auto destinationOffset = textSelector_.destinationOffset;
1121 HandleSelectionChange(aiSpan.start, aiSpan.end);
1122 parentGlobalOffset_ = GetParentGlobalOffset();
1123 if (calculateHandleFunc == nullptr) {
1124 CalculateHandleOffsetAndShowOverlay();
1125 } else {
1126 calculateHandleFunc();
1127 }
1128 HandleSelectionChange(baseOffset, destinationOffset);
1129 RectF aiRect;
1130 if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
1131 auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
1132 auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
1133 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
1134 auto left = textContentGlobalOffset.GetX();
1135 auto right = textContentGlobalOffset.GetY() + contentRect_.Width();
1136 aiRect = RectT(left, top, right - left, bottom - top);
1137 } else {
1138 aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
1139 }
1140 if (calculateHandleFunc == nullptr) {
1141 CalculateHandleOffsetAndShowOverlay();
1142 }
1143 bool isShowCopy = true;
1144 bool isShowSelectText = true;
1145 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1146 CHECK_NULL_RETURN(textLayoutProperty, false);
1147 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1148 if (copyOption_ == CopyOptions::None) {
1149 isShowCopy = false;
1150 isShowSelectText = false;
1151 } else if (mode == TextSelectableMode::UNSELECTABLE) {
1152 isShowSelectText = false;
1153 }
1154 return dataDetectorAdapter_->ShowAIEntityMenu(aiSpan, aiRect, host, isShowCopy, isShowSelectText);
1155 }
1156
HandleDoubleClickEvent(GestureEvent & info)1157 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
1158 {
1159 CheckOnClickEvent(info);
1160 if (!IsSelectableAndCopy() || textForDisplay_.empty()) {
1161 return;
1162 }
1163 auto host = GetHost();
1164 CHECK_NULL_VOID(host);
1165 auto hub = host->GetEventHub<EventHub>();
1166 CHECK_NULL_VOID(hub);
1167 auto gestureHub = hub->GetOrCreateGestureEventHub();
1168 CHECK_NULL_VOID(gestureHub);
1169 isDoubleClick_ = true;
1170 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1171 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1172 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1173 InitSelection(textOffset);
1174 textResponseType_ = TextResponseType::NONE;
1175 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
1176 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
1177 parentGlobalOffset_ = GetParentGlobalOffset();
1178 CalculateHandleOffsetAndShowOverlay();
1179 if (!isMousePressed_) {
1180 ShowSelectOverlay({ .animation = true });
1181 }
1182 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1183 }
1184
CheckOnClickEvent(GestureEvent & info)1185 void TextPattern::CheckOnClickEvent(GestureEvent& info)
1186 {
1187 RectF textContentRect = contentRect_;
1188 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1189 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1190 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
1191 info.GetLocalLocation().GetY() - textContentRect.GetY() };
1192 if (IsSelectableAndCopy()) {
1193 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
1194 }
1195 HandleClickOnTextAndSpan(info);
1196 }
1197
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)1198 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
1199 {
1200 CHECK_NULL_VOID(!clickEventInitialized_);
1201 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1202 auto pattern = weak.Upgrade();
1203 CHECK_NULL_VOID(pattern);
1204 pattern->sourceType_ = info.GetSourceDevice();
1205 pattern->HandleClickEvent(info);
1206 };
1207
1208 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
1209 clickListener->SetSysJudge([weak = WeakClaim(this)](const RefPtr<GestureInfo>& gestureInfo,
1210 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1211 auto textPattern = weak.Upgrade();
1212 CHECK_NULL_RETURN(textPattern, GestureJudgeResult::CONTINUE);
1213 if (info->GetFingerList().empty()) {
1214 return GestureJudgeResult::CONTINUE;
1215 }
1216 auto localLocation = info->GetFingerList().begin()->localLocation_;
1217 auto contentRect = textPattern->GetTextContentRect();
1218 auto baselineOffset = textPattern->GetBaselineOffset();
1219
1220 RectF textContentRect = contentRect;
1221 textContentRect.SetTop(contentRect.GetY() - std::min(baselineOffset, 0.0f));
1222 textContentRect.SetHeight(contentRect.Height() - std::max(baselineOffset, 0.0f));
1223 if (textPattern->GetCopyOptions() == CopyOptions::None && !textPattern->NeedShowAIDetect() &&
1224 !textPattern->CheckClickedOnSpanOrText(textContentRect, localLocation)) {
1225 return GestureJudgeResult::REJECT;
1226 }
1227 return GestureJudgeResult::CONTINUE;
1228 });
1229 gestureHub->AddClickEvent(clickListener);
1230 clickEventInitialized_ = true;
1231 }
1232
InitMouseEvent()1233 void TextPattern::InitMouseEvent()
1234 {
1235 CHECK_NULL_VOID(!mouseEventInitialized_);
1236 auto host = GetHost();
1237 CHECK_NULL_VOID(host);
1238 auto eventHub = host->GetEventHub<EventHub>();
1239 CHECK_NULL_VOID(eventHub);
1240 auto inputHub = eventHub->GetOrCreateInputEventHub();
1241 CHECK_NULL_VOID(inputHub);
1242
1243 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1244 auto pattern = weak.Upgrade();
1245 CHECK_NULL_VOID(pattern);
1246 pattern->HandleMouseEvent(info);
1247 };
1248 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1249 inputHub->AddOnMouseEvent(mouseEvent);
1250 mouseEventInitialized_ = true;
1251 }
1252
HandleMouseEvent(const MouseInfo & info)1253 void TextPattern::HandleMouseEvent(const MouseInfo& info)
1254 {
1255 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1256 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1257 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1258 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
1259 HandleMouseLeftButton(info, textOffset);
1260 if (IsSelected()) {
1261 selectOverlay_->SetSelectionHoldCallback();
1262 }
1263 sourceType_ = info.GetSourceDevice();
1264 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
1265 HandleMouseRightButton(info, textOffset);
1266 sourceType_ = info.GetSourceDevice();
1267 }
1268 }
1269
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)1270 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
1271 {
1272 if (info.GetAction() == MouseAction::PRESS) {
1273 HandleMouseLeftPressAction(info, textOffset);
1274 } else if (info.GetAction() == MouseAction::MOVE) {
1275 HandleMouseLeftMoveAction(info, textOffset);
1276 } else if (info.GetAction() == MouseAction::RELEASE) {
1277 HandleMouseLeftReleaseAction(info, textOffset);
1278 }
1279
1280 auto host = GetHost();
1281 CHECK_NULL_VOID(host);
1282 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1283 }
1284
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)1285 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
1286 {
1287 isMousePressed_ = true;
1288 leftMousePressed_ = true;
1289 if (BetweenSelectedPosition(info.GetGlobalLocation())) {
1290 blockPress_ = true;
1291 return;
1292 }
1293 mouseStatus_ = MouseStatus::PRESSED;
1294 CHECK_NULL_VOID(pManager_);
1295 auto start = pManager_->GetGlyphIndexByCoordinate(textOffset);
1296 textSelector_.Update(start, start);
1297 }
1298
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)1299 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
1300 {
1301 if (blockPress_) {
1302 blockPress_ = false;
1303 }
1304 auto oldMouseStatus = mouseStatus_;
1305 mouseStatus_ = MouseStatus::RELEASED;
1306 if (isDoubleClick_) {
1307 isDoubleClick_ = false;
1308 isMousePressed_ = false;
1309 leftMousePressed_ = false;
1310 return;
1311 }
1312 if (oldMouseStatus != MouseStatus::MOVE && !IsDragging()) {
1313 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1314 if (dataDetectorAdapter_->hasClickedAISpan_) {
1315 selectOverlay_->DisableMenu();
1316 isMousePressed_ = false;
1317 leftMousePressed_ = false;
1318 return;
1319 }
1320 }
1321
1322 CHECK_NULL_VOID(pManager_);
1323 auto start = textSelector_.baseOffset;
1324 auto end = textSelector_.destinationOffset;
1325 if (!IsSelected()) {
1326 start = -1;
1327 end = -1;
1328 }
1329 if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE) {
1330 HandleSelectionChange(start, end);
1331 }
1332
1333 if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
1334 selectOverlay_->SetMouseMenuOffset(OffsetF(
1335 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
1336 textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
1337 ShowSelectOverlay({ .animation = true });
1338 }
1339 isMousePressed_ = false;
1340 leftMousePressed_ = false;
1341 }
1342
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)1343 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
1344 {
1345 if (!IsSelectableAndCopy()) {
1346 isMousePressed_ = false;
1347 leftMousePressed_ = false;
1348 return;
1349 }
1350 if (blockPress_) {
1351 dragBoxes_ = GetTextBoxes();
1352 return;
1353 }
1354 if (isMousePressed_) {
1355 mouseStatus_ = MouseStatus::MOVE;
1356 CHECK_NULL_VOID(pManager_);
1357 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
1358 HandleSelectionChange(textSelector_.baseOffset, end);
1359 }
1360 }
1361
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)1362 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
1363 {
1364 if (info.GetAction() == MouseAction::RELEASE) {
1365 selectOverlay_->SetMouseMenuOffset(OffsetF(
1366 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
1367 if (!BetweenSelectedPosition(info.GetGlobalLocation())) {
1368 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1369 if (dataDetectorAdapter_->hasClickedAISpan_) {
1370 isMousePressed_ = false;
1371 return;
1372 }
1373 }
1374 if (!IsSelectableAndCopy()) {
1375 return;
1376 }
1377
1378 CalculateHandleOffsetAndShowOverlay(true);
1379 if (selectOverlay_->SelectOverlayIsOn()) {
1380 CloseSelectOverlay(true);
1381 }
1382 textResponseType_ = TextResponseType::RIGHT_CLICK;
1383 if (!IsSelected()) {
1384 auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
1385 if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
1386 selectedType_ = TextSpanType::IMAGE;
1387 } else {
1388 selectedType_ = TextSpanType::TEXT;
1389 }
1390 }
1391 ShowSelectOverlay({ .animation = true });
1392 isMousePressed_ = false;
1393 auto host = GetHost();
1394 CHECK_NULL_VOID(host);
1395 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1396 } else if (info.GetAction() == MouseAction::PRESS) {
1397 isMousePressed_ = true;
1398 CloseSelectOverlay(true);
1399 }
1400 }
1401
InitTouchEvent()1402 void TextPattern::InitTouchEvent()
1403 {
1404 CHECK_NULL_VOID(!touchEventInitialized_);
1405 auto host = GetHost();
1406 CHECK_NULL_VOID(host);
1407 auto gesture = host->GetOrCreateGestureEventHub();
1408 CHECK_NULL_VOID(gesture);
1409
1410 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1411 auto pattern = weak.Upgrade();
1412 CHECK_NULL_VOID(pattern);
1413 pattern->sourceType_ = info.GetSourceDevice();
1414 pattern->HandleTouchEvent(info);
1415 };
1416 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
1417 gesture->AddTouchEvent(touchListener);
1418 touchEventInitialized_ = true;
1419 }
1420
InitUrlTouchEvent()1421 void TextPattern::InitUrlTouchEvent()
1422 {
1423 CHECK_NULL_VOID(!urlTouchEventInitialized_);
1424 auto host = GetHost();
1425 CHECK_NULL_VOID(host);
1426 auto gesture = host->GetOrCreateGestureEventHub();
1427 CHECK_NULL_VOID(gesture);
1428
1429 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1430 auto pattern = weak.Upgrade();
1431 CHECK_NULL_VOID(pattern);
1432 pattern->HandleUrlTouchEvent(info);
1433 };
1434 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
1435 gesture->AddTouchEvent(touchListener);
1436 urlTouchEventInitialized_ = true;
1437 }
1438
MarkDirtySelf()1439 void TextPattern::MarkDirtySelf()
1440 {
1441 auto host = GetHost();
1442 CHECK_NULL_VOID(host);
1443 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1444 }
1445
HandleTouchEvent(const TouchEventInfo & info)1446 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
1447 {
1448 DoGestureSelection(info);
1449 }
1450
InitKeyEvent()1451 void TextPattern::InitKeyEvent()
1452 {
1453 CHECK_NULL_VOID(!keyEventInitialized_);
1454 auto host = GetHost();
1455 CHECK_NULL_VOID(host);
1456 auto focusHub = host->GetOrCreateFocusHub();
1457 CHECK_NULL_VOID(focusHub);
1458
1459 auto keyTask = [weak = WeakClaim(this)](const KeyEvent& event) -> bool {
1460 auto pattern = weak.Upgrade();
1461 CHECK_NULL_RETURN(pattern, false);
1462 return pattern->HandleKeyEvent(event);
1463 };
1464 focusHub->SetOnKeyEventInternal(std::move(keyTask));
1465 keyEventInitialized_ = true;
1466 }
1467
HandleKeyEvent(const KeyEvent & keyEvent)1468 bool TextPattern::HandleKeyEvent(const KeyEvent& keyEvent)
1469 {
1470 if (keyEvent.action != KeyAction::DOWN) {
1471 return false;
1472 }
1473
1474 if (keyEvent.IsCtrlWith(KeyCode::KEY_C)) {
1475 HandleOnCopy();
1476 return true;
1477 }
1478
1479 if (keyEvent.IsShiftWith(keyEvent.code)) {
1480 HandleOnSelect(keyEvent.code);
1481 return true;
1482 }
1483 return false;
1484 }
1485
HandleOnSelect(KeyCode code)1486 void TextPattern::HandleOnSelect(KeyCode code)
1487 {
1488 auto end = textSelector_.GetEnd();
1489 switch (code) {
1490 case KeyCode::KEY_DPAD_LEFT: {
1491 HandleSelection(true, end - 1);
1492 break;
1493 }
1494 case KeyCode::KEY_DPAD_RIGHT: {
1495 HandleSelection(false, end + 1);
1496 break;
1497 }
1498 case KeyCode::KEY_DPAD_UP: {
1499 HandleSelectionUp();
1500 break;
1501 }
1502 case KeyCode::KEY_DPAD_DOWN: {
1503 HandleSelectionDown();
1504 break;
1505 }
1506 default:
1507 break;
1508 }
1509 }
1510
HandleSelectionUp()1511 void TextPattern::HandleSelectionUp()
1512 {
1513 auto end = textSelector_.GetEnd();
1514 auto line = pManager_->GetLineCount();
1515 if (line == 1) {
1516 HandleSelection(true, 0);
1517 return;
1518 }
1519 CaretMetricsF secondHandleMetrics;
1520 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
1521 auto secondOffsetX = secondHandleMetrics.offset.GetX();
1522 auto secondOffsetY = secondHandleMetrics.offset.GetY();
1523 double height = GetTextHeight(end, false);
1524 Offset offset = { secondOffsetX, secondOffsetY - height * 0.5 };
1525 auto caculateIndex = GetHandleIndex(offset);
1526 if (end == caculateIndex) {
1527 caculateIndex = 0;
1528 }
1529 HandleSelection(true, caculateIndex);
1530 }
1531
HandleSelectionDown()1532 void TextPattern::HandleSelectionDown()
1533 {
1534 auto end = textSelector_.GetEnd();
1535 auto line = pManager_->GetLineCount();
1536 auto lastIndex = GetActualTextLength();
1537 if (line == 1) {
1538 HandleSelection(true, lastIndex);
1539 return;
1540 }
1541 CaretMetricsF secondHandleMetrics;
1542 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
1543 auto secondOffsetX = secondHandleMetrics.offset.GetX();
1544 double height = GetTextHeight(end, true);
1545 auto caculateIndex = GetHandleIndex({ secondOffsetX, height });
1546 if (NearZero(height) || caculateIndex == end || caculateIndex > lastIndex) {
1547 caculateIndex = lastIndex;
1548 }
1549 HandleSelection(true, caculateIndex);
1550 }
1551
HandleSelection(bool isEmojiStart,int32_t end)1552 void TextPattern::HandleSelection(bool isEmojiStart, int32_t end)
1553 {
1554 auto start = textSelector_.GetStart();
1555 auto lastIndex = GetActualTextLength();
1556 if (start < 0 || start > lastIndex || end < 0 || end > lastIndex) {
1557 return;
1558 }
1559 int32_t emojiStartIndex;
1560 int32_t emojiEndIndex;
1561 bool isIndexInEmoji = TextEmojiProcessor::IsIndexInEmoji(end, GetSelectedText(0, lastIndex),
1562 emojiStartIndex, emojiEndIndex);
1563 if (isIndexInEmoji) {
1564 end = isEmojiStart ? emojiStartIndex : emojiEndIndex;
1565 }
1566 HandleSelectionChange(start, end);
1567 CalculateHandleOffsetAndShowOverlay();
1568 CloseSelectOverlay(true);
1569 auto host = GetHost();
1570 CHECK_NULL_VOID(host);
1571 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1572 }
1573
GetTextHeight(int32_t index,bool isNextLine)1574 double TextPattern::GetTextHeight(int32_t index, bool isNextLine)
1575 {
1576 auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
1577 auto lineHeight = 0.0;
1578 for (auto lineNumber = 0; lineNumber < lineCount; lineNumber++) {
1579 auto lineMetrics = GetLineMetrics(lineNumber);
1580 auto startIndex = static_cast<int32_t>(lineMetrics.startIndex);
1581 auto endIndex = static_cast<int32_t>(lineMetrics.endIndex);
1582 lineHeight += lineMetrics.height;
1583 if (isNextLine) {
1584 if (index <= endIndex && endIndex != GetActualTextLength()) {
1585 return lineHeight;
1586 }
1587 } else {
1588 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1589 CHECK_NULL_RETURN(textLayoutProperty, 0);
1590 auto maxLines = textLayoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
1591 if ((index <= endIndex && startIndex != 0) ||
1592 ((lineNumber + 1) == static_cast<int32_t>(maxLines) && lineNumber != 0)) {
1593 return GetLineMetrics(lineNumber - 1).height;
1594 }
1595 }
1596 }
1597 return 0.0;
1598 }
1599
GetActualTextLength()1600 int32_t TextPattern::GetActualTextLength()
1601 {
1602 auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
1603 return GetLineMetrics(lineCount - 1).endIndex;
1604 }
1605
SetTextSelectableMode(TextSelectableMode value)1606 void TextPattern::SetTextSelectableMode(TextSelectableMode value)
1607 {
1608 auto host = GetHost();
1609 CHECK_NULL_VOID(host);
1610 auto focusHub = host->GetOrCreateFocusHub();
1611 CHECK_NULL_VOID(focusHub);
1612 if (value == TextSelectableMode::SELECTABLE_FOCUSABLE) {
1613 focusHub->SetFocusable(true);
1614 focusHub->SetIsFocusOnTouch(true);
1615 } else {
1616 focusHub->SetFocusable(false);
1617 focusHub->SetIsFocusOnTouch(false);
1618 }
1619 }
1620
IsSelectableAndCopy()1621 bool TextPattern::IsSelectableAndCopy()
1622 {
1623 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1624 CHECK_NULL_RETURN(textLayoutProperty, false);
1625 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1626 return mode != TextSelectableMode::UNSELECTABLE && copyOption_ != CopyOptions::None;
1627 }
1628
IsDraggable(const Offset & offset)1629 bool TextPattern::IsDraggable(const Offset& offset)
1630 {
1631 auto host = GetHost();
1632 CHECK_NULL_RETURN(host, false);
1633 auto eventHub = host->GetEventHub<EventHub>();
1634 bool draggable = eventHub->HasOnDragStart();
1635 if (IsSelectableAndCopy() && draggable &&
1636 GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
1637 // Determine if the pan location is in the selected area
1638 auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1639 TextBase::CalculateSelectedRect(selectedRects, contentRect_.Width());
1640 auto panOffset = OffsetF(offset.GetX(), offset.GetY()) - contentRect_.GetOffset() +
1641 OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1642 for (const auto& selectedRect : selectedRects) {
1643 if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1644 return true;
1645 }
1646 }
1647 }
1648 return false;
1649 }
1650
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)1651 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
1652 {
1653 DragDropInfo itemInfo;
1654 auto host = GetHost();
1655 CHECK_NULL_RETURN(host, itemInfo);
1656 if (overlayMod_) {
1657 overlayMod_->ClearSelectedForegroundColorAndRects();
1658 }
1659 auto hub = host->GetEventHub<EventHub>();
1660 auto gestureHub = hub->GetOrCreateGestureEventHub();
1661 auto selectStart = textSelector_.GetTextStart();
1662 auto selectEnd = textSelector_.GetTextEnd();
1663 recoverStart_ = selectStart;
1664 recoverEnd_ = selectEnd;
1665 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1666 dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
1667 ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
1668 status_ = Status::DRAGGING;
1669 if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
1670 return itemInfo;
1671 }
1672 auto data = event->GetData();
1673 if (!data) {
1674 AddUdmfData(event);
1675 }
1676 CloseOperate();
1677 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1678 return itemInfo;
1679 }
1680
AddUdmfTxtPreProcessor(const ResultObject src,ResultObject & result,bool isAppend)1681 void TextPattern::AddUdmfTxtPreProcessor(const ResultObject src, ResultObject& result, bool isAppend)
1682 {
1683 auto valueString = GetSelectedSpanText(StringUtils::ToWstring(src.valueString),
1684 src.offsetInSpan[RichEditorSpanRange::RANGESTART], src.offsetInSpan[RichEditorSpanRange::RANGEEND]);
1685 if (isAppend) {
1686 result.valueString = result.valueString + valueString;
1687 } else {
1688 result.valueString = valueString;
1689 }
1690 }
1691
AddUdmfData(const RefPtr<Ace::DragEvent> & event)1692 void TextPattern::AddUdmfData(const RefPtr<Ace::DragEvent>& event)
1693 {
1694 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
1695 if (isSpanStringMode_) {
1696 std::vector<uint8_t> arr;
1697 auto dragSpanString = styledString_->GetSubSpanString(recoverStart_, recoverEnd_ - recoverStart_);
1698 dragSpanString->EncodeTlv(arr);
1699 UdmfClient::GetInstance()->AddSpanStringRecord(unifiedData, arr);
1700 } else {
1701 ProcessNormalUdmfData(unifiedData);
1702 }
1703 event->SetData(unifiedData);
1704 }
1705
ProcessNormalUdmfData(const RefPtr<UnifiedData> & unifiedData)1706 void TextPattern::ProcessNormalUdmfData(const RefPtr<UnifiedData>& unifiedData)
1707 {
1708 std::list<ResultObject> finalResult;
1709 auto type = SelectSpanType::TYPESPAN;
1710 for (const auto& resultObj : dragResultObjects_) {
1711 if (finalResult.empty() || resultObj.type != SelectSpanType::TYPESPAN || type != SelectSpanType::TYPESPAN) {
1712 type = resultObj.type;
1713 finalResult.emplace_back(resultObj);
1714 if (resultObj.type == SelectSpanType::TYPESPAN) {
1715 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), false);
1716 }
1717 } else {
1718 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), true);
1719 }
1720 }
1721 auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
1722 auto pattern = weak.Upgrade();
1723 CHECK_NULL_VOID(pattern);
1724 if (result.type == SelectSpanType::TYPESPAN) {
1725 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, result.valueString);
1726 return;
1727 }
1728 if (result.type == SelectSpanType::TYPEIMAGE) {
1729 if (result.valuePixelMap) {
1730 pattern->AddPixelMapToUdmfData(result.valuePixelMap, unifiedData);
1731 } else if (result.valueString.size() > 1) {
1732 UdmfClient::GetInstance()->AddImageRecord(unifiedData, result.valueString);
1733 } else {
1734 // builder span, fill pixelmap data
1735 auto builderNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(result.spanPosition.spanIndex));
1736 CHECK_NULL_VOID(builderNode);
1737 pattern->AddPixelMapToUdmfData(builderNode->GetPixelMap(), unifiedData);
1738 }
1739 }
1740 };
1741 for (const auto& resultObj : finalResult) {
1742 resultProcessor(resultObj);
1743 }
1744 }
1745
AddPixelMapToUdmfData(const RefPtr<PixelMap> & pixelMap,const RefPtr<UnifiedData> & unifiedData)1746 void TextPattern::AddPixelMapToUdmfData(const RefPtr<PixelMap>& pixelMap, const RefPtr<UnifiedData>& unifiedData)
1747 {
1748 CHECK_NULL_VOID(pixelMap && unifiedData);
1749 const uint8_t* pixels = pixelMap->GetPixels();
1750 CHECK_NULL_VOID(pixels);
1751 int32_t length = pixelMap->GetByteCount();
1752 std::vector<uint8_t> data(pixels, pixels + length);
1753 PixelMapRecordDetails details = { pixelMap->GetWidth(), pixelMap->GetHeight(),
1754 pixelMap->GetPixelFormat(), pixelMap->GetAlphaType() };
1755 UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
1756 }
1757
CloseOperate()1758 void TextPattern::CloseOperate()
1759 {
1760 UpdateSpanItemDragStatus(dragResultObjects_, true);
1761 recoverDragResultObjects_ = dragResultObjects_;
1762 AceEngineExt::GetInstance().DragStartExt();
1763 CloseKeyboard(true);
1764 CloseSelectOverlay();
1765 ResetSelection();
1766 }
1767
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)1768 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
1769 {
1770 auto weakPtr = WeakClaim(this);
1771 DragDropInfo itemInfo;
1772 auto pattern = weakPtr.Upgrade();
1773 auto host = pattern->GetHost();
1774 auto hub = host->GetEventHub<EventHub>();
1775 auto gestureHub = hub->GetOrCreateGestureEventHub();
1776 CHECK_NULL_RETURN(gestureHub, itemInfo);
1777 if (!gestureHub->GetIsTextDraggable()) {
1778 return itemInfo;
1779 }
1780 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
1781 pattern->status_ = Status::DRAGGING;
1782 pattern->contentMod_->ChangeDragStatus();
1783 pattern->showSelect_ = false;
1784 auto start = textSelector_.GetTextStart();
1785 pattern->recoverStart_ = start;
1786 auto end = textSelector_.GetTextEnd();
1787 pattern->recoverEnd_ = end;
1788 auto beforeStr = GetSelectedText(0, start);
1789 auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1790 auto afterStr = GetSelectedText(end, GetWideText().length());
1791 pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
1792
1793 itemInfo.extraInfo = selectedStr;
1794 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
1795 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedStr);
1796 event->SetData(unifiedData);
1797 host->MarkDirtyNode(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1 ? PROPERTY_UPDATE_MEASURE_SELF
1798 : PROPERTY_UPDATE_MEASURE);
1799
1800 CloseSelectOverlay();
1801 ResetSelection();
1802 return itemInfo;
1803 }
1804
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)1805 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
1806 {
1807 if (resultObjects.empty()) {
1808 return;
1809 }
1810 auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
1811 auto pattern = weakPtr.Upgrade();
1812 CHECK_NULL_VOID(pattern);
1813 if (pattern->spans_.empty()) {
1814 return;
1815 }
1816 auto it = pattern->spans_.begin();
1817 if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
1818 std::advance(it, !pattern->spans_.empty() ? static_cast<int32_t>(pattern->spans_.size()) - 1 : 0);
1819 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
1820 } else {
1821 std::advance(it, resultObj.spanPosition.spanIndex);
1822 }
1823 auto spanItem = *it;
1824 CHECK_NULL_VOID(spanItem);
1825 if (resultObj.type == SelectSpanType::TYPESPAN) {
1826 if (pattern->isSpanStringMode_) {
1827 spanItem = resultObj.span.Upgrade();
1828 CHECK_NULL_VOID(spanItem);
1829 }
1830 if (isDragging) {
1831 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
1832 resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
1833 pattern->dragSpanItems_.emplace_back(spanItem);
1834 } else {
1835 spanItem->EndDrag();
1836 }
1837 return;
1838 }
1839
1840 if (resultObj.type == SelectSpanType::TYPEIMAGE) {
1841 if (isDragging) {
1842 pattern->dragSpanItems_.emplace_back(spanItem);
1843 }
1844 auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
1845 CHECK_NULL_VOID(imageNode);
1846 auto renderContext = imageNode->GetRenderContext();
1847 CHECK_NULL_VOID(renderContext);
1848 renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
1849 imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1850 }
1851 };
1852 for (const auto& resultObj : resultObjects) {
1853 dragStatusUpdateAction(resultObj);
1854 }
1855 }
1856
OnDragEnd(const RefPtr<Ace::DragEvent> & event)1857 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
1858 {
1859 auto wk = WeakClaim(this);
1860 auto pattern = wk.Upgrade();
1861 CHECK_NULL_VOID(pattern);
1862 auto host = GetHost();
1863 CHECK_NULL_VOID(host);
1864 if (status_ == Status::DRAGGING) {
1865 status_ = Status::NONE;
1866 }
1867 dragSpanItems_.clear();
1868 if (dragResultObjects_.empty()) {
1869 return;
1870 }
1871 UpdateSpanItemDragStatus(dragResultObjects_, false);
1872 dragResultObjects_.clear();
1873 if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
1874 HandleSelectionChange(recoverStart_, recoverEnd_);
1875 isShowMenu_ = false;
1876 if (GetCurrentDragTool() == SourceTool::FINGER) {
1877 CalculateHandleOffsetAndShowOverlay();
1878 ShowSelectOverlay({ .menuIsShow = false });
1879 }
1880 }
1881 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1882 }
1883
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)1884 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
1885 {
1886 auto wk = WeakClaim(this);
1887 auto pattern = wk.Upgrade();
1888 CHECK_NULL_VOID(pattern);
1889 auto host = pattern->GetHost();
1890 CHECK_NULL_VOID(host);
1891 if (pattern->status_ == Status::DRAGGING) {
1892 pattern->status_ = Status::NONE;
1893 pattern->MarkContentChange();
1894 pattern->contentMod_->ChangeDragStatus();
1895 if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
1896 HandleSelectionChange(recoverStart_, recoverEnd_);
1897 isShowMenu_ = false;
1898 if (GetCurrentDragTool() == SourceTool::FINGER) {
1899 CalculateHandleOffsetAndShowOverlay();
1900 ShowSelectOverlay({ .menuIsShow = false });
1901 }
1902 }
1903 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
1904 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1905 }
1906 }
1907
InitDragEvent()1908 void TextPattern::InitDragEvent()
1909 {
1910 auto host = GetHost();
1911 CHECK_NULL_VOID(host);
1912 auto eventHub = host->GetEventHub<EventHub>();
1913 CHECK_NULL_VOID(eventHub);
1914 auto gestureHub = host->GetOrCreateGestureEventHub();
1915 CHECK_NULL_VOID(gestureHub);
1916 gestureHub->InitDragDropEvent();
1917 gestureHub->SetTextDraggable(true);
1918 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
1919 auto onDragStart = [weakPtr = WeakClaim(this)](
1920 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
1921 NG::DragDropInfo itemInfo;
1922 auto pattern = weakPtr.Upgrade();
1923 CHECK_NULL_RETURN(pattern, itemInfo);
1924 auto eventHub = pattern->GetEventHub<EventHub>();
1925 CHECK_NULL_RETURN(eventHub, itemInfo);
1926 pattern->SetCurrentDragTool(event->GetSourceTool());
1927 if (pattern->spans_.empty() && !pattern->isSpanStringMode_) {
1928 return pattern->OnDragStartNoChild(event, extraParams);
1929 }
1930 return pattern->OnDragStart(event, extraParams);
1931 };
1932 eventHub->SetDefaultOnDragStart(std::move(onDragStart));
1933 auto onDragMove = [weakPtr = WeakClaim(this)](
1934 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
1935 auto pattern = weakPtr.Upgrade();
1936 CHECK_NULL_VOID(pattern);
1937 pattern->OnDragMove(event);
1938 };
1939 eventHub->SetOnDragMove(std::move(onDragMove));
1940 auto onDragEnd = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event) {
1941 auto pattern = weakPtr.Upgrade();
1942 CHECK_NULL_VOID(pattern);
1943 // 拖拽框架强引用导致退出页面后还能够运行到这里
1944 if (pattern->isDetachFromMainTree_) {
1945 return;
1946 }
1947 ContainerScope scope(pattern->GetHostInstanceId());
1948 pattern->showSelect_ = true;
1949 if (pattern->spans_.empty()) {
1950 pattern->OnDragEndNoChild(event);
1951 } else {
1952 pattern->OnDragEnd(event);
1953 }
1954 };
1955 eventHub->SetOnDragEnd(std::move(onDragEnd));
1956 }
1957
OnDragMove(const RefPtr<Ace::DragEvent> & event)1958 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
1959 {
1960 auto weakPtr = WeakClaim(this);
1961 auto pattern = weakPtr.Upgrade();
1962 if (pattern->status_ == Status::DRAGGING) {
1963 CloseSelectOverlay();
1964 pattern->showSelect_ = false;
1965 }
1966 }
1967
GetThumbnailCallback()1968 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
1969 {
1970 return [wk = WeakClaim(this)](const Offset& point) {
1971 auto pattern = wk.Upgrade();
1972 CHECK_NULL_VOID(pattern);
1973 if (pattern->BetweenSelectedPosition(point)) {
1974 auto host = pattern->GetHost();
1975 const auto& children = pattern->GetChildNodes();
1976 std::list<RefPtr<FrameNode>> imageChildren;
1977 for (const auto& child : children) {
1978 auto node = DynamicCast<FrameNode>(child);
1979 if (!node) {
1980 continue;
1981 }
1982 auto image = node->GetPattern<ImagePattern>();
1983 if (image) {
1984 imageChildren.emplace_back(node);
1985 }
1986 }
1987 RichEditorDragInfo info;
1988 info.firstHandle = pattern->textSelector_.firstHandle;
1989 info.secondHandle = pattern->textSelector_.secondHandle;
1990 pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren, info);
1991 auto textDragPattern = pattern->dragNode_->GetPattern<TextDragPattern>();
1992 if (textDragPattern) {
1993 auto option = pattern->GetHost()->GetDragPreviewOption();
1994 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
1995 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
1996 ShadowStyle::OuterFloatingSM);
1997 pattern->GetHost()->SetDragPreviewOptions(option);
1998 }
1999 FrameNode::ProcessOffscreenNode(pattern->dragNode_);
2000 auto gestureHub = host->GetOrCreateGestureEventHub();
2001 CHECK_NULL_VOID(gestureHub);
2002 gestureHub->SetPixelMap(nullptr);
2003 }
2004 };
2005 }
2006
GetAllChildren() const2007 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
2008 {
2009 return childNodes_;
2010 }
2011
2012 // ===========================================================
2013 // TextDragBase implementations
GetSelectedSpanText(std::wstring value,int32_t start,int32_t end) const2014 std::string TextPattern::GetSelectedSpanText(std::wstring value, int32_t start, int32_t end) const
2015 {
2016 if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
2017 return "";
2018 }
2019 auto min = std::min(start, end);
2020 auto max = std::max(start, end);
2021
2022 return StringUtils::ToString(value.substr(min, max - min));
2023 }
2024
GetSpanItemByIndex(int32_t index) const2025 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
2026 {
2027 int32_t size = static_cast<int32_t>(spans_.size());
2028 if (index < 0 || index >= size) {
2029 return nullptr;
2030 }
2031 auto pos = spans_.begin();
2032 std::advance(pos, index);
2033 return *pos;
2034 }
2035
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2036 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2037 {
2038 bool selectFlag = false;
2039 ResultObject resultObject;
2040 resultObject.isDraggable = false;
2041 if (!DynamicCast<SpanNode>(uinode)) {
2042 return resultObject;
2043 }
2044 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
2045 int32_t itemLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
2046 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
2047 int32_t startPosition = endPosition - itemLength;
2048
2049 if (startPosition >= start && endPosition <= end) {
2050 selectFlag = true;
2051 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2052 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2053 } else if (startPosition < start && endPosition <= end && endPosition > start) {
2054 selectFlag = true;
2055 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2056 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2057 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
2058 selectFlag = true;
2059 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2060 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2061 } else if (startPosition <= start && endPosition >= end) {
2062 selectFlag = true;
2063 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2064 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2065 }
2066 if (selectFlag) {
2067 resultObject.valueResource = spanItem->GetResourceObject();
2068 resultObject.spanPosition.spanIndex = index;
2069 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2070 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2071 resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
2072 resultObject.valueString = std::to_string(spanItem->unicode);
2073 auto spanNode = DynamicCast<SpanNode>(uinode);
2074 resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
2075 }
2076 return resultObject;
2077 }
2078
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)2079 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
2080 {
2081 SymbolSpanStyle symbolSpanStyle;
2082 std::string symbolColorValue;
2083 auto symbolColors = node->GetSymbolColorList();
2084 for (const auto& color : *symbolColors) {
2085 symbolColorValue += color.ColorToString() + ",";
2086 }
2087 symbolColorValue =
2088 symbolColorValue.substr(0, !symbolColorValue.empty() ? static_cast<int32_t>(symbolColorValue.size()) - 1 : 0);
2089 symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
2090 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
2091 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToFp();
2092 } else {
2093 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
2094 }
2095 symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
2096 symbolSpanStyle.renderingStrategy = static_cast<int32_t>(node->GetSymbolRenderingStrategyValue(0));
2097 symbolSpanStyle.effectStrategy = static_cast<int32_t>(node->GetSymbolEffectStrategyValue(0));
2098 return symbolSpanStyle;
2099 }
2100
2101 // ===========================================================
2102 // TextDragBase implementations
GetLineHeight() const2103 float TextPattern::GetLineHeight() const
2104 {
2105 auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
2106 CHECK_NULL_RETURN(selectedRects.size(), {});
2107 return selectedRects.front().Height();
2108 }
2109
GetTextBoxes()2110 std::vector<RectF> TextPattern::GetTextBoxes()
2111 {
2112 return pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
2113 }
2114
GetParentGlobalOffset() const2115 OffsetF TextPattern::GetParentGlobalOffset() const
2116 {
2117 selectOverlay_->UpdateHandleGlobalOffset();
2118 auto host = GetHost();
2119 CHECK_NULL_RETURN(host, {});
2120 auto pipeline = PipelineContext::GetCurrentContextSafely();
2121 CHECK_NULL_RETURN(pipeline, {});
2122 auto rootOffset = pipeline->GetRootRect().GetOffset();
2123 return host->GetPaintRectOffset() - rootOffset;
2124 }
2125
CreateHandles()2126 void TextPattern::CreateHandles()
2127 {
2128 if (IsDragging()) {
2129 TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging");
2130 return;
2131 }
2132 ShowSelectOverlay();
2133 }
2134
BetweenSelectedPosition(const Offset & globalOffset)2135 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
2136 {
2137 auto host = GetHost();
2138 CHECK_NULL_RETURN(host, false);
2139 auto offset = host->GetPaintRectOffset();
2140 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
2141 if (selectOverlay_->HasRenderTransform()) {
2142 localOffset = ConvertGlobalToLocalOffset(globalOffset);
2143 }
2144 return IsDraggable(localOffset);
2145 }
2146
2147 // end of TextDragBase implementations
2148 // ===========================================================
2149
OnModifyDone()2150 void TextPattern::OnModifyDone()
2151 {
2152 Pattern::OnModifyDone();
2153 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2154 CHECK_NULL_VOID(textLayoutProperty);
2155 auto host = GetHost();
2156 CHECK_NULL_VOID(host);
2157 auto renderContext = host->GetRenderContext();
2158 CHECK_NULL_VOID(renderContext);
2159 auto nowTime = static_cast<unsigned long long>(GetSystemTimestamp());
2160 ACE_TEXT_SCOPED_TRACE("OnModifyDone[Text][id:%d][time:%llu]", host->GetId(), nowTime);
2161 DumpRecord(std::to_string(nowTime));
2162 if (!(PipelineContext::GetCurrentContextSafely() &&
2163 PipelineContext::GetCurrentContextSafely()->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
2164 bool shouldClipToContent =
2165 textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
2166 host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
2167 }
2168 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
2169 if (!renderContext->GetClipEdge().has_value()) {
2170 renderContext->UpdateClipEdge(true);
2171 renderContext->SetClipToFrame(true);
2172 }
2173 CloseSelectOverlay();
2174 ResetSelection();
2175 copyOption_ = CopyOptions::None;
2176 } else {
2177 copyOption_ = textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
2178 }
2179
2180
2181 const auto& children = host->GetChildren();
2182 if (children.empty()) {
2183 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
2184 bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
2185 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
2186 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE ||
2187 (ifHaveObscured && !isSpanStringMode_)) {
2188 CloseSelectOverlay();
2189 ResetSelection();
2190 copyOption_ = CopyOptions::None;
2191 }
2192
2193 std::string textCache = textForDisplay_;
2194 if (!isSpanStringMode_) {
2195 textForDisplay_ = textLayoutProperty->GetContent().value_or("");
2196 }
2197 if (textCache != textForDisplay_) {
2198 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
2199 dataDetectorAdapter_->aiDetectInitialized_ = false;
2200 CloseSelectOverlay();
2201 ResetSelection();
2202 }
2203
2204 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
2205 ParseOriText(textForDisplay_);
2206 }
2207 }
2208
2209 if (children.empty() && CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
2210 dataDetectorAdapter_->textForAI_ = textForDisplay_;
2211 dataDetectorAdapter_->StartAITask();
2212 }
2213
2214 auto gestureEventHub = host->GetOrCreateGestureEventHub();
2215 CHECK_NULL_VOID(gestureEventHub);
2216 auto eventHub = host->GetEventHub<EventHub>();
2217 CHECK_NULL_VOID(eventHub);
2218 if (IsSelectableAndCopy()) {
2219 auto context = PipelineContext::GetCurrentContextSafely();
2220 CHECK_NULL_VOID(context);
2221 if (!clipboard_ && context) {
2222 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
2223 }
2224 InitLongPressEvent(gestureEventHub);
2225 if (host->IsDraggable()) {
2226 InitDragEvent();
2227 }
2228 InitKeyEvent();
2229 InitMouseEvent();
2230 InitTouchEvent();
2231 SetAccessibilityAction();
2232 } else {
2233 if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
2234 gestureEventHub->SetTextDraggable(false);
2235 eventHub->SetDefaultOnDragStart(nullptr);
2236 if (!eventHub->HasOnDragStart() && IsTextNode()) {
2237 gestureEventHub->RemoveDragEvent();
2238 }
2239 }
2240 if (longPressEvent_ && !hasSpanStringLongPressEvent_) {
2241 gestureEventHub->SetLongPressEvent(nullptr);
2242 longPressEvent_ = nullptr;
2243 }
2244 }
2245 if (onClick_ || IsSelectableAndCopy() || CanStartAITask()) {
2246 InitClickEvent(gestureEventHub);
2247 if (CanStartAITask()) {
2248 auto context = PipelineContext::GetCurrentContextSafely();
2249 CHECK_NULL_VOID(context);
2250 if (!clipboard_ && context) {
2251 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
2252 }
2253 InitMouseEvent();
2254 }
2255 }
2256 bool enabledCache = eventHub->IsEnabled();
2257 selectOverlay_->UpdateHandleColor();
2258 if (textDetectEnable_ && enabledCache != enabled_) {
2259 enabled_ = enabledCache;
2260 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
2261 }
2262 ProcessMarqueeVisibleAreaCallback();
2263 }
2264
SetActionExecSubComponent()2265 bool TextPattern::SetActionExecSubComponent()
2266 {
2267 auto host = GetHost();
2268 CHECK_NULL_RETURN(host, false);
2269 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2270 CHECK_NULL_RETURN(accessibilityProperty, false);
2271 accessibilityProperty->SetActionExecSubComponent([weakPtr = WeakClaim(this)](int32_t spanId) -> bool {
2272 const auto& pattern = weakPtr.Upgrade();
2273 CHECK_NULL_RETURN(pattern, false);
2274 return pattern->ExecSubComponent(spanId);
2275 });
2276 return true;
2277 }
2278
GetSubComponentInfos(std::vector<SubComponentInfo> & subComponentInfos)2279 size_t TextPattern::GetSubComponentInfos(std::vector<SubComponentInfo>& subComponentInfos)
2280 {
2281 subComponentInfos.clear();
2282 subComponentInfos_.clear();
2283 if (spans_.empty()) {
2284 GetSubComponentInfosForAISpans(subComponentInfos);
2285 } else {
2286 GetSubComponentInfosForSpans(subComponentInfos);
2287 }
2288 SetActionExecSubComponent();
2289 return subComponentInfos.size();
2290 }
2291
GetSubComponentInfosForAISpans(std::vector<SubComponentInfo> & subComponentInfos)2292 void TextPattern::GetSubComponentInfosForAISpans(std::vector<SubComponentInfo>& subComponentInfos)
2293 {
2294 CHECK_NULL_VOID(dataDetectorAdapter_);
2295 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
2296 auto& aiSpan = kv.second;
2297 AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
2298 }
2299 }
2300
GetSubComponentInfosForSpans(std::vector<SubComponentInfo> & subComponentInfos)2301 void TextPattern::GetSubComponentInfosForSpans(std::vector<SubComponentInfo>& subComponentInfos)
2302 {
2303 for (const auto& span : spans_) {
2304 if (span == nullptr) {
2305 continue; // skip null
2306 }
2307 if ((span->imageNodeId >= 0) || (span->unicode > 0)) {
2308 continue; // skip ImageSpan and SymbolSpan
2309 }
2310 if (span->spanItemType == SpanItemType::CustomSpan) {
2311 continue; // skip CustomSpan
2312 }
2313 auto placeholderSpan = DynamicCast<PlaceholderSpanItem>(span);
2314 if ((placeholderSpan != nullptr) && (placeholderSpan->placeholderSpanNodeId >=0)) {
2315 continue; // skip PlaceholderSpan
2316 }
2317 if (span->content.empty()) {
2318 continue; // skip empty text
2319 }
2320 AddSubComponentInfoForSpan(subComponentInfos, span->content, span);
2321 AddSubComponentInfosByDataDetectorForSpan(subComponentInfos, span);
2322 }
2323 }
2324
AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo> & subComponentInfos,const RefPtr<SpanItem> & span)2325 void TextPattern::AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo>& subComponentInfos,
2326 const RefPtr<SpanItem>& span)
2327 {
2328 CHECK_NULL_VOID(span);
2329 CHECK_NULL_VOID(dataDetectorAdapter_);
2330 auto wSpanContent = StringUtils::ToWstring(span->content);
2331 int32_t wSpanContentLength = static_cast<int32_t>(wSpanContent.length());
2332 int32_t spanStart = span->position - wSpanContentLength;
2333 if (span->needRemoveNewLine) {
2334 spanStart -= 1;
2335 }
2336 int32_t preEnd = spanStart;
2337 auto aiSpanMap = dataDetectorAdapter_->aiSpanMap_;
2338 while (!aiSpanMap.empty()) {
2339 auto aiSpan = aiSpanMap.begin()->second;
2340 if (aiSpan.start >= span->position || preEnd >= span->position) {
2341 break;
2342 }
2343 int32_t aiSpanStartInSpan = std::max(spanStart, aiSpan.start);
2344 int32_t aiSpanEndInSpan = std::min(span->position, aiSpan.end);
2345 if (aiSpan.end <= spanStart || aiSpanStartInSpan < preEnd) {
2346 TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
2347 aiSpanMap.erase(aiSpanMap.begin());
2348 continue;
2349 }
2350 AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
2351 preEnd = aiSpanEndInSpan;
2352 if (aiSpan.end > span->position) {
2353 return;
2354 } else {
2355 aiSpanMap.erase(aiSpanMap.begin());
2356 }
2357 }
2358 }
2359
ExecSubComponent(int32_t spanId)2360 bool TextPattern::ExecSubComponent(int32_t spanId)
2361 {
2362 if ((spanId < 0) || (spanId >= static_cast<int32_t>(subComponentInfos_.size()))) {
2363 return false;
2364 }
2365 auto subComponentInfo = subComponentInfos_[spanId];
2366 if (subComponentInfo.aiSpan.has_value()) {
2367 CHECK_NULL_RETURN(dataDetectorAdapter_, false);
2368 dataDetectorAdapter_->ResponseBestMatchItem(subComponentInfo.aiSpan.value());
2369 return true;
2370 }
2371 const auto& span = subComponentInfo.span.Upgrade();
2372 CHECK_NULL_RETURN(span, false);
2373 CHECK_NULL_RETURN(span->onClick, false);
2374 GestureEvent info;
2375 std::chrono::microseconds microseconds(GetMicroTickCount());
2376 TimeStamp time(microseconds);
2377 info.SetTimeStamp(time);
2378 span->onClick(info);
2379 return true;
2380 }
2381
AddSubComponentInfoForSpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const RefPtr<SpanItem> & span)2382 void TextPattern::AddSubComponentInfoForSpan(std::vector<SubComponentInfo>& subComponentInfos,
2383 const std::string& content, const RefPtr<SpanItem>& span)
2384 {
2385 CHECK_NULL_VOID(span);
2386 CHECK_NULL_VOID(span->onClick); // skip null onClick
2387 SubComponentInfo subComponentInfo;
2388 subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
2389 subComponentInfo.spanText = content;
2390 if (span->accessibilityProperty == nullptr) {
2391 subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
2392 } else {
2393 subComponentInfo.accessibilityText = span->accessibilityProperty->GetAccessibilityText();
2394 subComponentInfo.accessibilityDescription =
2395 span->accessibilityProperty->GetAccessibilityDescription();
2396 subComponentInfo.accessibilityLevel = span->accessibilityProperty->GetAccessibilityLevel();
2397 }
2398 subComponentInfos.emplace_back(subComponentInfo);
2399
2400 SubComponentInfoEx subComponentInfoEx;
2401 subComponentInfoEx.span = span;
2402 subComponentInfos_.emplace_back(subComponentInfoEx);
2403 }
2404
AddSubComponentInfoForAISpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const AISpan & aiSpan)2405 void TextPattern::AddSubComponentInfoForAISpan(std::vector<SubComponentInfo>& subComponentInfos,
2406 const std::string& content, const AISpan& aiSpan)
2407 {
2408 SubComponentInfo subComponentInfo;
2409 subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
2410 subComponentInfo.spanText = content;
2411 subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
2412 subComponentInfos.emplace_back(subComponentInfo);
2413
2414 SubComponentInfoEx subComponentInfoEx;
2415 subComponentInfoEx.aiSpan = aiSpan;
2416 subComponentInfos_.emplace_back(subComponentInfoEx);
2417 }
2418
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const2419 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
2420 {
2421 json->PutFixedAttr("content", textForDisplay_.c_str(), filter, FIXED_ATTR_CONTENT);
2422 /* no fixed attr below, just return */
2423 if (filter.IsFastFilter()) {
2424 return;
2425 }
2426 json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
2427 json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
2428 const auto& selector = GetTextSelector();
2429 auto result = "[" + std::to_string(selector.GetTextStart()) + "," + std::to_string(selector.GetTextEnd()) + "]";
2430 json->PutExtAttr("selection", result.c_str(), filter);
2431 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
2432 CHECK_NULL_VOID(textLayoutProp);
2433 json->PutExtAttr("fontSize", GetFontSizeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
2434 if (textStyle_.has_value() && textStyle_->GetAdaptTextSize()) {
2435 auto adaptedFontSize = textStyle_->GetFontSize();
2436 json->PutExtAttr("actualFontSize", adaptedFontSize.ToString().c_str(), filter);
2437 } else {
2438 json->PutExtAttr("actualFontSize", GetFontSizeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
2439 }
2440 json->PutExtAttr("font", GetFontInJson().c_str(), filter);
2441 json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
2442 json->PutExtAttr("caretColor", GetCaretColor().c_str(), filter);
2443 json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().c_str(), filter);
2444 }
2445
GetBindSelectionMenuInJson() const2446 std::string TextPattern::GetBindSelectionMenuInJson() const
2447 {
2448 auto jsonArray = JsonUtil::CreateArray(true);
2449 for (auto& [spanResponsePair, params] : selectionMenuMap_) {
2450 auto& [spanType, responseType] = spanResponsePair;
2451 auto jsonItem = JsonUtil::Create(true);
2452 jsonItem->Put("spanType", static_cast<int32_t>(spanType));
2453 jsonItem->Put("responseType", static_cast<int32_t>(responseType));
2454 jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::SELECTION_MENU));
2455 jsonArray->Put(jsonItem);
2456 }
2457 FillPreviewMenuInJson(jsonArray);
2458 return StringUtils::RestoreBackslash(jsonArray->ToString());
2459 }
2460
GetFontInJson() const2461 std::string TextPattern::GetFontInJson() const
2462 {
2463 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
2464 CHECK_NULL_RETURN(textLayoutProp, "");
2465 auto jsonValue = JsonUtil::Create(true);
2466 jsonValue->Put("style", GetFontStyleInJson(textLayoutProp->GetItalicFontStyle()).c_str());
2467 jsonValue->Put("size", GetFontSizeInJson(textLayoutProp->GetFontSize()).c_str());
2468 jsonValue->Put("weight", GetFontWeightInJson(textLayoutProp->GetFontWeight()).c_str());
2469 jsonValue->Put("variableFontWeight", std::to_string(textLayoutProp->GetVariableFontWeight().value_or(0)).c_str());
2470 jsonValue->Put("enableVariableFontWeight",
2471 textLayoutProp->GetEnableVariableFontWeight().value_or(false) ? "true" : "false");
2472 jsonValue->Put("family", GetFontFamilyInJson(textLayoutProp->GetFontFamily()).c_str());
2473 return jsonValue->ToString();
2474 }
2475
OnAfterModifyDone()2476 void TextPattern::OnAfterModifyDone()
2477 {
2478 auto host = GetHost();
2479 CHECK_NULL_VOID(host);
2480 auto inspectorId = host->GetInspectorId().value_or("");
2481 if (!inspectorId.empty()) {
2482 auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
2483 Recorder::NodeDataCache::Get().PutString(host, inspectorId, prop->GetText());
2484 }
2485 }
2486
ActSetSelection(int32_t start,int32_t end)2487 void TextPattern::ActSetSelection(int32_t start, int32_t end)
2488 {
2489 if (start == -1 && end == -1) {
2490 ResetSelection();
2491 CloseSelectOverlay();
2492 return;
2493 }
2494 int32_t min = 0;
2495 int32_t textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
2496 start = start < min ? min : start;
2497 end = end < min ? min : end;
2498 start = start > textSize ? textSize : start;
2499 end = end > textSize ? textSize : end;
2500 if (start >= end) {
2501 FireOnSelectionChange(-1, -1);
2502 return;
2503 }
2504 HandleSelectionChange(start, end);
2505 parentGlobalOffset_ = GetParentGlobalOffset();
2506 CalculateHandleOffsetAndShowOverlay();
2507 if (textSelector_.firstHandle == textSelector_.secondHandle) {
2508 ResetSelection();
2509 CloseSelectOverlay();
2510 return;
2511 }
2512 if (IsShowHandle()) {
2513 ShowSelectOverlay();
2514 } else {
2515 CloseSelectOverlay();
2516 if (IsSelected()) {
2517 selectOverlay_->SetSelectionHoldCallback();
2518 }
2519 }
2520 auto host = GetHost();
2521 CHECK_NULL_VOID(host);
2522 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2523 }
2524
IsShowHandle()2525 bool TextPattern::IsShowHandle()
2526 {
2527 auto pipeline = PipelineContext::GetCurrentContextSafely();
2528 CHECK_NULL_RETURN(pipeline, false);
2529 auto theme = pipeline->GetTheme<TextTheme>();
2530 CHECK_NULL_RETURN(theme, false);
2531 return !theme->IsShowHandle();
2532 }
2533
GetUrlHoverColor()2534 Color TextPattern::GetUrlHoverColor()
2535 {
2536 auto pipeline = PipelineContext::GetCurrentContextSafely();
2537 CHECK_NULL_RETURN(pipeline, Color());
2538 auto theme = pipeline->GetTheme<TextTheme>();
2539 CHECK_NULL_RETURN(theme, Color());
2540 return theme->GetUrlHoverColor();
2541 }
2542
GetUrlPressColor()2543 Color TextPattern::GetUrlPressColor()
2544 {
2545 auto pipeline = PipelineContext::GetCurrentContextSafely();
2546 CHECK_NULL_RETURN(pipeline, Color());
2547 auto theme = pipeline->GetTheme<TextTheme>();
2548 CHECK_NULL_RETURN(theme, Color());
2549 return theme->GetUrlPressColor();
2550 }
2551
GetUrlSpanColor()2552 Color TextPattern::GetUrlSpanColor()
2553 {
2554 auto pipeline = PipelineContext::GetCurrentContextSafely();
2555 CHECK_NULL_RETURN(pipeline, Color());
2556 auto theme = pipeline->GetTheme<TextTheme>();
2557 CHECK_NULL_RETURN(theme, Color());
2558
2559 auto host = GetHost();
2560 CHECK_NULL_RETURN(host, Color());
2561 auto eventHub = host->GetEventHub<EventHub>();
2562 CHECK_NULL_RETURN(eventHub, Color());
2563
2564 if (eventHub && !eventHub->IsEnabled()) {
2565 return theme->GetUrlDisabledColor();
2566 } else {
2567 return theme->GetUrlDefaultColor();
2568 }
2569 }
2570
2571 // Deprecated: Use the TextSelectOverlay::ProcessOverlay() instead.
2572 // It is currently used by RichEditorPattern.
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)2573 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
2574 {
2575 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
2576 SelectHandleInfo firstHandleInfo;
2577 firstHandleInfo.paintRect = textSelector_.firstHandle;
2578 CheckHandles(firstHandleInfo);
2579
2580 SelectHandleInfo secondHandleInfo;
2581 secondHandleInfo.paintRect = textSelector_.secondHandle;
2582 CheckHandles(secondHandleInfo);
2583
2584 auto start = textSelector_.GetTextStart();
2585 auto end = textSelector_.GetTextEnd();
2586 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
2587 if (selectInfo.isNewAvoid) {
2588 selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
2589 }
2590 selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
2591 selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
2592 } else {
2593 auto pipeline = PipelineContext::GetCurrentContextSafely();
2594 CHECK_NULL_VOID(pipeline);
2595 auto host = GetHost();
2596 CHECK_NULL_VOID(host);
2597 pipeline->AddOnAreaChangeNode(host->GetId());
2598 selectInfo.callerFrameNode = GetHost();
2599 selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
2600 if (!selectInfo.isUsingMouse) {
2601 CheckHandles(selectInfo.firstHandle);
2602 CheckHandles(selectInfo.secondHandle);
2603 }
2604 selectOverlayProxy_ =
2605 pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
2606 CHECK_NULL_VOID(selectOverlayProxy_);
2607 auto start = textSelector_.GetTextStart();
2608 auto end = textSelector_.GetTextEnd();
2609 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
2610 }
2611 }
2612
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)2613 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
2614 {
2615 if (config.skipMeasure || dirty->SkipMeasureContent()) {
2616 return false;
2617 }
2618 contentRect_ = dirty->GetGeometryNode()->GetContentRect();
2619
2620 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
2621 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
2622 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
2623 CHECK_NULL_RETURN(textLayoutAlgorithm, false);
2624 baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
2625 contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
2626 textStyle_ = textLayoutAlgorithm->GetTextStyle();
2627 ProcessOverlayAfterLayout();
2628 return true;
2629 }
2630
ProcessOverlayAfterLayout()2631 void TextPattern::ProcessOverlayAfterLayout()
2632 {
2633 if (selectOverlay_->SelectOverlayIsOn()) {
2634 CalculateHandleOffsetAndShowOverlay();
2635 selectOverlay_->UpdateAllHandlesOffset();
2636 selectOverlay_->UpdateViewPort();
2637 }
2638 }
2639
PreCreateLayoutWrapper()2640 void TextPattern::PreCreateLayoutWrapper()
2641 {
2642 auto host = GetHost();
2643 CHECK_NULL_VOID(host);
2644
2645 auto paintProperty = GetPaintProperty<PaintProperty>();
2646 CHECK_NULL_VOID(paintProperty);
2647 auto flag = paintProperty->GetPropertyChangeFlag();
2648 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2649 CHECK_NULL_VOID(textLayoutProperty);
2650 auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
2651 if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
2652 return;
2653 }
2654
2655 spans_.clear();
2656 childNodes_.clear();
2657
2658 // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
2659 const auto& children = host->GetChildren();
2660 if (children.empty()) {
2661 placeholderCount_ = 0;
2662 return;
2663 }
2664
2665 // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
2666 // tree.
2667 std::stack<SpanNodeInfo> nodes;
2668 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
2669 nodes.push({ .node = *iter });
2670 }
2671
2672 InitSpanItem(nodes);
2673 }
2674
InitSpanItem(std::stack<SpanNodeInfo> nodes)2675 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
2676 {
2677 auto host = GetHost();
2678 CHECK_NULL_VOID(host);
2679 std::string textCache;
2680 std::string textForAICache;
2681 int32_t oldPlaceholderCount = placeholderCount_;
2682 placeholderCount_ = 0;
2683 if (!nodes.empty()) {
2684 textCache = textForDisplay_;
2685 textForAICache = dataDetectorAdapter_->textForAI_;
2686 textForDisplay_.clear();
2687 dataDetectorAdapter_->textForAI_.clear();
2688 }
2689
2690 bool isSpanHasClick = false;
2691 CollectSpanNodes(nodes, isSpanHasClick);
2692 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2693 CHECK_NULL_VOID(textLayoutProperty);
2694 if (childNodes_.empty()) {
2695 textForDisplay_ = textLayoutProperty->GetContent().value_or("");
2696 }
2697 if (oldPlaceholderCount != placeholderCount_) {
2698 CloseSelectOverlay();
2699 ResetSelection();
2700 }
2701
2702 if (textCache != textForDisplay_) {
2703 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
2704 OnAfterModifyDone();
2705 for (const auto& item : spans_) {
2706 if (item->inspectId.empty()) {
2707 continue;
2708 }
2709 Recorder::NodeDataCache::Get().PutString(host, item->inspectId, item->content);
2710 }
2711 ResetAfterTextChange();
2712 }
2713 if (isSpanHasClick) {
2714 auto gestureEventHub = host->GetOrCreateGestureEventHub();
2715 InitClickEvent(gestureEventHub);
2716 }
2717 if (textForAICache != dataDetectorAdapter_->textForAI_) {
2718 dataDetectorAdapter_->aiDetectInitialized_ = false;
2719 }
2720 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
2721 ParseOriText(textLayoutProperty->GetContent().value_or(""));
2722 if (!dataDetectorAdapter_->aiDetectInitialized_) {
2723 dataDetectorAdapter_->StartAITask();
2724 }
2725 }
2726 }
2727
ResetAfterTextChange()2728 void TextPattern::ResetAfterTextChange()
2729 {
2730 CloseSelectOverlay();
2731 ResetSelection();
2732 }
2733
ParseOriText(const std::string & currentText)2734 void TextPattern::ParseOriText(const std::string& currentText)
2735 {
2736 auto entityJson = JsonUtil::ParseJsonString(currentText);
2737 bool entityIsJson = !entityJson->IsNull();
2738 TAG_LOGI(AceLogTag::ACE_TEXT, "text content is the json format: %{public}d", entityIsJson);
2739 if (entityIsJson && !entityJson->GetValue("bundleName")->IsNull() &&
2740 dataDetectorAdapter_->ParseOriText(entityJson, textForDisplay_)) {
2741 if (childNodes_.empty()) {
2742 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2743 CHECK_NULL_VOID(textLayoutProperty);
2744 textLayoutProperty->UpdateContent(textForDisplay_);
2745 }
2746 }
2747 }
2748
BeforeCreateLayoutWrapper()2749 void TextPattern::BeforeCreateLayoutWrapper()
2750 {
2751 if (!isSpanStringMode_) {
2752 PreCreateLayoutWrapper();
2753 }
2754 selectOverlay_->MarkOverlayDirty();
2755 }
2756
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick)2757 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick)
2758 {
2759 while (!nodes.empty()) {
2760 auto current = nodes.top();
2761 nodes.pop();
2762 if (!current.node) {
2763 continue;
2764 }
2765 UpdateContainerChildren(current.containerSpanNode, current.node);
2766 auto spanNode = DynamicCast<SpanNode>(current.node);
2767 auto tag = current.node->GetTag();
2768 if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG && spanNode->GetSpanItem()->GetSymbolUnicode() != 0) {
2769 spanNode->CleanSpanItemChildren();
2770 UpdateChildProperty(spanNode);
2771 spanNode->MountToParagraph();
2772 textForDisplay_.append(" ");
2773 dataDetectorAdapter_->textForAI_.append(StringUtils::Str16ToStr8(SYMBOL_TRANS));
2774 childNodes_.push_back(current.node);
2775 } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
2776 CollectTextSpanNodes(spanNode, isSpanHasClick);
2777 childNodes_.push_back(current.node);
2778 } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2779 placeholderCount_++;
2780 AddChildSpanItem(current.node);
2781 dataDetectorAdapter_->textForAI_.append("\n");
2782 auto imageNode = DynamicCast<FrameNode>(current.node);
2783 if (!imageNode) {
2784 continue;
2785 }
2786 auto focus_hub = imageNode->GetOrCreateFocusHub();
2787 if (focus_hub && focus_hub->GetOnClickCallback()) {
2788 isSpanHasClick = true;
2789 }
2790 childNodes_.push_back(current.node);
2791 } else if (tag == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
2792 placeholderCount_++;
2793 AddChildSpanItem(current.node);
2794 dataDetectorAdapter_->textForAI_.append("\n");
2795 childNodes_.emplace_back(current.node);
2796 }
2797 if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2798 continue;
2799 }
2800 const auto& nextChildren = current.node->GetChildren();
2801 if (nextChildren.empty()) {
2802 continue;
2803 }
2804 auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
2805 for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
2806 nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
2807 }
2808 }
2809 }
2810
CollectTextSpanNodes(const RefPtr<SpanNode> & spanNode,bool & isSpanHasClick)2811 void TextPattern::CollectTextSpanNodes(const RefPtr<SpanNode>& spanNode, bool& isSpanHasClick)
2812 {
2813 spanNode->CleanSpanItemChildren();
2814 UpdateChildProperty(spanNode);
2815 spanNode->MountToParagraph();
2816 textForDisplay_.append(spanNode->GetSpanItem()->content);
2817 dataDetectorAdapter_->textForAI_.append(spanNode->GetSpanItem()->content);
2818 if (spanNode->GetSpanItem()->onClick) {
2819 isSpanHasClick = true;
2820 }
2821 }
2822
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)2823 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
2824 {
2825 CHECK_NULL_VOID(child);
2826 auto parent = DynamicCast<ContainerSpanNode>(parentNode);
2827 CHECK_NULL_VOID(parent);
2828 auto baseSpan = DynamicCast<BaseSpan>(child);
2829 if (baseSpan) {
2830 if (baseSpan->HasTextBackgroundStyle()) {
2831 return;
2832 }
2833 baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
2834 return;
2835 }
2836 if (child->GetTag() == V2::IMAGE_ETS_TAG) {
2837 auto imageNode = DynamicCast<FrameNode>(child);
2838 CHECK_NULL_VOID(imageNode);
2839 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2840 CHECK_NULL_VOID(imageLayoutProperty);
2841 if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
2842 return;
2843 }
2844 if (parent->GetTextBackgroundStyle().has_value()) {
2845 imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
2846 }
2847 }
2848 }
2849
GetGlobalOffset(Offset & offset)2850 void TextPattern::GetGlobalOffset(Offset& offset)
2851 {
2852 auto host = GetHost();
2853 CHECK_NULL_VOID(host);
2854 auto pipeline = PipelineContext::GetCurrentContextSafely();
2855 CHECK_NULL_VOID(pipeline);
2856 auto rootOffset = pipeline->GetRootRect().GetOffset();
2857 auto globalOffset = host->GetPaintRectOffset() - rootOffset;
2858 offset = Offset(globalOffset.GetX(), globalOffset.GetY());
2859 }
2860
OnVisibleChange(bool isVisible)2861 void TextPattern::OnVisibleChange(bool isVisible)
2862 {
2863 if (!isVisible) {
2864 if (textSelector_.IsValid()) {
2865 CloseSelectOverlay();
2866 ResetSelection();
2867 }
2868 if (textDetectEnable_) {
2869 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
2870 }
2871 } else {
2872 if (CanStartAITask()) {
2873 dataDetectorAdapter_->StartAITask();
2874 }
2875 }
2876 }
2877
InitSurfaceChangedCallback()2878 void TextPattern::InitSurfaceChangedCallback()
2879 {
2880 auto pipeline = PipelineContext::GetCurrentContextSafely();
2881 CHECK_NULL_VOID(pipeline);
2882 if (!HasSurfaceChangedCallback()) {
2883 auto callbackId = pipeline->RegisterSurfaceChangedCallback(
2884 [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
2885 WindowSizeChangeReason type) {
2886 auto pattern = weak.Upgrade();
2887 if (pattern) {
2888 pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
2889 }
2890 });
2891 UpdateSurfaceChangedCallbackId(callbackId);
2892 }
2893 }
2894
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight)2895 void TextPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
2896 {
2897 TAG_LOGD(AceLogTag::ACE_TEXT_FIELD,
2898 "TextPattern handle surface change, new width %{public}d, new height %{public}d, prev width %{public}d, prev "
2899 "height %{public}d",
2900 newWidth, newHeight, prevWidth, prevHeight);
2901 if (newWidth == prevWidth && newHeight == prevHeight) {
2902 return;
2903 }
2904 CHECK_NULL_VOID(selectOverlay_->SelectOverlayIsOn());
2905 if (selectOverlay_->IsShowMouseMenu()) {
2906 CloseSelectOverlay();
2907 } else {
2908 auto context = PipelineContext::GetCurrentContextSafely();
2909 if (context) {
2910 context->AddAfterLayoutTask([weak = WeakClaim(this)]() {
2911 auto pattern = weak.Upgrade();
2912 CHECK_NULL_VOID(pattern);
2913 pattern->CalculateHandleOffsetAndShowOverlay();
2914 pattern->ShowSelectOverlay({ .menuIsShow = false });
2915 });
2916 }
2917 }
2918 }
2919
InitSurfacePositionChangedCallback()2920 void TextPattern::InitSurfacePositionChangedCallback()
2921 {
2922 auto pipeline = PipelineContext::GetCurrentContextSafely();
2923 CHECK_NULL_VOID(pipeline);
2924 if (!HasSurfacePositionChangedCallback()) {
2925 auto callbackId =
2926 pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
2927 auto pattern = weak.Upgrade();
2928 if (pattern) {
2929 pattern->HandleSurfacePositionChanged(posX, posY);
2930 }
2931 });
2932 UpdateSurfacePositionChangedCallbackId(callbackId);
2933 }
2934 }
2935
AddChildSpanItem(const RefPtr<UINode> & child)2936 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
2937 {
2938 CHECK_NULL_VOID(child);
2939 auto chidNode = DynamicCast<FrameNode>(child);
2940 if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
2941 return;
2942 }
2943
2944 if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2945 auto spanNode = DynamicCast<SpanNode>(child);
2946 if (spanNode) {
2947 spans_.emplace_back(spanNode->GetSpanItem());
2948 }
2949 } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
2950 AddImageToSpanItem(child);
2951 } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2952 auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
2953 if (placeholderSpanNode) {
2954 auto placeholderSpan = placeholderSpanNode->GetSpanItem();
2955 placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
2956 spans_.emplace_back(placeholderSpan);
2957 }
2958 } else if (child->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
2959 auto customSpanNode = DynamicCast<CustomSpanNode>(child);
2960 if (customSpanNode) {
2961 auto customSpan = customSpanNode->GetSpanItem();
2962 customSpan->placeholderSpanNodeId = customSpanNode->GetId();
2963 spans_.emplace_back(customSpan);
2964 }
2965 }
2966 }
2967
AddImageToSpanItem(const RefPtr<UINode> & child)2968 void TextPattern::AddImageToSpanItem(const RefPtr<UINode>& child)
2969 {
2970 auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
2971 if (imageSpanNode) {
2972 auto host = GetHost();
2973 CHECK_NULL_VOID(host);
2974 auto imageSpanItem = imageSpanNode->GetSpanItem();
2975 if (host->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
2976 auto focus_hub = imageSpanNode->GetOrCreateFocusHub();
2977 CHECK_NULL_VOID(focus_hub);
2978 auto clickCall = focus_hub->GetOnClickCallback();
2979 if (clickCall) {
2980 imageSpanItem->SetOnClickEvent(std::move(clickCall));
2981 }
2982 auto gesture = imageSpanNode->GetOrCreateGestureEventHub();
2983 CHECK_NULL_VOID(gesture);
2984 gesture->SetHitTestMode(HitTestMode::HTMNONE);
2985 }
2986 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageSpanNode);
2987 spans_.emplace_back(imageSpanItem);
2988 spans_.back()->imageNodeId = imageSpanNode->GetId();
2989 return;
2990 }
2991 auto imageNode = DynamicCast<FrameNode>(child);
2992 if (imageNode) {
2993 auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
2994 imageSpanItem->imageNodeId = imageNode->GetId();
2995 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
2996 auto focus_hub = imageNode->GetOrCreateFocusHub();
2997 CHECK_NULL_VOID(focus_hub);
2998 auto clickCall = focus_hub->GetOnClickCallback();
2999 if (clickCall) {
3000 imageSpanItem->SetOnClickEvent(std::move(clickCall));
3001 }
3002 spans_.emplace_back(imageSpanItem);
3003 auto gesture = imageNode->GetOrCreateGestureEventHub();
3004 CHECK_NULL_VOID(gesture);
3005 gesture->SetHitTestMode(HitTestMode::HTMNONE);
3006 return;
3007 }
3008 }
3009
DumpAdvanceInfo()3010 void TextPattern::DumpAdvanceInfo()
3011 {
3012 DumpLog::GetInstance().AddDesc(std::string("-----DumpAdvanceInfo-----"));
3013 DumpLog::GetInstance().AddDesc(
3014 std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
3015 auto host = GetHost();
3016 CHECK_NULL_VOID(host);
3017 auto renderContext = host->GetRenderContext();
3018 CHECK_NULL_VOID(renderContext);
3019 if (renderContext->HasForegroundColor()) {
3020 DumpLog::GetInstance().AddDesc(
3021 std::string("ForegroundColor: ").append(renderContext->GetForegroundColorValue().ColorToString()));
3022 }
3023 if (renderContext->GetForegroundColorStrategy().has_value()) {
3024 auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
3025 DumpLog::GetInstance().AddDesc(std::string("ForegroundColorStrategy: ").append(std::to_string(strategy)));
3026 }
3027 DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
3028 }
3029
DumpInfo()3030 void TextPattern::DumpInfo()
3031 {
3032 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3033 CHECK_NULL_VOID(textLayoutProp);
3034 auto& dumpLog = DumpLog::GetInstance();
3035 auto nowTime = GetSystemTimestamp();
3036 dumpLog.AddDesc(std::string("frameRecord: ").append(frameRecord_));
3037 dumpLog.AddDesc(std::string("time: ").append(std::to_string(nowTime)));
3038 if (!IsSetObscured()) {
3039 dumpLog.AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
3040 }
3041 dumpLog.AddDesc(std::string("FontColor: ")
3042 .append((textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString()));
3043 dumpLog.AddDesc(
3044 std::string("FontSize: ")
3045 .append((textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
3046 .ToString()));
3047 if (textStyle_.has_value()) {
3048 dumpLog.AddDesc(std::string("MaxFontSize: ").append(textStyle_->GetAdaptMaxFontSize().ToString()));
3049 dumpLog.AddDesc(std::string("MinFontSize: ").append(textStyle_->GetAdaptMinFontSize().ToString()));
3050 dumpLog.AddDesc(std::string("FontWeight: ").append(StringUtils::ToString(textStyle_->GetFontWeight())));
3051 dumpLog.AddDesc(std::string("FontStyle: ").append(StringUtils::ToString(textStyle_->GetFontStyle())));
3052 dumpLog.AddDesc(std::string("LineHeight: ").append(textStyle_->GetLineHeight().ToString()));
3053 dumpLog.AddDesc(std::string("LineSpacing: ").append(textStyle_->GetLineSpacing().ToString()));
3054 dumpLog.AddDesc(std::string("maxLines: ").append(std::to_string(textStyle_->GetMaxLines())));
3055 dumpLog.AddDesc(std::string("BaselineOffset: ").append(textStyle_->GetBaselineOffset().ToString()));
3056 dumpLog.AddDesc(std::string("TextIndent: ").append(textStyle_->GetTextIndent().ToString()));
3057 dumpLog.AddDesc(std::string("LetterSpacing: ").append(textStyle_->GetLetterSpacing().ToString()));
3058 dumpLog.AddDesc(std::string("TextOverflow: ").append(StringUtils::ToString(textStyle_->GetTextOverflow())));
3059 dumpLog.AddDesc(std::string("TextAlign: ").append(StringUtils::ToString(textStyle_->GetTextAlign())));
3060 dumpLog.AddDesc(std::string("WordBreak: ").append(StringUtils::ToString(textStyle_->GetWordBreak())));
3061 dumpLog.AddDesc(std::string("TextCase: ").append(StringUtils::ToString(textStyle_->GetTextCase())));
3062 dumpLog.AddDesc(std::string("EllipsisMode: ").append(StringUtils::ToString(textStyle_->GetEllipsisMode())));
3063 dumpLog.AddDesc(
3064 std::string("LineBreakStrategy: ").append(GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy())));
3065 }
3066 dumpLog.AddDesc(
3067 std::string("HeightAdaptivePolicy: ")
3068 .append(V2::ConvertWrapTextHeightAdaptivePolicyToString(
3069 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))));
3070 if (pManager_) {
3071 auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
3072 dumpLog.AddDesc(std::string("Paragraphs num: ").append(std::to_string(num)));
3073 dumpLog.AddDesc(std::string("PaintInfo: ").append(paintInfo_));
3074 }
3075 DumpScaleInfo();
3076 DumpTextEngineInfo();
3077 if (SystemProperties::GetDebugEnabled()) {
3078 DumpAdvanceInfo();
3079 }
3080 }
3081
DumpScaleInfo()3082 void TextPattern::DumpScaleInfo()
3083 {
3084 auto& dumpLog = DumpLog::GetInstance();
3085 dumpLog.AddDesc(std::string("-----DumpScaleInfo-----"));
3086 auto host = GetHost();
3087 CHECK_NULL_VOID(host);
3088 auto pipeline = host->GetContext();
3089 CHECK_NULL_VOID(pipeline);
3090 auto fontScale = pipeline->GetFontScale();
3091 auto fontWeightScale = pipeline->GetFontWeightScale();
3092 auto followSystem = pipeline->IsFollowSystem();
3093 float maxFontScale = pipeline->GetMaxAppFontScale();
3094 auto halfLeading = pipeline->GetHalfLeading();
3095 dumpLog.AddDesc(std::string("fontScale: ").append(std::to_string(fontScale)));
3096 dumpLog.AddDesc(std::string("fontWeightScale: ").append(std::to_string(fontWeightScale)));
3097 dumpLog.AddDesc(std::string("IsFollowSystem: ").append(std::to_string(followSystem)));
3098 dumpLog.AddDesc(std::string("maxFontScale: ").append(std::to_string(maxFontScale)));
3099 dumpLog.AddDesc(std::string("halfLeading: ").append(std::to_string(halfLeading)));
3100 }
3101
DumpTextEngineInfo()3102 void TextPattern::DumpTextEngineInfo()
3103 {
3104 auto& dumpLog = DumpLog::GetInstance();
3105 dumpLog.AddDesc(std::string("-----TextEngine paragraphs_ info-----"));
3106 dumpLog.AddDesc(std::string("contentRect :").append(contentRect_.ToString()));
3107 if (pManager_) {
3108 dumpLog.AddDesc(std::string("from TextEngine paragraphs_ info :"));
3109 dumpLog.AddDesc(std::string("DidExceedMaxLines:").append(std::to_string(pManager_->DidExceedMaxLines())));
3110 dumpLog.AddDesc(std::string("GetTextWidth:")
3111 .append(std::to_string(pManager_->GetTextWidth()))
3112 .append(" GetHeight:")
3113 .append(std::to_string(pManager_->GetHeight()))
3114 .append(" GetMaxWidth:")
3115 .append(std::to_string(pManager_->GetMaxWidth()))
3116 .append(" GetMaxIntrinsicWidth:")
3117 .append(std::to_string(pManager_->GetMaxIntrinsicWidth())));
3118 dumpLog.AddDesc(std::string("GetLineCount:")
3119 .append(std::to_string(pManager_->GetLineCount()))
3120 .append(" GetLongestLine:")
3121 .append(std::to_string(pManager_->GetLongestLine()))
3122 .append(" GetLongestLineWithIndent:")
3123 .append(std::to_string(pManager_->GetLongestLineWithIndent())));
3124 }
3125 dumpLog.AddDesc(std::string("spans size :").append(std::to_string(spans_.size())));
3126 }
3127
UpdateChildProperty(const RefPtr<SpanNode> & child) const3128 void TextPattern::UpdateChildProperty(const RefPtr<SpanNode>& child) const
3129 {
3130 CHECK_NULL_VOID(child);
3131 auto host = GetHost();
3132 CHECK_NULL_VOID(host);
3133 auto textLayoutProp = host->GetLayoutProperty<TextLayoutProperty>();
3134 CHECK_NULL_VOID(textLayoutProp);
3135
3136 auto inheritPropertyInfo = child->CalculateInheritPropertyInfo();
3137 for (const PropertyInfo& info : inheritPropertyInfo) {
3138 switch (info) {
3139 case PropertyInfo::FONTSIZE:
3140 if (textLayoutProp->HasFontSize()) {
3141 child->UpdateFontSizeWithoutFlushDirty(textLayoutProp->GetFontSize().value());
3142 }
3143 break;
3144 case PropertyInfo::FONTCOLOR:
3145 if (textLayoutProp->HasTextColor()) {
3146 child->UpdateTextColorWithoutFlushDirty(textLayoutProp->GetTextColor().value());
3147 }
3148 break;
3149 case PropertyInfo::FONTSTYLE:
3150 if (textLayoutProp->HasItalicFontStyle()) {
3151 child->UpdateItalicFontStyleWithoutFlushDirty(textLayoutProp->GetItalicFontStyle().value());
3152 }
3153 break;
3154 case PropertyInfo::FONTWEIGHT:
3155 if (textLayoutProp->HasFontWeight()) {
3156 child->UpdateFontWeightWithoutFlushDirty(textLayoutProp->GetFontWeight().value());
3157 }
3158 break;
3159 case PropertyInfo::FONTFAMILY:
3160 if (textLayoutProp->HasFontFamily()) {
3161 child->UpdateFontFamilyWithoutFlushDirty(textLayoutProp->GetFontFamily().value());
3162 }
3163 break;
3164 case PropertyInfo::FONTFEATURE:
3165 if (textLayoutProp->HasFontFeature()) {
3166 child->UpdateFontFeatureWithoutFlushDirty(textLayoutProp->GetFontFeature().value());
3167 }
3168 break;
3169 case PropertyInfo::TEXTDECORATION:
3170 if (textLayoutProp->HasTextDecoration()) {
3171 child->UpdateTextDecorationWithoutFlushDirty(textLayoutProp->GetTextDecoration().value());
3172 if (textLayoutProp->HasTextDecorationColor()) {
3173 child->UpdateTextDecorationColorWithoutFlushDirty(
3174 textLayoutProp->GetTextDecorationColor().value());
3175 }
3176 if (textLayoutProp->HasTextDecorationStyle()) {
3177 child->UpdateTextDecorationStyleWithoutFlushDirty(
3178 textLayoutProp->GetTextDecorationStyle().value());
3179 }
3180 }
3181 break;
3182 case PropertyInfo::TEXTCASE:
3183 if (textLayoutProp->HasTextCase()) {
3184 child->UpdateTextCaseWithoutFlushDirty(textLayoutProp->GetTextCase().value());
3185 }
3186 break;
3187 case PropertyInfo::LETTERSPACE:
3188 if (textLayoutProp->HasLetterSpacing()) {
3189 child->UpdateLetterSpacingWithoutFlushDirty(textLayoutProp->GetLetterSpacing().value());
3190 }
3191 break;
3192 case PropertyInfo::LINEHEIGHT:
3193 if (textLayoutProp->HasLineHeight()) {
3194 child->UpdateLineHeightWithoutFlushDirty(textLayoutProp->GetLineHeight().value());
3195 }
3196 break;
3197 case PropertyInfo::LINESPACING:
3198 if (textLayoutProp->HasLineSpacing()) {
3199 child->UpdateLineSpacingWithoutFlushDirty(textLayoutProp->GetLineSpacing().value());
3200 }
3201 break;
3202 case PropertyInfo::MIN_FONT_SCALE:
3203 if (textLayoutProp->HasMinFontScale()) {
3204 child->UpdateMinFontScaleWithoutFlushDirty(textLayoutProp->GetMinFontScale().value());
3205 }
3206 break;
3207 case PropertyInfo::MAX_FONT_SCALE:
3208 if (textLayoutProp->HasMaxFontScale()) {
3209 child->UpdateMaxFontScaleWithoutFlushDirty(textLayoutProp->GetMaxFontScale().value());
3210 }
3211 break;
3212 case PropertyInfo::TEXTSHADOW:
3213 if (textLayoutProp->HasTextShadow()) {
3214 child->UpdateTextShadowWithoutFlushDirty(textLayoutProp->GetTextShadow().value());
3215 }
3216 break;
3217 case PropertyInfo::HALFLEADING:
3218 if (textLayoutProp->HasHalfLeading()) {
3219 child->UpdateHalfLeadingWithoutFlushDirty(textLayoutProp->GetHalfLeading().value());
3220 }
3221 break;
3222 case PropertyInfo::VARIABLE_FONT_WEIGHT:
3223 if (textLayoutProp->HasVariableFontWeight() && !child->GetHasUserFontWeight()) {
3224 child->UpdateVariableFontWeightWithoutFlushDirty(textLayoutProp->GetVariableFontWeight().value());
3225 }
3226 break;
3227 case PropertyInfo::ENABLE_VARIABLE_FONT_WEIGHT:
3228 if (textLayoutProp->HasEnableVariableFontWeight() && !child->GetHasUserFontWeight()) {
3229 child->UpdateEnableVariableFontWeightWithoutFlushDirty(
3230 textLayoutProp->GetEnableVariableFontWeight().value());
3231 }
3232 break;
3233 default:
3234 break;
3235 }
3236 }
3237 }
3238
SetAccessibilityAction()3239 void TextPattern::SetAccessibilityAction()
3240 {
3241 auto host = GetHost();
3242 CHECK_NULL_VOID(host);
3243 auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3244 CHECK_NULL_VOID(textAccessibilityProperty);
3245 textAccessibilityProperty->SetActionSetSelection(
3246 [weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
3247 const auto& pattern = weakPtr.Upgrade();
3248 CHECK_NULL_VOID(pattern);
3249 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
3250 CHECK_NULL_VOID(textLayoutProperty);
3251 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
3252 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
3253 mode != TextSelectableMode::UNSELECTABLE) {
3254 pattern->ActSetSelection(start, end);
3255 }
3256 });
3257
3258 textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
3259 const auto& pattern = weakPtr.Upgrade();
3260 CHECK_NULL_VOID(pattern);
3261 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
3262 CHECK_NULL_VOID(textLayoutProperty);
3263 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
3264 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
3265 mode != TextSelectableMode::UNSELECTABLE) {
3266 pattern->CloseSelectOverlay(true);
3267 pattern->ResetSelection();
3268 }
3269 });
3270
3271 textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
3272 const auto& pattern = weakPtr.Upgrade();
3273 CHECK_NULL_VOID(pattern);
3274 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
3275 CHECK_NULL_VOID(textLayoutProperty);
3276 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
3277 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
3278 mode != TextSelectableMode::UNSELECTABLE) {
3279 pattern->HandleOnCopy();
3280 pattern->CloseSelectOverlay(true);
3281 pattern->ResetSelection();
3282 }
3283 });
3284 }
3285
OnColorConfigurationUpdate()3286 void TextPattern::OnColorConfigurationUpdate()
3287 {
3288 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3289 CHECK_NULL_VOID(textLayoutProperty);
3290 CHECK_NULL_VOID(!textLayoutProperty->GetTextColorFlagByUserValue(false));
3291 auto context = PipelineContext::GetCurrentContextSafely();
3292 CHECK_NULL_VOID(context);
3293 auto theme = context->GetTheme<TextTheme>();
3294 CHECK_NULL_VOID(theme);
3295 textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
3296 if (magnifierController_) {
3297 magnifierController_->SetColorModeChange(true);
3298 }
3299 auto host = GetHost();
3300 CHECK_NULL_VOID(host);
3301 ACE_TEXT_SCOPED_TRACE("OnColorConfigurationUpdate[Text][self:%d]", host->GetId());
3302 }
3303
GetDragUpperLeftCoordinates()3304 OffsetF TextPattern::GetDragUpperLeftCoordinates()
3305 {
3306 if (dragBoxes_.empty()) {
3307 return { 0.0f, 0.0f };
3308 }
3309 auto startY = dragBoxes_.front().Top();
3310 auto startX = dragBoxes_.front().Left();
3311
3312 auto endY = dragBoxes_.back().Top();
3313 OffsetF offset;
3314 if (NearEqual(startY, endY)) {
3315 offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
3316 } else {
3317 offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
3318 }
3319
3320 return GetParentGlobalOffset() + offset;
3321 }
3322
ProcessBoundRectByTextShadow(RectF & rect)3323 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
3324 {
3325 auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
3326 auto shadows = property->GetTextShadow();
3327 if (!shadows.has_value()) {
3328 return;
3329 }
3330 float leftOffsetX = 0.0f;
3331 float rightOffsetX = 0.0f;
3332 float upOffsetY = 0.0f;
3333 float downOffsetY = 0.0f;
3334 for (const auto& shadow : shadows.value()) {
3335 auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
3336 if (LessOrEqual(shadow.GetOffset().GetX(), 0.0f) && LessNotEqual(shadow.GetOffset().GetX(), leftOffsetX)) {
3337 leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
3338 }
3339
3340 if (GreatOrEqual(shadow.GetOffset().GetX(), 0.0f) &&
3341 GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
3342 rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
3343 }
3344
3345 if (LessOrEqual(shadow.GetOffset().GetY(), 0.0f) && LessNotEqual(shadow.GetOffset().GetY(), upOffsetY)) {
3346 upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
3347 }
3348
3349 if (GreatOrEqual(shadow.GetOffset().GetY(), 0.0f) &&
3350 GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
3351 downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
3352 }
3353 }
3354 rect.SetRect(
3355 leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
3356 }
3357
ProcessBoundRectByTextMarquee(RectF & rect)3358 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
3359 {
3360 auto host = GetHost();
3361 CHECK_NULL_VOID(host);
3362 auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
3363 CHECK_NULL_VOID(textLayoutProperty);
3364 if (!(textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE)) {
3365 return;
3366 }
3367 auto geometryNode = host->GetGeometryNode();
3368 CHECK_NULL_VOID(geometryNode);
3369 auto contentSize = geometryNode->GetContentSize();
3370 CHECK_NULL_VOID(pManager_);
3371 if (pManager_->GetTextWidth() < contentSize.Width()) {
3372 return;
3373 }
3374 auto frameSize = geometryNode->GetFrameSize();
3375 auto relativeSelfLeftOffsetX =
3376 std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - pManager_->GetTextWidth());
3377 rect.SetLeft(relativeSelfLeftOffsetX);
3378 rect.SetWidth(frameSize.Width() + pManager_->GetTextWidth() - relativeSelfLeftOffsetX);
3379 }
3380
CreateNodePaintMethod()3381 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
3382 {
3383 CreateModifier();
3384 auto paintMethod =
3385 MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
3386 auto host = GetHost();
3387 CHECK_NULL_RETURN(host, paintMethod);
3388 auto context = host->GetRenderContext();
3389 CHECK_NULL_RETURN(context, paintMethod);
3390 auto geometryNode = host->GetGeometryNode();
3391 CHECK_NULL_RETURN(geometryNode, paintMethod);
3392 auto frameSize = geometryNode->GetFrameSize();
3393 if (context->GetClipEdge().value_or(Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
3394 SetResponseRegion(frameSize, frameSize);
3395 return paintMethod;
3396 }
3397 CHECK_NULL_RETURN(pManager_, paintMethod);
3398 RectF boundsRect = overlayMod_->GetBoundsRect();
3399 auto boundsWidth = contentRect_.GetX() + std::ceil(pManager_->GetLongestLineWithIndent());
3400 auto boundsHeight = contentRect_.GetY() + static_cast<float>(pManager_->GetHeight() + std::fabs(baselineOffset_));
3401 boundsRect.SetWidth(boundsWidth);
3402 boundsRect.SetHeight(boundsHeight);
3403 SetResponseRegion(frameSize, boundsRect.GetSize());
3404 ProcessBoundRectByTextShadow(boundsRect);
3405 ProcessBoundRectByTextMarquee(boundsRect);
3406 boundsRect.SetWidth(std::max(frameSize.Width(), boundsRect.Width()));
3407 boundsRect.SetHeight(std::max(frameSize.Height(), boundsRect.Height()));
3408 auto baselineOffset = LessOrEqual(baselineOffset_, 0) ? std::fabs(baselineOffset_) : 0;
3409 pManager_->GetPaintRegion(boundsRect, contentRect_.GetX(), contentRect_.GetY() + baselineOffset);
3410 overlayMod_->SetBoundsRect(boundsRect);
3411 return paintMethod;
3412 }
3413
SetResponseRegion(const SizeF & frameSize,const SizeF & boundsSize)3414 void TextPattern::SetResponseRegion(const SizeF& frameSize, const SizeF& boundsSize)
3415 {
3416 auto host = GetHost();
3417 CHECK_NULL_VOID(host);
3418 auto gestureHub = host->GetOrCreateGestureEventHub();
3419 CHECK_NULL_VOID(gestureHub);
3420 if (isUserSetResponseRegion_) {
3421 return;
3422 }
3423 std::vector<DimensionRect> hotZoneRegions;
3424 DimensionRect hotZoneRegion;
3425 hotZoneRegion.SetSize(DimensionSize(Dimension(std::max(boundsSize.Width(), frameSize.Width())),
3426 Dimension(std::max(frameSize.Height(), boundsSize.Height()))));
3427 hotZoneRegions.emplace_back(hotZoneRegion);
3428 gestureHub->SetResponseRegion(hotZoneRegions);
3429 }
3430
CreateModifier()3431 void TextPattern::CreateModifier()
3432 {
3433 if (!contentMod_) {
3434 contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_, WeakClaim(this));
3435 }
3436 if (!overlayMod_) {
3437 overlayMod_ = MakeRefPtr<TextOverlayModifier>();
3438 }
3439 if (isCustomFont_) {
3440 contentMod_->SetIsCustomFont(true);
3441 }
3442 }
3443
GetHandleIndex(const Offset & offset) const3444 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
3445 {
3446 return pManager_->GetGlyphIndexByCoordinate(offset);
3447 }
3448
OnHandleAreaChanged()3449 void TextPattern::OnHandleAreaChanged()
3450 {
3451 if (selectOverlay_->SelectOverlayIsOn()) {
3452 auto parentGlobalOffset = GetParentGlobalOffset();
3453 if (parentGlobalOffset != parentGlobalOffset_) {
3454 parentGlobalOffset_ = parentGlobalOffset;
3455 CalculateHandleOffsetAndShowOverlay();
3456 ShowSelectOverlay({ .menuIsShow = false, .animation = true });
3457 }
3458 }
3459 }
3460
RemoveAreaChangeInner()3461 void TextPattern::RemoveAreaChangeInner()
3462 {
3463 auto pipeline = PipelineContext::GetCurrentContextSafely();
3464 CHECK_NULL_VOID(pipeline);
3465 auto host = GetHost();
3466 CHECK_NULL_VOID(host);
3467 auto eventHub = host->GetEventHub<TextEventHub>();
3468 CHECK_NULL_VOID(eventHub);
3469 if (eventHub->HasOnAreaChanged()) {
3470 return;
3471 }
3472 pipeline->RemoveOnAreaChangeNode(host->GetId());
3473 }
3474
SetTextDetectEnable(bool enable)3475 void TextPattern::SetTextDetectEnable(bool enable)
3476 {
3477 auto host = GetHost();
3478 CHECK_NULL_VOID(host);
3479 dataDetectorAdapter_->frameNode_ = host;
3480 if (enable == textDetectEnable_) {
3481 return;
3482 }
3483 textDetectEnable_ = enable;
3484 if (textDetectEnable_) {
3485 auto pipeline = PipelineContext::GetCurrentContextSafely();
3486 CHECK_NULL_VOID(pipeline);
3487 auto callback = [weak = WeakClaim(this)]() {
3488 auto pattern = weak.Upgrade();
3489 CHECK_NULL_VOID(pattern);
3490 pattern->dataDetectorAdapter_->GetAIEntityMenu();
3491 };
3492 pipeline->SetConfigChangedCallback(host->GetId(), callback);
3493 } else {
3494 dataDetectorAdapter_->CancelAITask();
3495 }
3496 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3497 }
3498
CanStartAITask()3499 bool TextPattern::CanStartAITask()
3500 {
3501 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3502 if (textLayoutProperty) {
3503 return textDetectEnable_ && enabled_ && !IsSetObscured() &&
3504 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE;
3505 } else {
3506 return textDetectEnable_ && enabled_;
3507 }
3508 }
3509
NeedShowAIDetect()3510 bool TextPattern::NeedShowAIDetect()
3511 {
3512 return CanStartAITask() && !dataDetectorAdapter_->aiSpanMap_.empty();
3513 }
3514
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,std::function<void (int32_t,int32_t)> & onAppear,std::function<void ()> & onDisappear)3515 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
3516 std::function<void()>& menuBuilder, std::function<void(int32_t, int32_t)>& onAppear,
3517 std::function<void()>& onDisappear)
3518 {
3519 auto key = std::make_pair(spanType, responseType);
3520 auto it = selectionMenuMap_.find(key);
3521 if (it != selectionMenuMap_.end()) {
3522 if (menuBuilder == nullptr) {
3523 selectionMenuMap_.erase(it);
3524 return;
3525 }
3526 it->second->buildFunc = menuBuilder;
3527 it->second->onAppear = onAppear;
3528 it->second->onDisappear = onDisappear;
3529 return;
3530 }
3531
3532 auto selectionMenuParams =
3533 std::make_shared<SelectionMenuParams>(spanType, menuBuilder, onAppear, onDisappear, responseType);
3534 selectionMenuMap_[key] = selectionMenuParams;
3535 auto host = GetHost();
3536 CHECK_NULL_VOID(host);
3537 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
3538 }
3539
CloseSelectionMenu()3540 void TextPattern::CloseSelectionMenu()
3541 {
3542 textResponseType_ = TextResponseType::NONE;
3543 CloseSelectOverlay(true);
3544 }
3545
GetMenuParams(TextSpanType spanType,TextResponseType responseType)3546 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
3547 {
3548 auto key = std::make_pair(spanType, responseType);
3549 auto it = selectionMenuMap_.find(key);
3550 if (it != selectionMenuMap_.end()) {
3551 return it->second;
3552 }
3553
3554 TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
3555 return nullptr;
3556 }
3557
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)3558 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
3559 {
3560 auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
3561 std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
3562 menuParams = GetMenuParams(currentSpanType, responseType);
3563 if (menuParams == nullptr) {
3564 return;
3565 }
3566 CopyBindSelectionMenuParams(selectInfo, menuParams);
3567 }
3568
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)3569 void TextPattern::CopyBindSelectionMenuParams(
3570 SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
3571 {
3572 selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
3573 if (menuParams->onAppear) {
3574 auto weak = AceType::WeakClaim(this);
3575 auto callback = [weak, menuParams]() {
3576 auto pattern = weak.Upgrade();
3577 CHECK_NULL_VOID(pattern);
3578 CHECK_NULL_VOID(menuParams->onAppear);
3579
3580 auto& textSelector = pattern->textSelector_;
3581 auto selectStart = std::min(textSelector.baseOffset, textSelector.destinationOffset);
3582 auto selectEnd = std::max(textSelector.baseOffset, textSelector.destinationOffset);
3583 menuParams->onAppear(selectStart, selectEnd);
3584 };
3585 selectInfo.menuCallback.onAppear = std::move(callback);
3586 }
3587 selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
3588 }
3589
FireOnSelectionChange(int32_t start,int32_t end)3590 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
3591 {
3592 auto host = GetHost();
3593 CHECK_NULL_VOID(host);
3594 auto eventHub = host->GetEventHub<TextEventHub>();
3595 CHECK_NULL_VOID(eventHub);
3596 eventHub->FireOnSelectionChange(start, end);
3597 }
3598
OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback && onCreateMenuCallback,const NG::OnMenuItemClickCallback && onMenuItemClick)3599 void TextPattern::OnSelectionMenuOptionsUpdate(
3600 const NG::OnCreateMenuCallback&& onCreateMenuCallback, const NG::OnMenuItemClickCallback&& onMenuItemClick)
3601 {
3602 selectOverlay_->OnSelectionMenuOptionsUpdate(std::move(onCreateMenuCallback), std::move(onMenuItemClick));
3603 }
3604
StartVibratorByIndexChange(int32_t currentIndex,int32_t preIndex)3605 void TextPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
3606 {
3607 CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
3608 VibratorUtils::StartVibraFeedback("slide");
3609 }
3610
HandleSelectionChange(int32_t start,int32_t end)3611 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
3612 {
3613 if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
3614 return;
3615 }
3616 textSelector_.Update(start, end);
3617 UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
3618 FireOnSelectionChange(std::min(start, end), std::max(start, end));
3619 }
3620
IsSelectedBindSelectionMenu()3621 bool TextPattern::IsSelectedBindSelectionMenu()
3622 {
3623 auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
3624 return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
3625 }
3626
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)3627 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
3628 {
3629 UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
3630 if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
3631 textSelector_.StartEqualToDest()) {
3632 selectedType_ = TextSpanType::TEXT;
3633 }
3634 }
3635
UpdateSelectionType(const SelectionInfo & selection)3636 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
3637 {
3638 selectedType_ = TextSpanType::NONE;
3639 auto list = selection.GetSelection().resultObjects;
3640 bool imageSelected = false;
3641 bool textSelected = false;
3642 bool builderSelected = false;
3643 for (const auto& obj : list) {
3644 if (obj.type == SelectSpanType::TYPEIMAGE) {
3645 imageSelected = true;
3646 } else if (obj.type == SelectSpanType::TYPESPAN) {
3647 textSelected = true;
3648 } else if (obj.type == SelectSpanType::TYPEBUILDERSPAN) {
3649 builderSelected = true;
3650 }
3651 if ((imageSelected && textSelected) || (builderSelected && textSelected) ||
3652 (imageSelected && builderSelected)) {
3653 selectedType_ = TextSpanType::MIXED;
3654 return;
3655 }
3656 }
3657 if (imageSelected) {
3658 selectedType_ = TextSpanType::IMAGE;
3659 } else if (textSelected) {
3660 selectedType_ = TextSpanType::TEXT;
3661 } else if (builderSelected) {
3662 selectedType_ = TextSpanType::BUILDER;
3663 }
3664
3665 TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
3666 }
3667
GetSelectionSpanItemIndex(const MouseInfo & info)3668 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
3669 {
3670 RectF textContentRect = contentRect_;
3671 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
3672 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
3673 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
3674 info.GetLocalLocation().GetY() - textContentRect.GetY() };
3675 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
3676 spans_.empty() || pManager_->GetParagraphs().empty()) {
3677 return -1;
3678 }
3679 int32_t start = 0;
3680 bool isFind = false;
3681 int32_t index = -1;
3682 for (const auto& item : spans_) {
3683 index++;
3684 if (!item) {
3685 continue;
3686 }
3687 auto selectedRects = pManager_->GetRects(start, item->position);
3688 start = item->position;
3689 for (auto&& rect : selectedRects) {
3690 if (rect.IsInRegion(textOffset)) {
3691 isFind = true;
3692 break;
3693 }
3694 }
3695 if (isFind) {
3696 TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
3697 return index;
3698 }
3699 }
3700 return -1;
3701 }
3702
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)3703 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
3704 {
3705 int32_t itemLength = 1;
3706 ResultObject resultObject;
3707 resultObject.isDraggable = true;
3708 if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
3709 return resultObject;
3710 }
3711 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
3712 int32_t startPosition = endPosition - itemLength;
3713 if ((start <= startPosition) && (end >= endPosition)) {
3714 auto builderNode = DynamicCast<FrameNode>(uiNode);
3715 CHECK_NULL_RETURN(builderNode, resultObject);
3716 resultObject.spanPosition.spanIndex = index;
3717 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3718 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3719 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3720 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3721 resultObject.type = SelectSpanType::TYPEIMAGE;
3722 auto geometryNode = builderNode->GetGeometryNode();
3723 CHECK_NULL_RETURN(geometryNode, resultObject);
3724 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
3725 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
3726 resultObject.valueString = " ";
3727 }
3728 return resultObject;
3729 }
3730
SetStyledString(const RefPtr<SpanString> & value)3731 void TextPattern::SetStyledString(const RefPtr<SpanString>& value)
3732 {
3733 isSpanStringMode_ = true;
3734 auto host = GetHost();
3735 CHECK_NULL_VOID(host);
3736 CloseSelectOverlay();
3737 auto length = styledString_->GetLength();
3738 styledString_->RemoveCustomSpan();
3739 styledString_->ReplaceSpanString(0, length, value);
3740 spans_ = styledString_->GetSpanItems();
3741 ProcessSpanString();
3742 styledString_->AddCustomSpan();
3743 styledString_->SetFramNode(WeakClaim(host.GetRawPtr()));
3744 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3745 }
3746
GetTextStyleObject(const RefPtr<SpanNode> & node)3747 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
3748 {
3749 TextStyleResult textStyle;
3750 textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
3751 textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
3752 textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
3753 std::string fontFamilyValue;
3754 const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
3755 auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
3756 for (const auto& str : fontFamily) {
3757 fontFamilyValue += str;
3758 fontFamilyValue += ",";
3759 }
3760 fontFamilyValue =
3761 fontFamilyValue.substr(0, !fontFamilyValue.empty() ? static_cast<int32_t>(fontFamilyValue.size()) - 1 : 0);
3762 textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
3763 textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationValue(TextDecoration::NONE));
3764 textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
3765 textStyle.decorationStyle = static_cast<int32_t>(node->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
3766 textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
3767 auto lm = node->GetLeadingMarginValue({});
3768 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
3769 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToFp();
3770 textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToFp();
3771 textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToFp();
3772 textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToFp();
3773 } else {
3774 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
3775 textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToVp();
3776 textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToVp();
3777 textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToVp();
3778 }
3779 textStyle.fontFeature = node->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1"));
3780 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.size.Width().ToString();
3781 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.size.Height().ToString();
3782 textStyle.wordBreak = static_cast<int32_t>(node->GetWordBreakValue(WordBreak::BREAK_WORD));
3783 textStyle.lineBreakStrategy = static_cast<int32_t>(node->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY));
3784 textStyle.textShadows = node->GetTextShadowValue({});
3785 return textStyle;
3786 }
3787
GetChildByIndex(int32_t index) const3788 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
3789 {
3790 const auto& children = childNodes_;
3791 int32_t size = static_cast<int32_t>(children.size());
3792 if (index < 0 || index >= size) {
3793 return nullptr;
3794 }
3795 auto pos = children.begin();
3796 std::advance(pos, index);
3797 return *pos;
3798 }
3799
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3800 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3801 {
3802 bool selectFlag = false;
3803 ResultObject resultObject;
3804 if (!DynamicCast<SpanNode>(uinode)) {
3805 return resultObject;
3806 }
3807 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
3808 int32_t itemLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
3809 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
3810 int32_t startPosition = endPosition - itemLength;
3811
3812 if (startPosition >= start && endPosition <= end) {
3813 selectFlag = true;
3814 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3815 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3816 } else if (startPosition < start && endPosition <= end && endPosition > start) {
3817 selectFlag = true;
3818 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3819 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3820 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
3821 selectFlag = true;
3822 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3823 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3824 } else if (startPosition <= start && endPosition >= end) {
3825 selectFlag = true;
3826 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3827 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3828 }
3829 if (selectFlag) {
3830 resultObject.spanPosition.spanIndex = index;
3831 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3832 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3833 resultObject.type = SelectSpanType::TYPESPAN;
3834 SetResultObjectText(resultObject, spanItem);
3835 auto spanNode = DynamicCast<SpanNode>(uinode);
3836 resultObject.textStyle = GetTextStyleObject(spanNode);
3837 }
3838 return resultObject;
3839 }
3840
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)3841 void TextPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
3842 {
3843 CHECK_NULL_VOID(spanItem);
3844 resultObject.valueString = spanItem->content;
3845 }
3846
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3847 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3848 {
3849 int32_t itemLength = 1;
3850 ResultObject resultObject;
3851 if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
3852 return resultObject;
3853 }
3854 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
3855 int32_t startPosition = endPosition - itemLength;
3856 if ((start <= startPosition) && (end >= endPosition)) {
3857 auto imageNode = DynamicCast<FrameNode>(uinode);
3858 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
3859 resultObject.spanPosition.spanIndex = index;
3860 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3861 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3862 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3863 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3864 resultObject.type = SelectSpanType::TYPEIMAGE;
3865 if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
3866 resultObject.valueString = imageLayoutProperty->GetImageSourceInfo()->GetSrc();
3867 } else {
3868 resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
3869 }
3870 auto geometryNode = imageNode->GetGeometryNode();
3871 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
3872 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
3873 if (imageLayoutProperty->HasImageFit()) {
3874 resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
3875 }
3876 if (imageLayoutProperty->HasVerticalAlign()) {
3877 resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
3878 }
3879 if (imageLayoutProperty->GetMarginProperty()) {
3880 resultObject.imageStyle.margin = imageLayoutProperty->GetMarginProperty()->ToString();
3881 }
3882 auto imageRenderCtx = imageNode->GetRenderContext();
3883 if (imageRenderCtx->GetBorderRadius()) {
3884 BorderRadiusProperty brp;
3885 auto jsonObject = JsonUtil::Create(true);
3886 auto jsonBorder = JsonUtil::Create(true);
3887 InspectorFilter emptyFilter;
3888 imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, emptyFilter);
3889 resultObject.imageStyle.borderRadius = jsonObject->GetValue("borderRadius")->IsObject()
3890 ? jsonObject->GetValue("borderRadius")->ToString()
3891 : jsonObject->GetString("borderRadius");
3892 }
3893 }
3894 return resultObject;
3895 }
3896
OnSensitiveStyleChange(bool isSensitive)3897 void TextPattern::OnSensitiveStyleChange(bool isSensitive)
3898 {
3899 auto host = GetHost();
3900 CHECK_NULL_VOID(host);
3901 isSensitive_ = isSensitive;
3902 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3903 }
3904
IsSensitiveEnalbe()3905 bool TextPattern::IsSensitiveEnalbe()
3906 {
3907 auto host = GetHost();
3908 CHECK_NULL_RETURN(host, false);
3909 return isSensitive_ && host->IsPrivacySensitive();
3910 }
3911
ConvertGlobalToLocalOffset(const Offset & globalOffset)3912 Offset TextPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
3913 {
3914 auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
3915 selectOverlay_->RevertLocalPointWithTransform(localPoint);
3916 return Offset(localPoint.GetX(), localPoint.GetY());
3917 }
3918
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)3919 void TextPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
3920 {
3921 auto host = GetHost();
3922 CHECK_NULL_VOID(host);
3923 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
3924 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
3925 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
3926 auto options = imageItem->options;
3927 auto imageInfo = CreateImageSourceInfo(options);
3928 imageLayoutProperty->UpdateImageSourceInfo(imageInfo);
3929 auto index = host->GetChildren().size();
3930 imageNode->MountToParent(host, index);
3931 auto gesture = imageNode->GetOrCreateGestureEventHub();
3932 CHECK_NULL_VOID(gesture);
3933 gesture->SetHitTestMode(HitTestMode::HTMNONE);
3934 if (options.imageAttribute.has_value()) {
3935 auto imgAttr = options.imageAttribute.value();
3936 if (imgAttr.size.has_value()) {
3937 imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
3938 }
3939 if (imgAttr.verticalAlign.has_value()) {
3940 imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
3941 }
3942 if (imgAttr.objectFit.has_value()) {
3943 imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
3944 }
3945 if (imgAttr.marginProp.has_value()) {
3946 imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
3947 }
3948 if (imgAttr.paddingProp.has_value()) {
3949 imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
3950 }
3951 if (imgAttr.borderRadius.has_value()) {
3952 auto imageRenderCtx = imageNode->GetRenderContext();
3953 imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
3954 imageRenderCtx->SetClipToBounds(true);
3955 }
3956 }
3957 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
3958 imageNode->MarkModifyDone();
3959 imageItem->imageNodeId = imageNode->GetId();
3960 imageNode->SetImageItem(imageItem);
3961 childNodes_.emplace_back(imageNode);
3962 }
3963
CreateImageSourceInfo(const ImageSpanOptions & options)3964 ImageSourceInfo TextPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
3965 {
3966 std::string src;
3967 RefPtr<PixelMap> pixMap = nullptr;
3968 std::string bundleName;
3969 std::string moduleName;
3970 if (options.image.has_value()) {
3971 src = options.image.value();
3972 }
3973 if (options.imagePixelMap.has_value()) {
3974 pixMap = options.imagePixelMap.value();
3975 }
3976 if (options.bundleName.has_value()) {
3977 bundleName = options.bundleName.value();
3978 }
3979 if (options.moduleName.has_value()) {
3980 moduleName = options.moduleName.value();
3981 }
3982 #if defined(PIXEL_MAP_SUPPORTED)
3983 if (!options.imagePixelMap.has_value()) {
3984 return { src, bundleName, moduleName };
3985 }
3986 return ImageSourceInfo(pixMap);
3987 #else
3988 return { src, bundleName, moduleName };
3989 #endif
3990 }
3991
ProcessSpanString()3992 void TextPattern::ProcessSpanString()
3993 {
3994 auto host = GetHost();
3995 CHECK_NULL_VOID(host);
3996 textForDisplay_.clear();
3997 childNodes_.clear();
3998 dataDetectorAdapter_->textForAI_.clear();
3999 host->Clean();
4000 hasSpanStringLongPressEvent_ = false;
4001 hasUrlSpan_ = false;
4002
4003 // 适配AI&&挂载image节点
4004 auto imageChildren = host->GetChildren();
4005 for (const auto& span : spans_) {
4006 auto imageSpan = DynamicCast<ImageSpanItem>(span);
4007 if (imageSpan) {
4008 dataDetectorAdapter_->textForAI_ += '\n';
4009 MountImageNode(imageSpan);
4010 } else {
4011 dataDetectorAdapter_->textForAI_ += span->content;
4012 }
4013 if (span->onClick || span->urlOnRelease) {
4014 auto gestureEventHub = host->GetOrCreateGestureEventHub();
4015 InitClickEvent(gestureEventHub);
4016 }
4017 if (span->onLongPress) {
4018 auto gestureEventHub = host->GetOrCreateGestureEventHub();
4019 InitLongPressEvent(gestureEventHub);
4020 hasSpanStringLongPressEvent_ = true;
4021 }
4022 if (span->urlOnRelease) {
4023 hasUrlSpan_ = true;
4024 InitUrlMouseEvent();
4025 InitUrlTouchEvent();
4026 }
4027 textForDisplay_ += span->content;
4028 }
4029 if (dataDetectorAdapter_->textForAI_ != textForDisplay_) {
4030 dataDetectorAdapter_->aiDetectInitialized_ = false;
4031 }
4032 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
4033 dataDetectorAdapter_->StartAITask();
4034 }
4035 }
4036
SetExternalSpanItem(const std::list<RefPtr<SpanItem>> & spans)4037 void TextPattern::SetExternalSpanItem(const std::list<RefPtr<SpanItem>>& spans)
4038 {
4039 isSpanStringMode_ = !spans.empty();
4040 spans_ = spans;
4041 ProcessSpanString();
4042 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4043 CHECK_NULL_VOID(layoutProperty);
4044 layoutProperty->UpdateContent(textForDisplay_);
4045 }
4046
GetTextContentRect(bool isActualText) const4047 RectF TextPattern::GetTextContentRect(bool isActualText) const
4048 {
4049 auto textRect = contentRect_;
4050 auto host = GetHost();
4051 CHECK_NULL_RETURN(host, textRect);
4052 auto renderContext = host->GetRenderContext();
4053 CHECK_NULL_RETURN(renderContext, textRect);
4054 CHECK_NULL_RETURN(pManager_, textRect);
4055 if (!renderContext->GetClipEdge().value_or(false) &&
4056 LessNotEqual(textRect.Width(), pManager_->GetLongestLine())) {
4057 textRect.SetWidth(pManager_->GetLongestLine());
4058 }
4059 if (isActualText && !renderContext->GetClipEdge().value_or(false) &&
4060 LessNotEqual(textRect.Height(), pManager_->GetHeight())) {
4061 textRect.SetHeight(pManager_->GetHeight());
4062 }
4063 return textRect;
4064 }
4065
GetLineCount() const4066 size_t TextPattern::GetLineCount() const
4067 {
4068 CHECK_NULL_RETURN(pManager_, 0);
4069 return pManager_->GetLineCount();
4070 }
4071
DidExceedMaxLines() const4072 bool TextPattern::DidExceedMaxLines() const
4073 {
4074 CHECK_NULL_RETURN(pManager_, false);
4075 return pManager_->DidExceedMaxLines();
4076 }
4077
IsSetObscured()4078 bool TextPattern::IsSetObscured()
4079 {
4080 auto host = GetHost();
4081 CHECK_NULL_RETURN(host, false);
4082 auto renderContext = host->GetRenderContext();
4083 CHECK_NULL_RETURN(renderContext, false);
4084 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
4085 bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
4086 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
4087 return ifHaveObscured;
4088 }
4089
GetLineMetrics(int32_t lineNumber)4090 TextLineMetrics TextPattern::GetLineMetrics(int32_t lineNumber)
4091 {
4092 CHECK_NULL_RETURN(pManager_, TextLineMetrics());
4093 if (lineNumber < 0 || GetLineCount() == 0 || lineNumber > static_cast<int32_t>(GetLineCount()) - 1) {
4094 TAG_LOGI(AceLogTag::ACE_TEXT, "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d",
4095 lineNumber);
4096 return TextLineMetrics();
4097 }
4098 auto lineMetrics = pManager_->GetLineMetrics(lineNumber);
4099 RectF textContentRect = contentRect_;
4100 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4101 lineMetrics.x += textContentRect.GetX();
4102 lineMetrics.y += textContentRect.GetY();
4103 lineMetrics.baseline += textContentRect.GetY();
4104 return lineMetrics;
4105 }
4106
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)4107 std::vector<ParagraphManager::TextBox> TextPattern::GetRectsForRange(
4108 int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
4109 {
4110 if (start < 0 || end < 0 || start > end) {
4111 return {};
4112 }
4113 std::vector<ParagraphManager::TextBox> textBoxes = pManager_->GetRectsForRange(start, end, heightStyle, widthStyle);
4114 RectF textContentRect = contentRect_;
4115 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4116 std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
4117 for (auto& textBox : textBoxes) {
4118 ParagraphManager::TextBox adjustedTextBox = textBox;
4119 adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textContentRect.Left());
4120 adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textContentRect.Top());
4121 adjustedTextBoxes.push_back(adjustedTextBox);
4122 }
4123 return adjustedTextBoxes;
4124 }
4125
ConvertLocalOffsetToParagraphOffset(const Offset & offset)4126 Offset TextPattern::ConvertLocalOffsetToParagraphOffset(const Offset& offset)
4127 {
4128 RectF textContentRect = contentRect_;
4129 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4130 Offset paragraphOffset = { offset.GetX() - textContentRect.GetX(), offset.GetY() - textContentRect.GetY() };
4131 return paragraphOffset;
4132 }
4133
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)4134 PositionWithAffinity TextPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
4135 {
4136 Offset offset(x, y);
4137 return pManager_->GetGlyphPositionAtCoordinate(ConvertLocalOffsetToParagraphOffset(offset));
4138 }
4139
ProcessMarqueeVisibleAreaCallback()4140 void TextPattern::ProcessMarqueeVisibleAreaCallback()
4141 {
4142 if (!IsMarqueeOverflow()) {
4143 return;
4144 }
4145 auto host = GetHost();
4146 CHECK_NULL_VOID(host);
4147 auto pipeline = GetContext();
4148 CHECK_NULL_VOID(pipeline);
4149 auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
4150 auto pattern = weak.Upgrade();
4151 CHECK_NULL_VOID(pattern);
4152 CHECK_NULL_VOID(pattern->contentMod_);
4153 if (!pattern->IsMarqueeOverflow()) {
4154 return;
4155 }
4156 if (visible && Positive(ratio)) {
4157 pattern->contentMod_->ResumeAnimation();
4158 }
4159 if (!visible && NonPositive(ratio)) {
4160 pattern->contentMod_->PauseAnimation();
4161 }
4162 };
4163 std::vector<double> ratioList = { 0.0 };
4164 pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
4165 }
4166
OnTextOverflowChanged()4167 void TextPattern::OnTextOverflowChanged()
4168 {
4169 auto host = GetHost();
4170 CHECK_NULL_VOID(host);
4171 auto pipeline = GetContext();
4172 CHECK_NULL_VOID(pipeline);
4173 auto eventHub = host->GetEventHub<TextEventHub>();
4174 CHECK_NULL_VOID(eventHub);
4175 auto hasInnerCallabck = eventHub->HasVisibleAreaCallback(false);
4176 if (!hasInnerCallabck) {
4177 return;
4178 }
4179 auto hasUserCallback = eventHub->HasVisibleAreaCallback(true);
4180 if (!hasUserCallback) {
4181 pipeline->RemoveVisibleAreaChangeNode(host->GetId());
4182 }
4183 eventHub->CleanVisibleAreaCallback(false);
4184 }
4185
OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)4186 void TextPattern::OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)
4187 {
4188 selectOverlay_->OnAncestorNodeChanged(flag);
4189 }
4190
IsMarqueeOverflow() const4191 bool TextPattern::IsMarqueeOverflow() const
4192 {
4193 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4194 CHECK_NULL_RETURN(textLayoutProperty, false);
4195 return textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE;
4196 }
4197
UpdateFontColor(const Color & value)4198 void TextPattern::UpdateFontColor(const Color& value)
4199 {
4200 auto host = GetHost();
4201 CHECK_NULL_VOID(host);
4202 const auto& children = host->GetChildren();
4203 if (children.empty()) {
4204 auto paragraphs = pManager_->GetParagraphs();
4205 for (auto &&info : paragraphs) {
4206 auto paragraph = info.paragraph;
4207 CHECK_NULL_VOID(paragraph);
4208 auto length = paragraph->GetParagraphText().length();
4209 paragraph->UpdateColor(0, length, value);
4210 }
4211 } else {
4212 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
4213 }
4214 }
4215
MarkDirtyNodeRender()4216 void TextPattern::MarkDirtyNodeRender()
4217 {
4218 auto host = GetHost();
4219 CHECK_NULL_VOID(host);
4220 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4221 }
4222
BeforeCreatePaintWrapper()4223 void TextPattern::BeforeCreatePaintWrapper()
4224 {
4225 // mark content dirty
4226 if (contentMod_) {
4227 contentMod_->ContentChange();
4228 }
4229 }
4230
StartGestureSelection(int32_t start,int32_t end,const Offset & startOffset)4231 void TextPattern::StartGestureSelection(int32_t start, int32_t end, const Offset& startOffset)
4232 {
4233 scrollableParent_ = selectOverlay_->FindScrollableParent();
4234 TextGestureSelector::StartGestureSelection(start, end, startOffset);
4235 }
4236
GetTouchIndex(const OffsetF & offset)4237 int32_t TextPattern::GetTouchIndex(const OffsetF& offset)
4238 {
4239 OffsetF deltaOffset;
4240 if (scrollableParent_.Upgrade()) {
4241 auto parentGlobalOffset = GetParentGlobalOffset();
4242 deltaOffset = parentGlobalOffset - parentGlobalOffset_;
4243 }
4244 auto paragraphOffset =
4245 offset - deltaOffset - GetTextContentRect().GetOffset() + OffsetF(0.0f, std::min(GetBaselineOffset(), 0.0f));
4246 return GetHandleIndex({ paragraphOffset.GetX(), paragraphOffset.GetY() });
4247 }
4248
OnTextGestureSelectionUpdate(int32_t start,int32_t end,const TouchEventInfo & info)4249 void TextPattern::OnTextGestureSelectionUpdate(int32_t start, int32_t end, const TouchEventInfo& info)
4250 {
4251 selectOverlay_->TriggerScrollableParentToScroll(
4252 scrollableParent_.Upgrade(), info.GetTouches().front().GetGlobalLocation(), false);
4253 auto localOffset = info.GetTouches().front().GetLocalLocation();
4254 if (magnifierController_) {
4255 magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
4256 }
4257 if (start != textSelector_.GetStart()) {
4258 StartVibratorByIndexChange(start, textSelector_.GetStart());
4259 } else if (end != textSelector_.GetEnd()) {
4260 StartVibratorByIndexChange(end, textSelector_.GetEnd());
4261 }
4262 auto host = GetHost();
4263 CHECK_NULL_VOID(host);
4264 HandleSelectionChange(start, end);
4265 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4266 }
4267
OnTextGenstureSelectionEnd()4268 void TextPattern::OnTextGenstureSelectionEnd()
4269 {
4270 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), Offset(), true);
4271 if (magnifierController_) {
4272 magnifierController_->RemoveMagnifierFrameNode();
4273 }
4274 CalculateHandleOffsetAndShowOverlay();
4275 ShowSelectOverlay({ .animation = true });
4276 }
4277
ChangeHandleHeight(const GestureEvent & event,bool isFirst,bool isOverlayMode)4278 void TextPattern::ChangeHandleHeight(const GestureEvent& event, bool isFirst, bool isOverlayMode)
4279 {
4280 auto touchOffset = event.GetLocalLocation();
4281 if (!isOverlayMode) {
4282 touchOffset = event.GetGlobalLocation();
4283 }
4284 auto& currentHandle = isFirst ? textSelector_.firstHandle : textSelector_.secondHandle;
4285 bool isChangeFirstHandle = isFirst ? (!textSelector_.StartGreaterDest()) : textSelector_.StartGreaterDest();
4286 if (isChangeFirstHandle) {
4287 ChangeFirstHandleHeight(touchOffset, currentHandle);
4288 } else {
4289 ChangeSecondHandleHeight(touchOffset, currentHandle);
4290 }
4291 }
4292
ChangeFirstHandleHeight(const Offset & touchOffset,RectF & handleRect)4293 void TextPattern::ChangeFirstHandleHeight(const Offset& touchOffset, RectF& handleRect)
4294 {
4295 auto height = handleRect.Height();
4296 CalculateDefaultHandleHeight(height);
4297 bool isTouchHandleCircle = LessNotEqual(touchOffset.GetY(), handleRect.Top());
4298 if (!isTouchHandleCircle) {
4299 handleRect.SetTop(static_cast<float>(touchOffset.GetY()) - height / 2.0f);
4300 }
4301 handleRect.SetHeight(height);
4302 }
4303
ChangeSecondHandleHeight(const Offset & touchOffset,RectF & handleRect)4304 void TextPattern::ChangeSecondHandleHeight(const Offset& touchOffset, RectF& handleRect)
4305 {
4306 auto height = handleRect.Height();
4307 CalculateDefaultHandleHeight(height);
4308 bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
4309 auto handleOffsetY = isTouchHandleCircle
4310 ? handleRect.Bottom() - height
4311 : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
4312 handleRect.SetTop(handleOffsetY);
4313 handleRect.SetHeight(height);
4314 }
4315
CalculateDefaultHandleHeight(float & height)4316 void TextPattern::CalculateDefaultHandleHeight(float& height)
4317 {
4318 CHECK_NULL_VOID(textStyle_.has_value());
4319 #ifdef ENABLE_ROSEN_BACKEND
4320 MeasureContext content;
4321 content.textContent = "a";
4322 content.fontSize = textStyle_.value().GetFontSize();
4323 auto fontweight = StringUtils::FontWeightToString(textStyle_.value().GetFontWeight());
4324 content.fontWeight = fontweight;
4325 height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
4326 #endif
4327 }
4328
BeforeSyncGeometryProperties(const DirtySwapConfig & config)4329 void TextPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
4330 {
4331 if (afterLayoutCallback_.has_value()) {
4332 (*afterLayoutCallback_)();
4333 }
4334 }
4335
DoTextSelectionTouchCancel()4336 void TextPattern::DoTextSelectionTouchCancel()
4337 {
4338 CHECK_NULL_VOID(magnifierController_);
4339 magnifierController_->RemoveMagnifierFrameNode();
4340 ResetSelection();
4341 }
4342
GetCaretColor() const4343 std::string TextPattern::GetCaretColor() const
4344 {
4345 auto context = PipelineContext::GetCurrentContextSafely();
4346 CHECK_NULL_RETURN(context, "");
4347 auto theme = context->GetTheme<TextTheme>();
4348 CHECK_NULL_RETURN(theme, "");
4349 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4350 CHECK_NULL_RETURN(textLayoutProperty, "");
4351 return textLayoutProperty->GetCursorColorValue(theme->GetCaretColor()).ColorToString();
4352 }
4353
GetSelectedBackgroundColor() const4354 std::string TextPattern::GetSelectedBackgroundColor() const
4355 {
4356 auto context = PipelineContext::GetCurrentContextSafely();
4357 CHECK_NULL_RETURN(context, "");
4358 auto theme = context->GetTheme<TextTheme>();
4359 CHECK_NULL_RETURN(theme, "");
4360 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4361 CHECK_NULL_RETURN(textLayoutProperty, "");
4362 return textLayoutProperty->GetSelectedBackgroundColorValue(theme->GetSelectedColor()).ColorToString();
4363 }
4364 } // namespace OHOS::Ace::NG
4365