1 /*
2 * Copyright (c) 2022-2024 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 <future>
20 #include <iterator>
21 #include <stack>
22 #include <string>
23
24 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
25 #include "base/geometry/ng/offset_t.h"
26 #include "base/geometry/ng/point_t.h"
27 #include "base/geometry/ng/rect_t.h"
28 #include "base/geometry/offset.h"
29 #include "base/log/dump_log.h"
30 #include "base/log/log_wrapper.h"
31 #include "base/utils/multi_thread.h"
32 #include "base/utils/string_utils.h"
33 #include "base/utils/utf_helper.h"
34 #include "base/utils/utils.h"
35 #include "base/window/drag_window.h"
36 #include "core/common/ace_engine_ext.h"
37 #include "core/common/ai/data_detector_mgr.h"
38 #include "core/common/container.h"
39 #include "core/common/container_scope.h"
40 #include "core/common/font_manager.h"
41 #include "core/common/recorder/node_data_cache.h"
42 #include "core/common/udmf/udmf_client.h"
43 #include "core/common/vibrator/vibrator_utils.h"
44 #include "core/components/common/properties/text_style_parser.h"
45 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
46 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
47 #include "core/components_ng/pattern/text/text_styles.h"
48 #include "core/text/html_utils.h"
49 #include "core/components_ng/pattern/text/paragraph_util.h"
50 #include "core/text/text_emoji_processor.h"
51 #include "core/components_ng/render/render_property.h"
52 #ifdef ENABLE_ROSEN_BACKEND
53 #include "core/components/custom_paint/rosen_render_custom_paint.h"
54 #include "render_service_client/core/ui/rs_ui_director.h"
55 #endif
56
57 namespace OHOS::Ace::NG {
58 namespace {
59 constexpr double DIMENSION_VALUE = 16.0;
60 constexpr char COPY[] = "copy";
61 constexpr char SELECT_TEXT[] = "selectText";
62 constexpr const char SYMBOL_COLOR[] = "BLACK";
63 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
64 const std::u16string SYMBOL_TRANS = u"\uF0001";
65 const std::u16string WIDE_NEWLINE = u"\n";
66 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
67 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
68 constexpr Dimension CLICK_THRESHOLD = 5.0_vp;
69 const OffsetF DEFAULT_NEGATIVE_CARET_OFFSET {-1.0f, -1.0f};
70 constexpr int MAX_SELECTED_AI_ENTITY = 1;
71 constexpr int32_t PREVIEW_MENU_DELAY = 600;
72 constexpr int32_t DRAG_NODE_HIDE = 300;
73
74 const std::unordered_map<TextDataDetectType, std::string> TEXT_DETECT_MAP = {
75 { TextDataDetectType::PHONE_NUMBER, "phoneNum" }, { TextDataDetectType::URL, "url" },
76 { TextDataDetectType::EMAIL, "email" }, { TextDataDetectType::ADDRESS, "location" },
77 { TextDataDetectType::DATE_TIME, "datetime" }
78 };
79
IsJumpLink(const std::string & content)80 bool IsJumpLink(const std::string& content)
81 {
82 // start with http:// or https://
83 std::regex pattern(R"(https?://[^\s]+)");
84 return std::regex_match(content, pattern);
85 }
86 }; // namespace
87
~TextPattern()88 TextPattern::~TextPattern()
89 {
90 // node destruct, need to stop text race animation
91 CHECK_NULL_VOID(contentMod_);
92 contentMod_->StopTextRace();
93 }
94
OnWindowHide()95 void TextPattern::OnWindowHide()
96 {
97 if (magnifierController_) {
98 magnifierController_->RemoveMagnifierFrameNode();
99 }
100 CHECK_NULL_VOID(contentMod_);
101 contentMod_->PauseAnimation();
102 auto host = GetHost();
103 CHECK_NULL_VOID(host);
104 TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowHide [%{public}d]", host->GetId());
105 PauseSymbolAnimation();
106 }
107
OnWindowShow()108 void TextPattern::OnWindowShow()
109 {
110 CHECK_NULL_VOID(contentMod_);
111 contentMod_->ResumeAnimation();
112 auto host = GetHost();
113 CHECK_NULL_VOID(host);
114 TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowShow [%{public}d]", host->GetId());
115 ResumeSymbolAnimation();
116 }
117
OnAttachToFrameNode()118 void TextPattern::OnAttachToFrameNode()
119 {
120 auto host = GetHost();
121 THREAD_SAFE_NODE_CHECK(host, OnAttachToFrameNode); // call OnAttachToFrameNodeMultiThread() by multi thread
122 CHECK_NULL_VOID(host);
123 auto pipeline = host->GetContext();
124 CHECK_NULL_VOID(pipeline);
125 pipeline_ = pipeline;
126 auto fontManager = pipeline->GetFontManager();
127 if (fontManager) {
128 fontManager->AddFontNodeNG(host);
129 }
130 if (host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
131 if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
132 host->GetRenderContext()->UpdateClipEdge(true);
133 host->GetRenderContext()->SetClipToFrame(true);
134 }
135 }
136 InitSurfaceChangedCallback();
137 InitSurfacePositionChangedCallback();
138 pipeline->AddWindowStateChangedCallback(host->GetId());
139 pipeline->AddWindowSizeChangeCallback(host->GetId());
140 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
141 CHECK_NULL_VOID(textLayoutProperty);
142 auto theme = pipeline->GetTheme<TextTheme>();
143 CHECK_NULL_VOID(theme);
144 textLayoutProperty->UpdateTextAlign(theme->GetTextStyle().GetTextAlign());
145 textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
146 }
147
OnDetachFromFrameNode(FrameNode * node)148 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
149 {
150 // call OnDetachFromFrameNodeMultiThread() by multi thread
151 THREAD_SAFE_NODE_CHECK(node, OnDetachFromFrameNode, node);
152 if (dataDetectorAdapter_) {
153 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
154 }
155 selectOverlay_->CloseOverlay(false, CloseReason::CLOSE_REASON_NORMAL);
156 auto pipeline = pipeline_.Upgrade();
157 CHECK_NULL_VOID(pipeline);
158 if (HasSurfaceChangedCallback()) {
159 pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
160 }
161 if (HasSurfacePositionChangedCallback()) {
162 pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
163 }
164 auto frameNode = WeakClaim(node);
165 pipeline->RemoveFontNodeNG(frameNode);
166 auto fontManager = pipeline->GetFontManager();
167 if (fontManager) {
168 fontManager->UnRegisterCallbackNG(frameNode);
169 fontManager->RemoveVariationNodeNG(frameNode);
170 #ifdef ENABLE_ROSEN_BACKEND
171 if (Rosen::RSUIDirector::IsHybridRenderEnabled()) {
172 fontManager->RemoveHybridRenderNode(frameNode);
173 }
174 #endif
175 }
176 pipeline->RemoveOnAreaChangeNode(node->GetId());
177 pipeline->RemoveWindowStateChangedCallback(node->GetId());
178 pipeline->RemoveVisibleAreaChangeNode(node->GetId());
179 pipeline->RemoveWindowSizeChangeCallback(node->GetId());
180 RemoveFormVisibleChangeCallback(node->GetId());
181 }
182
OnAttachToMainTree()183 void TextPattern::OnAttachToMainTree()
184 {
185 auto host = GetHost();
186 THREAD_SAFE_NODE_CHECK(host, OnAttachToMainTree); // call OnAttachToMainTreeMultiThread() by multi thread
187 isDetachFromMainTree_ = false;
188 }
189
OnDetachFromMainTree()190 void TextPattern::OnDetachFromMainTree()
191 {
192 auto host = GetHost();
193 THREAD_SAFE_NODE_CHECK(host, OnDetachFromMainTree); // call OnDetachFromMainTreeMultiThread() by multi thread
194 isDetachFromMainTree_ = true;
195 }
196
CloseSelectOverlay()197 void TextPattern::CloseSelectOverlay()
198 {
199 CloseSelectOverlay(false);
200 }
201
CloseSelectOverlay(bool animation)202 void TextPattern::CloseSelectOverlay(bool animation)
203 {
204 auto host = GetHost();
205 FREE_NODE_CHECK(host, CloseSelectOverlay, animation); // call CloseSelectOverlayMultiThread() by multi thread
206 // Deprecated use selectOverlay_ instead.
207 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
208 selectOverlayProxy_->Close(animation);
209 RemoveAreaChangeInner();
210 }
211 selectOverlay_->CloseOverlay(animation, CloseReason::CLOSE_REASON_NORMAL);
212 }
213
ResetSelection()214 void TextPattern::ResetSelection()
215 {
216 if (textSelector_.IsValid() && !shiftFlag_) {
217 HandleSelectionChange(-1, -1);
218 auto host = GetHost();
219 CHECK_NULL_VOID(host);
220 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
221 }
222 }
223
GetIndexByOffset(const Offset & pos,int32_t & extend)224 void TextPattern::GetIndexByOffset(const Offset& pos, int32_t& extend)
225 {
226 auto selectionOffset = pos;
227 if (GreatNotEqual(selectionOffset.GetY(), pManager_->GetHeight())) {
228 selectionOffset.SetX(contentRect_.Width());
229 selectionOffset.SetY(pManager_->GetHeight());
230 }
231 extend = pManager_->GetGlyphIndexByCoordinate(selectionOffset, true);
232 if (pManager_->GetParagraphs().size() > 1) {
233 // paragraph may contain only newlines, look forward for non-newlines characters.
234 auto selectRects = pManager_->GetRects(extend, extend + 1);
235 if (selectRects.size() == 1 && NearZero(selectRects.back().Width())) {
236 auto selectStr = GetSelectedText(extend, extend + 1);
237 while (selectStr == u"\n" && extend > 0) {
238 --extend;
239 selectStr = GetSelectedText(extend, extend + 1);
240 }
241 }
242 }
243 }
244
InitSelection(const Offset & pos)245 void TextPattern::InitSelection(const Offset& pos)
246 {
247 CHECK_NULL_VOID(pManager_);
248 int32_t extend;
249 GetIndexByOffset(pos, extend);
250 int32_t start = 0;
251 int32_t end = 0;
252 if (!pManager_->GetWordBoundary(extend, start, end)) {
253 start = extend;
254 end = std::min(static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_,
255 extend + GetGraphemeClusterLength(textForDisplay_, extend));
256 }
257 auto host = GetHost();
258 CHECK_NULL_VOID(host);
259 if (SystemProperties::GetTextTraceEnabled()) {
260 TAG_LOGI(AceLogTag::ACE_TEXT,
261 "InitSelection[id:%{public}d][extend:%{public}d][start:%{public}d][end:%{public}d]", host->GetId(), extend,
262 start, end);
263 }
264 HandleSelectionChange(start, end);
265 }
266
ResetAISelected(AIResetSelectionReason reason)267 void TextPattern::ResetAISelected(AIResetSelectionReason reason)
268 {
269 textSelector_.ResetAiSelected();
270 if (SystemProperties::GetTextTraceEnabled()) {
271 auto host = GetHost();
272 CHECK_NULL_VOID(host);
273 TAG_LOGI(
274 AceLogTag::ACE_TEXT, "TextPattern::ResetAISelected id:%{public}d reason:%{public}d", host->GetId(), reason);
275 }
276 }
277
GetOrCreatePreviewMenuController()278 RefPtr<PreviewMenuController> TextPattern::GetOrCreatePreviewMenuController()
279 {
280 if (!previewController_) {
281 previewController_ = AceType::MakeRefPtr<PreviewMenuController>(WeakClaim(this));
282 }
283 return previewController_;
284 }
285
CanAIEntityDrag()286 bool TextPattern::CanAIEntityDrag()
287 {
288 CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
289 CHECK_NULL_RETURN(dataDetectorAdapter_->enablePreviewMenu_, false);
290 CHECK_NULL_RETURN(!IsSelected(), false);
291 // 如果已触发拉起预览菜单则,不允许做拖拽
292 if (previewController_ && previewController_->IsPreviewMenuShow()) {
293 return false;
294 }
295 return NeedShowAIDetect();
296 }
297
CheckAIPreviewMenuEnable()298 bool TextPattern::CheckAIPreviewMenuEnable()
299 {
300 return GetDataDetectorAdapter() && dataDetectorAdapter_->enablePreviewMenu_
301 && NeedShowAIDetect()
302 && IsShowHandle();
303 }
304
InitAiSelection(const Offset & globalOffset)305 void TextPattern::InitAiSelection(const Offset& globalOffset)
306 {
307 ResetAISelected(AIResetSelectionReason::INIT_SELECTION);
308 CHECK_NULL_VOID(pManager_ && selectOverlay_ && CheckAIPreviewMenuEnable());
309 int32_t extend = 0;
310 auto host = GetHost();
311 CHECK_NULL_VOID(host);
312 auto offset = host->GetPaintRectOffset(false, true);
313 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
314 if (selectOverlay_->HasRenderTransform()) {
315 localOffset = ConvertGlobalToLocalOffset(globalOffset);
316 }
317 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
318 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
319 GetIndexByOffset(textOffset, extend);
320 if (IsSelected() && LocalOffsetInRange(localOffset, textSelector_.GetTextStart(), textSelector_.GetTextEnd())) {
321 return;
322 }
323 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
324 CHECK_NULL_VOID(textLayoutProperty);
325 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::ELLIPSIS) {
326 auto range = pManager_->GetEllipsisTextRange();
327 if (LocalOffsetInRange(localOffset, static_cast<int32_t>(range.first), static_cast<int32_t>(range.second))) {
328 return;
329 }
330 }
331 int32_t start = 0;
332 int32_t end = 0;
333 bool isAiSpan = false;
334 if (GetDataDetectorAdapter()) {
335 auto aiSpanIter = dataDetectorAdapter_->aiSpanMap_.upper_bound(extend);
336 if (aiSpanIter != dataDetectorAdapter_->aiSpanMap_.begin()) {
337 --aiSpanIter;
338 }
339 start = aiSpanIter->second.start;
340 end = aiSpanIter->second.end;
341 if (extend >= start && extend < end && LocalOffsetInRange(localOffset, start, end)) {
342 isAiSpan = true;
343 }
344 }
345 if (isAiSpan && start >= 0 && end >= 0 && start < end) {
346 textSelector_.aiStart = start;
347 textSelector_.aiEnd = end;
348 }
349 TAG_LOGI(AceLogTag::ACE_TEXT, "InitAiSelection[id:%{public}d][extend:%{public}d][start:%{public}d][end:%{public}d]",
350 host->GetId(), extend, textSelector_.aiStart.value_or(-1), textSelector_.aiEnd.value_or(-1));
351 }
352
IsAiSelected()353 bool TextPattern::IsAiSelected()
354 {
355 return textSelector_.aiStart && textSelector_.aiEnd;
356 }
357
IsPreviewMenuShow()358 bool TextPattern::IsPreviewMenuShow()
359 {
360 CHECK_NULL_RETURN(previewController_, false);
361 return previewController_->IsPreviewMenuShow();
362 }
363
DragNodeDetachFromParent()364 void TextPattern::DragNodeDetachFromParent()
365 {
366 auto host = GetHost();
367 CHECK_NULL_VOID(host);
368 auto gestureHub = host->GetOrCreateGestureEventHub();
369 CHECK_NULL_VOID(gestureHub);
370 gestureHub->DragNodeDetachFromParent();
371 }
372
ShowAIEntityMenuForCancel()373 void TextPattern::ShowAIEntityMenuForCancel()
374 {
375 auto host = GetHost();
376 CHECK_NULL_VOID(host);
377 CHECK_NULL_VOID(IsAiSelected() && GetDataDetectorAdapter() && previewController_);
378 auto [start, end] = GetSelectedStartAndEnd();
379 ResetAISelected(AIResetSelectionReason::SHOW_FOR_CANCEL);
380 if (SystemProperties::GetTextTraceEnabled()) {
381 TAG_LOGI(AceLogTag::ACE_TEXT,
382 "TextPattern::ShowAIEntityMenuForCancel id:%{public}d IsPreviewMenuShow:%{public}d start:%{public}d, "
383 "end:%{public}d",
384 host->GetId(), previewController_->IsPreviewMenuShow(), start, end);
385 }
386 // ai预览菜单已显示,长按回落无需再选中
387 if (previewController_->IsPreviewMenuShow()) {
388 return;
389 }
390 auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(start);
391 if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
392 return;
393 }
394 HandleSelectionChange(start, end);
395 textResponseType_ = TextResponseType::LONG_PRESS;
396 UpdateSelectionSpanType(start, end);
397 CalculateHandleOffsetAndShowOverlay();
398 ShowSelectOverlay({ .animation = true });
399 TAG_LOGI(AceLogTag::ACE_TEXT,
400 "TextPattern::ShowAIEntityMenuForCancel id:%{public}d IsPreviewMenuShow:%{public}d start:%{public}d, "
401 "end:%{public}d",
402 host->GetId(), previewController_->IsPreviewMenuShow(), start, end);
403 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
404 }
405
GetSelectedAIData()406 AISpan TextPattern::GetSelectedAIData()
407 {
408 CHECK_NULL_RETURN(IsAiSelected(), AISpan());
409 CHECK_NULL_RETURN(GetDataDetectorAdapter(), AISpan());
410 auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(textSelector_.aiStart.value());
411 if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
412 return AISpan();
413 }
414 return aiSpan->second;
415 }
416
GetPreviewMenuAISpanClickrCallback(const AISpan & aiSpan)417 std::function<void()> TextPattern::GetPreviewMenuAISpanClickrCallback(const AISpan& aiSpan)
418 {
419 return [weak = WeakClaim(this), aiSpan, mainId = Container::CurrentIdSafelyWithCheck()]() {
420 ContainerScope scope(mainId);
421 auto pattern = weak.Upgrade();
422 CHECK_NULL_VOID(pattern);
423 auto dataDetectorAdapter_ = pattern->GetDataDetectorAdapter();
424 CHECK_NULL_VOID(dataDetectorAdapter_);
425 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
426 };
427 }
428
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)429 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
430 {
431 auto host = GetHost();
432 CHECK_NULL_VOID(host);
433 auto rect = host->GetGeometryNode()->GetFrameRect();
434 CHECK_NULL_VOID(pManager_);
435 auto computeSuccess = pManager_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
436 if (!computeSuccess) {
437 caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Height()), 0.0f);
438 }
439 }
440
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)441 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
442 {
443 parentGlobalOffset_ = GetParentGlobalOffset();
444 auto textContentGlobalOffset = selectOverlay_->GetHandleGlobalOffset() + contentRect_.GetOffset();
445 auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
446
447 // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
448 CaretMetricsF firstHandleMetrics;
449 CaretMetricsF secondHandleMetrics;
450 CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
451 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
452 OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
453 OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
454
455 textSelector_.selectionBaseOffset = firstHandleOffset;
456 textSelector_.selectionDestinationOffset = secondHandleOffset;
457
458 RectF firstHandle;
459 firstHandle.SetOffset(firstHandleOffset);
460 firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
461 firstHandle.SetOffset(OffsetF(firstHandle.GetX() - firstHandle.Width() / 2.0f, firstHandle.GetY()));
462 textSelector_.firstHandle = firstHandle;
463
464 RectF secondHandle;
465 secondHandle.SetOffset(secondHandleOffset);
466 secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
467 secondHandle.SetHeight(secondHandleMetrics.height);
468 secondHandle.SetOffset(OffsetF(secondHandle.GetX() - secondHandle.Width() / 2.0f, secondHandle.GetY()));
469 textSelector_.secondHandle = secondHandle;
470 }
471
GetSpansInfoInStyledString(int32_t start,int32_t end)472 std::list<ResultObject> TextPattern::GetSpansInfoInStyledString(int32_t start, int32_t end)
473 {
474 std::list<ResultObject> resultObjects;
475 int32_t imageIndex = 0;
476 for (const auto& item : spans_) {
477 auto obj = item->GetSpanResultObject(start, end);
478 if (obj.type == SelectSpanType::TYPEIMAGE) {
479 obj.spanPosition.spanIndex = imageIndex;
480 ++imageIndex;
481 }
482 if (obj.isInit) {
483 resultObjects.emplace_back(obj);
484 }
485 }
486 return resultObjects;
487 }
488
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)489 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
490 {
491 int32_t index = 0;
492 std::int32_t realEnd = 0;
493 std::int32_t realStart = 0;
494 SelectionInfo selection;
495 std::list<ResultObject> resultObjects;
496 auto length = GetTextContentLength();
497 if (method == GetSpansMethod::GETSPANS) {
498 realStart = (start == -1) ? 0 : start;
499 realEnd = (end == -1) ? length : end;
500 if (realStart > realEnd) {
501 std::swap(realStart, realEnd);
502 }
503 realStart = std::max(0, realStart);
504 realEnd = std::min(length, realEnd);
505 } else if (method == GetSpansMethod::ONSELECT) {
506 realEnd = std::min(length, end);
507 realStart = std::min(length, start);
508 }
509 selection.SetSelectionEnd(realEnd);
510 selection.SetSelectionStart(realStart);
511 // Verify that realStart, realEnd, and spans_ are valid
512 if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
513 (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
514 selection.SetResultObjectList(resultObjects);
515 return selection;
516 }
517 if (isSpanStringMode_) {
518 auto result = GetSpansInfoInStyledString(realStart, realEnd);
519 selection.SetResultObjectList(result);
520 return selection;
521 }
522 const auto& children = GetAllChildren();
523 for (const auto& uinode : children) {
524 if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
525 ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
526 if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
527 resultObjects.emplace_back(resultObject);
528 }
529 } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
530 ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
531 if (!resultObject.valueString.empty()) {
532 resultObjects.emplace_back(resultObject);
533 }
534 } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
535 ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
536 if (!resultObject.valueString.empty()) {
537 resultObjects.emplace_back(resultObject);
538 }
539 } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG ||
540 uinode->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
541 ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
542 if (!resultObject.valueString.empty()) {
543 resultObjects.emplace_back(resultObject);
544 }
545 }
546 index++;
547 }
548 selection.SetResultObjectList(resultObjects);
549 return selection;
550 }
551
GetTextContentLength()552 int32_t TextPattern::GetTextContentLength()
553 {
554 if (!spans_.empty()) {
555 return static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
556 }
557 return 0;
558 }
559
StartVibratorByLongPress()560 void TextPattern::StartVibratorByLongPress()
561 {
562 CHECK_NULL_VOID(isEnableHapticFeedback_);
563 VibratorUtils::StartVibraFeedback("longPress.light");
564 }
565
HandleLongPress(GestureEvent & info)566 void TextPattern::HandleLongPress(GestureEvent& info)
567 {
568 auto host = GetHost();
569 CHECK_NULL_VOID(host);
570 if (SystemProperties::GetTextTraceEnabled()) {
571 ACE_TEXT_SCOPED_TRACE("TextPattern::HandleLongPress[id:%d][isMousePressed:%d]", host->GetId(), isMousePressed_);
572 TAG_LOGI(AceLogTag::ACE_TEXT, "HandleLongPress[id:%{public}d]", host->GetId());
573 }
574 HandleSpanLongPressEvent(info);
575 if (!IsSelectableAndCopy() || isMousePressed_ || selectOverlay_->GetIsHandleDragging()) {
576 return;
577 }
578 auto hub = host->GetOrCreateEventHub<EventHub>();
579 CHECK_NULL_VOID(hub);
580 auto gestureHub = hub->GetOrCreateGestureEventHub();
581 CHECK_NULL_VOID(gestureHub);
582 auto localOffset = info.GetLocalLocation();
583 if (selectOverlay_->HasRenderTransform()) {
584 localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
585 }
586
587 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
588 if ((textLayoutProperty && textLayoutProperty->GetMaxLines() != 0) && textForDisplay_.length() != 0) {
589 StartVibratorByLongPress();
590 }
591
592 if (IsDraggable(localOffset)) {
593 // prevent long press event from being triggered when dragging
594 if (IsAiSelected()) {
595 ResetSelection();
596 CloseSelectOverlay(true);
597 ShowAIEntityPreviewMenuTimer();
598 }
599 gestureHub->SetIsTextDraggable(true);
600 return;
601 }
602 ResetAISelected(AIResetSelectionReason::LONG_PRESS);
603 gestureHub->SetIsTextDraggable(false);
604 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
605 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
606 InitSelection(textOffset);
607 textResponseType_ = TextResponseType::LONG_PRESS;
608 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
609 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
610 oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
611 CalculateHandleOffsetAndShowOverlay();
612 CloseSelectOverlay(true);
613 if (GetOrCreateMagnifier() && HasContent()) {
614 magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
615 }
616 StartGestureSelection(textSelector_.GetStart(), textSelector_.GetEnd(), localOffset);
617 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
618 }
619
ShowAIEntityPreviewMenuTimer()620 void TextPattern::ShowAIEntityPreviewMenuTimer()
621 {
622 CHECK_NULL_VOID(GetOrCreatePreviewMenuController());
623 CHECK_NULL_VOID(dragNode_);
624 auto host = GetHost();
625 CHECK_NULL_VOID(host);
626 auto&& task = [weakPtr = AceType::WeakClaim(this), node = WeakPtr<FrameNode>(host),
627 mainId = Container::CurrentIdSafelyWithCheck()]() {
628 ContainerScope scope(mainId);
629 auto pattern = weakPtr.Upgrade();
630 CHECK_NULL_VOID(pattern);
631 CHECK_NULL_VOID(pattern->IsAiSelected());
632 auto previewController = pattern->GetOrCreatePreviewMenuController();
633 CHECK_NULL_VOID(previewController);
634 auto dragNode = pattern->MoveDragNode();
635 CHECK_NULL_VOID(dragNode);
636 auto parent = dragNode->GetParent();
637 CHECK_NULL_VOID(parent);
638 pattern->PreviewDragNodeHideAnimation();
639 previewController->BindContextMenu(dragNode);
640 };
641 auto context = host->GetContext();
642 CHECK_NULL_VOID(context);
643 auto taskExecutor = context->GetTaskExecutor();
644 CHECK_NULL_VOID(taskExecutor);
645 taskExecutor->PostDelayedTask(
646 task, TaskExecutor::TaskType::UI, PREVIEW_MENU_DELAY, "ArkShowAIEntityPreviewMenuTimer");
647 }
648
PreviewDragNodeHideAnimation()649 void TextPattern::PreviewDragNodeHideAnimation()
650 {
651 CHECK_NULL_VOID(dragNode_);
652 auto renderContext = dragNode_->GetRenderContext();
653 CHECK_NULL_VOID(renderContext);
654 renderContext->UpdateOpacity(1.0f);
655 AnimationOption option;
656 option.SetDuration(DRAG_NODE_HIDE);
657 option.SetCurve(Curves::SHARP);
658 AnimationUtils::Animate(
659 option, [renderContext, mainId = Container::CurrentIdSafelyWithCheck()]() {
660 ContainerScope scope(mainId);
661 renderContext->UpdateOpacity(0.0);
662 });
663 }
664
CreateAIEntityMenu()665 RefPtr<FrameNode> TextPattern::CreateAIEntityMenu()
666 {
667 CHECK_NULL_RETURN(IsAiSelected() && GetDataDetectorAdapter(), nullptr);
668 auto aiSpan = dataDetectorAdapter_->aiSpanMap_.find(textSelector_.aiStart.value());
669 if (aiSpan == dataDetectorAdapter_->aiSpanMap_.end()) {
670 return nullptr;
671 }
672 auto host = GetHost();
673 CHECK_NULL_RETURN(host, nullptr);
674 SetOnClickMenu(aiSpan->second, nullptr, nullptr);
675 auto [isShowCopy, isShowSelectText] = GetCopyAndSelectable();
676 return dataDetectorAdapter_->CreateAIEntityMenu(aiSpan->second, host, { isShowCopy, isShowSelectText });
677 }
678
ShowShadow(const PointF & textOffset,const Color & color)679 bool TextPattern::ShowShadow(const PointF& textOffset, const Color& color)
680 {
681 CHECK_NULL_RETURN(overlayMod_, false);
682 CHECK_NULL_RETURN(hasUrlSpan_, false);
683 CHECK_NULL_RETURN(!spans_.empty() && pManager_, false);
684 int32_t start = 0;
685 for (const auto& item : spans_) {
686 if (!item) {
687 continue;
688 }
689 auto selectedRects = GetSelectedRects(start, item->position);
690 for (auto&& rect : selectedRects) {
691 if (!rect.IsInRegion(textOffset)) {
692 continue;
693 }
694 if (!item->urlOnRelease) {
695 overlayMod_->ClearSelectedForegroundColorAndRects();
696 MarkDirtySelf();
697 return false;
698 }
699 auto inter = GetStartAndEnd(start, item);
700 auto rects = GetSelectedRects(inter.first, inter.second);
701 overlayMod_->SetSelectedForegroundColorAndRects(rects, color.GetValue());
702 MarkDirtySelf();
703 return true;
704 }
705 start = item->position;
706 }
707 overlayMod_->ClearSelectedForegroundColorAndRects();
708 MarkDirtySelf();
709 return false;
710 }
711
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & spanItem)712 std::pair<int32_t, int32_t> TextPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& spanItem)
713 {
714 auto spanBases = styledString_->GetSpans(0, styledString_->GetLength(), SpanType::Url);
715 for (const auto& spanBase : spanBases) {
716 if (start >= spanBase->GetStartIndex() && start < spanBase->GetEndIndex()) {
717 return {spanBase->GetStartIndex(), spanBase->GetEndIndex()};
718 }
719 }
720 return {0, 0};
721 }
722
HandleSpanLongPressEvent(GestureEvent & info)723 void TextPattern::HandleSpanLongPressEvent(GestureEvent& info)
724 {
725 RectF textContentRect = contentRect_;
726 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
727 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
728
729 auto localLocation = info.GetLocalLocation();
730 if (selectOverlay_->HasRenderTransform()) {
731 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
732 }
733
734 auto host = GetHost();
735 CHECK_NULL_VOID(host);
736 auto renderContext = host->GetRenderContext();
737 CHECK_NULL_VOID(renderContext);
738 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
739 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
740 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
741 textContentRect = overlayMod_->GetBoundsRect();
742 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
743 }
744 auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info, const RectF& rect,
745 const PointF& textOffset) -> bool {
746 if (rect.IsInRegion(textOffset)) {
747 if (item && item->onLongPress) {
748 item->onLongPress(info);
749 }
750 return true;
751 }
752 return false;
753 };
754
755 if (textContentRect.IsInRegion(
756 PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
757 !spans_.empty() && pManager_) {
758 int32_t start = 0;
759 for (const auto& item : spans_) {
760 if (!item) {
761 continue;
762 }
763 auto selectedRects = GetSelectedRects(start, item->position);
764 for (auto&& rect : selectedRects) {
765 CHECK_NULL_VOID(!longPressFunc(item, info, rect, textOffset));
766 }
767 start = item->position;
768 }
769 }
770 }
771
772 // Deprecated: Use the TextSelectOverlay::OnHandleMove() instead.
773 // It is currently used by RichEditorPattern.
OnHandleMove(const RectF & handleRect,bool isFirstHandle)774 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
775 {
776 auto host = GetHost();
777 CHECK_NULL_VOID(host);
778 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
779 auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
780
781 auto localOffset = handleRect.GetOffset();
782
783 auto renderContext = host->GetRenderContext();
784 CHECK_NULL_VOID(renderContext);
785 auto clip = false;
786 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
787 clip = true;
788 }
789 if (renderContext->GetClipEdge().value_or(clip)) {
790 if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
791 localOffset.SetX(textContentGlobalOffset.GetX());
792 } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
793 localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
794 }
795
796 if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
797 localOffset.SetY(textContentGlobalOffset.GetY());
798 } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
799 localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
800 }
801 }
802
803 localOffset -= textPaintOffset;
804
805 CHECK_NULL_VOID(pManager_);
806 // the handle position is calculated based on the middle of the handle height.
807 UpdateSelectorOnHandleMove(localOffset, handleRect.Height(), isFirstHandle);
808 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
809
810 CHECK_NULL_VOID(selectOverlayProxy_);
811 auto start = textSelector_.GetTextStart();
812 auto end = textSelector_.GetTextEnd();
813 selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
814 }
815
UpdateSelectorOnHandleMove(const OffsetF & localOffset,float handleHeight,bool isFirstHandle)816 void TextPattern::UpdateSelectorOnHandleMove(const OffsetF& localOffset, float handleHeight, bool isFirstHandle)
817 {
818 if (isFirstHandle) {
819 auto start = GetHandleIndex(Offset(
820 localOffset.GetX(), localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() ? handleHeight : 0)));
821 HandleSelectionChange(start, textSelector_.destinationOffset);
822 } else {
823 auto end = GetHandleIndex(Offset(localOffset.GetX(),
824 localOffset.GetY() +
825 (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0) ? 0 : handleHeight)));
826 HandleSelectionChange(textSelector_.baseOffset, end);
827 }
828 }
829
IsSelectAll()830 bool TextPattern::IsSelectAll()
831 {
832 return textSelector_.GetTextStart() == 0 &&
833 textSelector_.GetTextEnd() == static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
834 }
835
GetSelectedText(int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const836 std::u16string TextPattern::GetSelectedText(int32_t start, int32_t end, bool includeStartHalf,
837 bool includeEndHalf, bool getSubstrDirectly) const
838 {
839 if (spans_.empty()) {
840 auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(textForDisplay_.length()));
841 auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(textForDisplay_.length())), 0,
842 static_cast<int32_t>(textForDisplay_.length()));
843 if (max - min < 0) {
844 return std::u16string();
845 }
846 if (getSubstrDirectly) {
847 return textForDisplay_.substr(min, max - min);
848 } else {
849 return TextEmojiProcessor::SubU16string(min, max - min, textForDisplay_, includeStartHalf, includeEndHalf);
850 }
851 }
852 std::u16string value;
853 int32_t tag = 0;
854 for (const auto& span : spans_) {
855 if (span->GetSymbolUnicode() != 0) {
856 tag = span->position == -1 ? tag + 1 : span->position;
857 continue;
858 }
859 if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
860 auto wideString = span->GetSpanContent();
861 auto max = std::min(span->position, end);
862 auto min = std::max(start, tag);
863 if (getSubstrDirectly) {
864 value += wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
865 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())));
866 } else {
867 value += TextEmojiProcessor::SubU16string(
868 std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
869 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())),
870 wideString, includeStartHalf, includeEndHalf);
871 }
872 } else if (span->position - 1 >= start && span->position != -1) {
873 // image span or custom span (span->placeholderIndex != -1)
874 value += u" ";
875 }
876 tag = span->position == -1 ? tag + 1 : span->position;
877 if (span->position >= end) {
878 break;
879 }
880 }
881 return value;
882 }
883
GetSelectedStartAndEnd()884 std::pair<int32_t, int32_t> TextPattern::GetSelectedStartAndEnd()
885 {
886 auto start = IsAiSelected() ? textSelector_.aiStart.value() : textSelector_.GetTextStart();
887 auto end = IsAiSelected() ? textSelector_.aiEnd.value() : textSelector_.GetTextEnd();
888 return std::make_pair(start, end);
889 }
890
HandleOnCopy()891 void TextPattern::HandleOnCopy()
892 {
893 CHECK_NULL_VOID(clipboard_);
894 CHECK_NULL_VOID(GetDataDetectorAdapter());
895 if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd() && !IsAiSelected()) {
896 HandleSelectionChange(-1, -1);
897 return;
898 }
899 auto [start, end] = GetSelectedStartAndEnd();
900 auto value = GetSelectedText(start, end, false, false, true);
901 if (IsSelectableAndCopy() || dataDetectorAdapter_->hasClickedMenuOption_) {
902 if (isSpanStringMode_ && !externalParagraph_) {
903 HandleOnCopySpanString();
904 } else if (!value.empty()) {
905 HandleOnCopyWithoutSpanString(UtfUtils::Str16DebugToStr8(value));
906 }
907 }
908 HiddenMenu();
909 CHECK_NULL_VOID(!value.empty());
910 auto host = GetHost();
911 CHECK_NULL_VOID(host);
912 auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
913 CHECK_NULL_VOID(eventHub);
914 eventHub->FireOnCopy(value);
915 }
916
HandleAIMenuOption(const std::string & labelInfo)917 void TextPattern::HandleAIMenuOption(const std::string& labelInfo)
918 {
919 // lableInfo can be used for further extension: multiple ai entity in selected range
920 // only support one ai entity's first function now, hence pick begin
921 CHECK_NE_VOID(isShowAIMenuOption_, true);
922 CHECK_NE_VOID(aiMenuOptions_.size(), 1);
923 auto aiSpan = aiMenuOptions_.begin()->second;
924 auto aiEntityType = aiSpan.type;
925 CHECK_NULL_VOID(GetDataDetectorAdapter());
926 auto menuOptionAndActions = dataDetectorAdapter_->textDetectResult_.
927 menuOptionAndAction[TEXT_DETECT_MAP.at(aiEntityType)];
928 CHECK_EQUAL_VOID(menuOptionAndActions.empty(), true);
929 HiddenMenu();
930 dataDetectorAdapter_->OnClickAIMenuOption(aiSpan, *menuOptionAndActions.begin(), nullptr);
931 }
932
HandleOnAskCelia()933 void TextPattern::HandleOnAskCelia()
934 {
935 CHECK_NULL_VOID(IsSelected());
936 CHECK_NULL_VOID(GetDataDetectorAdapter());
937 auto baseOffset = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
938 auto destinationOffset = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
939 auto selectedContent = GetSelectedText(baseOffset, destinationOffset, false, false, true);
940 auto menuOptionAndActions = dataDetectorAdapter_->textDetectResult_.
941 menuOptionAndAction["askCelia"];
942 CHECK_EQUAL_VOID(menuOptionAndActions.empty(), true);
943 HiddenMenu();
944 AISpan aiSpan{
945 .start = baseOffset,
946 .end = destinationOffset,
947 .content = UtfUtils::Str16DebugToStr8(selectedContent),
948 .type = TextDataDetectType::ASK_CELIA
949 };
950 dataDetectorAdapter_->OnClickAIMenuOption(aiSpan, *menuOptionAndActions.begin(), nullptr);
951 }
952
GetSpanItemAttributeUseForHtml(NG::FontStyle & fontStyle,NG::TextLineStyle & textLineStyle,const std::optional<TextStyle> & textStyle)953 void TextPattern::GetSpanItemAttributeUseForHtml(NG::FontStyle& fontStyle,
954 NG::TextLineStyle& textLineStyle, const std::optional<TextStyle>& textStyle)
955 {
956 if (!textStyle.has_value()) {
957 return;
958 }
959 fontStyle.UpdateFontSize(textStyle->GetFontSize());
960 fontStyle.UpdateTextColor(textStyle->GetTextColor());
961 fontStyle.UpdateTextShadow(textStyle->GetTextShadows());
962 fontStyle.UpdateItalicFontStyle(textStyle->GetFontStyle());
963 fontStyle.UpdateFontWeight(textStyle->GetFontWeight());
964 fontStyle.UpdateVariableFontWeight(textStyle->GetVariableFontWeight());
965 fontStyle.UpdateEnableVariableFontWeight(textStyle->GetEnableVariableFontWeight());
966 fontStyle.UpdateFontFamily(textStyle->GetFontFamilies());
967 fontStyle.UpdateFontFeature(textStyle->GetFontFeatures());
968 fontStyle.UpdateTextDecoration(textStyle->GetTextDecoration());
969 fontStyle.UpdateTextDecorationColor(textStyle->GetTextDecorationColor());
970 fontStyle.UpdateTextDecorationStyle(textStyle->GetTextDecorationStyle());
971 fontStyle.UpdateTextCase(textStyle->GetTextCase());
972 fontStyle.UpdateAdaptMinFontSize(textStyle->GetAdaptMinFontSize());
973 fontStyle.UpdateAdaptMaxFontSize(textStyle->GetAdaptMaxFontSize());
974 fontStyle.UpdateLetterSpacing(textStyle->GetLetterSpacing());
975 fontStyle.UpdateSymbolColorList(textStyle->GetSymbolColorList());
976 fontStyle.UpdateSymbolType(textStyle->GetSymbolType());
977 textLineStyle.UpdateLineHeight(textStyle->GetLineHeight());
978 textLineStyle.UpdateTextBaseline(textStyle->GetTextBaseline());
979 textLineStyle.UpdateBaselineOffset(textStyle->GetBaselineOffset());
980 textLineStyle.UpdateTextOverflow(textStyle->GetTextOverflow());
981 textLineStyle.UpdateTextAlign(textStyle->GetTextAlign());
982 textLineStyle.UpdateMaxLines(textStyle->GetMaxLines());
983 textLineStyle.UpdateTextIndent(textStyle->GetTextIndent());
984 textLineStyle.UpdateWordBreak(textStyle->GetWordBreak());
985 textLineStyle.UpdateEllipsisMode(textStyle->GetEllipsisMode());
986 textLineStyle.UpdateLineSpacing(textStyle->GetLineSpacing());
987 textLineStyle.UpdateLineBreakStrategy(textStyle->GetLineBreakStrategy());
988 textLineStyle.UpdateHalfLeading(textStyle->GetHalfLeading());
989 textLineStyle.UpdateAllowScale(textStyle->IsAllowScale());
990 textLineStyle.UpdateParagraphSpacing(textStyle->GetParagraphSpacing());
991 }
992
GetTaskExecutorItem()993 RefPtr<TaskExecutor> TextPattern::GetTaskExecutorItem()
994 {
995 auto host = GetHost();
996 CHECK_NULL_RETURN(host, nullptr);
997 auto pipeline = host->GetContext();
998 CHECK_NULL_RETURN(pipeline, nullptr);
999 return pipeline->GetTaskExecutor();
1000 }
1001
AsyncHandleOnCopySpanStringHtml(RefPtr<SpanString> & subSpanString)1002 void TextPattern::AsyncHandleOnCopySpanStringHtml(RefPtr<SpanString>& subSpanString)
1003 {
1004 auto taskExecutor = GetTaskExecutorItem();
1005 CHECK_NULL_VOID(taskExecutor);
1006 std::list<RefPtr<SpanItem>> spans = GetSpanSelectedContent();
1007 auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
1008 subSpanString->EncodeTlv(multiTypeRecordImpl->GetSpanStringBuffer());
1009 multiTypeRecordImpl->SetPlainText(subSpanString->GetString());
1010 taskExecutor->PostTask(
1011 [spans, multiTypeRecordImpl, weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor))]() {
1012 CHECK_NULL_VOID(multiTypeRecordImpl);
1013 std::string htmlText = HtmlUtils::ToHtml(spans);
1014 multiTypeRecordImpl->SetHtmlText(htmlText);
1015
1016 auto uiTaskExecutor = task.Upgrade();
1017 CHECK_NULL_VOID(uiTaskExecutor);
1018 uiTaskExecutor->PostTask(
1019 [weak, multiTypeRecordImpl]() {
1020 auto textPattern = weak.Upgrade();
1021 CHECK_NULL_VOID(textPattern && textPattern->clipboard_);
1022 RefPtr<PasteDataMix> pasteData = textPattern->clipboard_->CreatePasteDataMix();
1023 textPattern->clipboard_->AddMultiTypeRecord(pasteData, multiTypeRecordImpl);
1024 textPattern->clipboard_->SetData(pasteData, textPattern->copyOption_);
1025 }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopySpanStringHtmlSetClipboardData");
1026 }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopySpanStringHtml");
1027 }
1028
HandleOnCopySpanString()1029 void TextPattern::HandleOnCopySpanString()
1030 {
1031 auto [start, end] = GetSelectedStartAndEnd();
1032 auto subSpanString = styledString_->GetSubSpanString(start, end - start);
1033 subSpanString->isFromStyledStringMode = true;
1034 #if defined(PREVIEW)
1035 clipboard_->SetData(subSpanString->GetString(), copyOption_);
1036 return;
1037 #endif
1038 AsyncHandleOnCopySpanStringHtml(subSpanString);
1039 }
1040
GetSpanSelectedContent()1041 std::list<RefPtr<SpanItem>> TextPattern::GetSpanSelectedContent()
1042 {
1043 std::list<RefPtr<SpanItem>> spans;
1044 if (!textSelector_.IsValid()) {
1045 return spans;
1046 }
1047 auto selectStart = textSelector_.GetTextStart();
1048 auto selectEnd = textSelector_.GetTextEnd();
1049 int32_t tag = 0;
1050 for (const auto& item : spans_) {
1051 CHECK_NULL_CONTINUE(item);
1052 if (item->GetSymbolUnicode() != 0) {
1053 tag = item->position == -1 ? tag + 1 : item->position;
1054 continue;
1055 }
1056 std::u16string spanSelectedContent;
1057 if (item->position - 1 >= selectStart && item->placeholderIndex == -1 && item->position != -1) {
1058 auto wideString = item->GetSpanContent();
1059 auto max = std::min(item->position, selectEnd);
1060 auto min = std::max(selectStart, tag);
1061 spanSelectedContent = TextEmojiProcessor::SubU16string(
1062 std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
1063 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())),
1064 wideString, false, false);
1065 auto spanItem = MakeRefPtr<SpanItem>();
1066 NG::FontStyle fontStyle;
1067 NG::TextLineStyle textLineStyle;
1068 GetSpanItemAttributeUseForHtml(fontStyle, textLineStyle, item->GetTextStyle());
1069 spanItem->fontStyle = std::make_unique<FontStyle>(fontStyle);
1070 spanItem->textLineStyle = std::make_unique<TextLineStyle>(textLineStyle);
1071 spanItem->content = spanSelectedContent;
1072 spanItem->spanItemType = item->spanItemType;
1073 spans.emplace_back(spanItem);
1074 } else if (item->position - 1 >= selectStart && item->position != -1) {
1075 spanSelectedContent = u" ";
1076 auto spanItem = item->GetSameStyleSpanItem(true);
1077 spanItem->content = spanSelectedContent;
1078 spanItem->spanItemType = item->spanItemType;
1079 spans.emplace_back(spanItem);
1080 }
1081 tag = item->position == -1 ? tag + 1 : item->position;
1082 if (item->position >= selectEnd) {
1083 break;
1084 }
1085 }
1086 return spans;
1087 }
1088
AsyncHandleOnCopyWithoutSpanStringHtml(const std::string & pasteData)1089 void TextPattern::AsyncHandleOnCopyWithoutSpanStringHtml(const std::string& pasteData)
1090 {
1091 auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
1092 std::list<RefPtr<SpanItem>> spans;
1093 NG::FontStyle fontStyle;
1094 NG::TextLineStyle textLineStyle;
1095 if (spans_.empty()) {
1096 EncodeTlvNoChild(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
1097 GetSpanItemAttributeUseForHtml(fontStyle, textLineStyle, textStyle_);
1098 } else {
1099 EncodeTlvSpanItems(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
1100 spans = GetSpanSelectedContent();
1101 }
1102 auto taskExecutor = GetTaskExecutorItem();
1103 CHECK_NULL_VOID(taskExecutor);
1104 taskExecutor->PostTask(
1105 [pasteData, multiTypeRecordImpl, fontStyle, textLineStyle, spans,
1106 weak = WeakClaim(this), task = WeakClaim(RawPtr(taskExecutor))]() {
1107 auto textPattern = weak.Upgrade();
1108 CHECK_NULL_VOID(textPattern);
1109 CHECK_NULL_VOID(multiTypeRecordImpl);
1110 multiTypeRecordImpl->SetPlainText(pasteData);
1111 std::string htmlText = "";
1112 if (!textPattern->spans_.empty()) {
1113 htmlText = HtmlUtils::ToHtml(spans);
1114 } else {
1115 std::u16string content = UtfUtils::Str8DebugToStr16(pasteData);
1116 htmlText = HtmlUtils::ToHtmlForNormalType(fontStyle, textLineStyle, content);
1117 }
1118 multiTypeRecordImpl->SetHtmlText(htmlText);
1119 auto uiTaskExecutor = task.Upgrade();
1120 CHECK_NULL_VOID(uiTaskExecutor);
1121 uiTaskExecutor->PostTask(
1122 [weak, multiTypeRecordImpl]() {
1123 auto textPattern = weak.Upgrade();
1124 CHECK_NULL_VOID(textPattern && textPattern->clipboard_);
1125 RefPtr<PasteDataMix> pasteDataMix = textPattern->clipboard_->CreatePasteDataMix();
1126 textPattern->clipboard_->AddMultiTypeRecord(pasteDataMix, multiTypeRecordImpl);
1127 textPattern->clipboard_->SetData(pasteDataMix, textPattern->copyOption_);
1128 }, TaskExecutor::TaskType::UI, "AsyncHandleOnCopyWithoutSpanStringSetClipboardData");
1129 }, TaskExecutor::TaskType::BACKGROUND, "AsyncHandleOnCopyWithoutSpanStringHtml");
1130 }
1131
HandleOnCopyWithoutSpanString(const std::string & pasteData)1132 void TextPattern::HandleOnCopyWithoutSpanString(const std::string& pasteData)
1133 {
1134 #if defined(PREVIEW)
1135 clipboard_->SetData(pasteData, copyOption_);
1136 return;
1137 #endif
1138 AsyncHandleOnCopyWithoutSpanStringHtml(pasteData);
1139 }
1140
1141 #define WRITE_TLV_INHERIT(group, name, tag, type, inheritName) \
1142 if ((group)->Has##name()) { \
1143 TLVUtil::WriteUint8(buff, (tag)); \
1144 TLVUtil::Write##type(buff, (group)->prop##name.value()); \
1145 } else if (textStyle_.has_value()) { \
1146 auto temp##name = textStyle_->Get##inheritName(); \
1147 TLVUtil::WriteUint8(buff, (tag)); \
1148 TLVUtil::Write##type(buff, temp##name); \
1149 }
1150
1151 #define WRITE_TEXT_STYLE_TLV(group, name, tag, type) \
1152 do { \
1153 if ((group)->Has##name()) { \
1154 TLVUtil::WriteUint8(buff, (tag)); \
1155 TLVUtil::Write##type(buff, (group)->prop##name.value()); \
1156 } \
1157 } while (false)
1158
EncodeTlvNoChild(const std::string & pasteData,std::vector<uint8_t> & buff)1159 void TextPattern::EncodeTlvNoChild(const std::string& pasteData, std::vector<uint8_t>& buff)
1160 {
1161 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
1162 TLVUtil::WriteInt32(buff, 1);
1163
1164 TLVUtil::WriteInt32(buff, static_cast<int32_t>(SpanItemType::NORMAL));
1165 TLVUtil::WriteUint8(buff, TLV_SPANITEM_TAG);
1166 TLVUtil::WriteInt32(buff, 0);
1167 TLVUtil::WriteInt32(buff, pasteData.length());
1168 TLVUtil::WriteString(buff, pasteData);
1169 EncodeTlvFontStyleNoChild(buff);
1170 EncodeTlvTextLineStyleNoChild(buff);
1171 TLVUtil::WriteUint8(buff, TLV_SPANITEM_END_TAG);
1172
1173 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
1174 TLVUtil::WriteString(buff, pasteData);
1175 TLVUtil::WriteUint8(buff, TLV_END);
1176 }
1177
EncodeTlvFontStyleNoChild(std::vector<uint8_t> & buff)1178 void TextPattern::EncodeTlvFontStyleNoChild(std::vector<uint8_t>& buff)
1179 {
1180 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1181 CHECK_NULL_VOID(textLayoutProperty);
1182 auto& fontStyle = textLayoutProperty->GetFontStyle();
1183 CHECK_NULL_VOID(fontStyle);
1184 WRITE_TLV_INHERIT(fontStyle, FontSize, TLV_SPAN_FONT_STYLE_FONTSIZE, Dimension, FontSize);
1185 WRITE_TLV_INHERIT(fontStyle, TextColor, TLV_SPAN_FONT_STYLE_TEXTCOLOR, Color, TextColor);
1186 WRITE_TLV_INHERIT(fontStyle, TextShadow, TLV_SPAN_FONT_STYLE_TEXTSHADOW, TextShadows, TextShadows);
1187 WRITE_TLV_INHERIT(fontStyle, ItalicFontStyle, TLV_SPAN_FONT_STYLE_ITALICFONTSTYLE, FontStyle, FontStyle);
1188 WRITE_TLV_INHERIT(fontStyle, FontWeight, TLV_SPAN_FONT_STYLE_FONTWEIGHT, FontWeight, FontWeight);
1189 WRITE_TLV_INHERIT(fontStyle, FontFamily, TLV_SPAN_FONT_STYLE_FONTFAMILY, FontFamily, FontFamilies);
1190 WRITE_TLV_INHERIT(fontStyle, FontFeature, TLV_SPAN_FONT_STYLE_FONTFEATURE, FontFeature, FontFeatures);
1191 WRITE_TLV_INHERIT(
1192 fontStyle, TextDecorationColor, TLV_SPAN_FONT_STYLE_TEXTDECORATIONCOLOR, Color, TextDecorationColor);
1193 WRITE_TLV_INHERIT(fontStyle, TextDecorationStyle, TLV_SPAN_FONT_STYLE_TEXTDECORATIONSTYLE, TextDecorationStyle,
1194 TextDecorationStyle);
1195 WRITE_TLV_INHERIT(fontStyle, TextCase, TLV_SPAN_FONT_STYLE_TEXTCASE, TextCase, TextCase);
1196 WRITE_TLV_INHERIT(fontStyle, AdaptMinFontSize, TLV_SPAN_FONT_STYLE_ADPATMINFONTSIZE, Dimension, AdaptMinFontSize);
1197 WRITE_TLV_INHERIT(fontStyle, AdaptMaxFontSize, TLV_SPAN_FONT_STYLE_ADPATMAXFONTSIZE, Dimension, AdaptMaxFontSize);
1198 WRITE_TLV_INHERIT(fontStyle, LetterSpacing, TLV_SPAN_FONT_STYLE_LETTERSPACING, Dimension, LetterSpacing);
1199 WRITE_TLV_INHERIT(fontStyle, LineThicknessScale, TLV_SPAN_FONT_STYLE_LineThicknessScale, Float,
1200 LineThicknessScale);
1201 if (fontStyle->HasTextDecoration()) {
1202 TLVUtil::WriteTextDecorations(buff, fontStyle->GetTextDecoration().value());
1203 } else if (textStyle_.has_value()) {
1204 TLVUtil::WriteTextDecorations(buff, textStyle_->GetTextDecoration());
1205 }
1206 }
1207
EncodeTlvTextLineStyleNoChild(std::vector<uint8_t> & buff)1208 void TextPattern::EncodeTlvTextLineStyleNoChild(std::vector<uint8_t>& buff)
1209 {
1210 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1211 CHECK_NULL_VOID(textLayoutProperty);
1212 auto& textLineStyle = textLayoutProperty->GetTextLineStyle();
1213 CHECK_NULL_VOID(textLineStyle);
1214 WRITE_TLV_INHERIT(textLineStyle, LineHeight, TLV_SPAN_TEXT_LINE_STYLE_LINEHEIGHT, Dimension, LineHeight);
1215 WRITE_TLV_INHERIT(textLineStyle, LineSpacing, TLV_SPAN_TEXT_LINE_STYLE_LINESPACING, Dimension, LineSpacing);
1216 WRITE_TLV_INHERIT(textLineStyle, TextBaseline, TLV_SPAN_TEXT_LINE_STYLE_TEXTBASELINE, TextBaseline, TextBaseline);
1217 WRITE_TLV_INHERIT(textLineStyle, TextOverflow, TLV_SPAN_TEXT_LINE_STYLE_TEXTOVERFLOW, TextOverflow, TextOverflow);
1218 WRITE_TLV_INHERIT(textLineStyle, TextAlign, TLV_SPAN_TEXT_LINE_STYLE_TEXTALIGN, TextAlign, TextAlign);
1219 WRITE_TEXT_STYLE_TLV(textLineStyle, MaxLength, TLV_SPAN_TEXT_LINE_STYLE_MAXLENGTH, Int32);
1220 WRITE_TLV_INHERIT(textLineStyle, MaxLines, TLV_SPAN_TEXT_LINE_STYLE_MAXLINES, Int32, MaxLines);
1221 WRITE_TEXT_STYLE_TLV(
1222 textLineStyle, HeightAdaptivePolicy, TLV_SPAN_TEXT_LINE_STYLE_HEIGHTADAPTIVEPOLICY, TextHeightAdaptivePolicy);
1223 WRITE_TLV_INHERIT(textLineStyle, TextIndent, TLV_SPAN_TEXT_LINE_STYLE_TEXTINDENT, Dimension, TextIndent);
1224 WRITE_TEXT_STYLE_TLV(textLineStyle, LeadingMargin, TLV_SPAN_TEXT_LINE_STYLE_LEADINGMARGIN, LeadingMargin);
1225 WRITE_TLV_INHERIT(textLineStyle, WordBreak, TLV_SPAN_TEXT_LINE_STYLE_WORDBREAK, WordBreak, WordBreak);
1226 WRITE_TLV_INHERIT(textLineStyle, LineBreakStrategy, TLV_SPAN_TEXT_LINE_STYLE_LINEBREAKSTRATEGY, LineBreakStrategy,
1227 LineBreakStrategy);
1228 WRITE_TLV_INHERIT(textLineStyle, EllipsisMode, TLV_SPAN_TEXT_LINE_STYLE_ELLIPSISMODE, EllipsisMode, EllipsisMode);
1229 }
1230
EncodeTlvSpanItems(const std::string & pasteData,std::vector<uint8_t> & buff)1231 void TextPattern::EncodeTlvSpanItems(const std::string& pasteData, std::vector<uint8_t>& buff)
1232 {
1233 auto [start, end] = GetSelectedStartAndEnd();
1234 std::list<RefPtr<NG::SpanItem>> selectSpanItems;
1235 int32_t ignoreLength = 0;
1236 for (const auto& spanItem : spans_) {
1237 int32_t oldStart = spanItem->position - static_cast<int32_t>(spanItem->length);
1238 int32_t oldEnd = spanItem->position;
1239 if (oldEnd <= start || end <= oldStart) {
1240 continue;
1241 }
1242 if (spanItem->spanItemType == SpanItemType::SYMBOL) {
1243 ignoreLength += static_cast<int32_t>(spanItem->length);
1244 continue;
1245 }
1246 auto spanStart = oldStart <= start ? 0 : oldStart - start;
1247 auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
1248 auto newSpanItem = spanItem->GetSameStyleSpanItem(true);
1249 newSpanItem->interval = { spanStart - ignoreLength, spanEnd - ignoreLength };
1250 newSpanItem->content = spanItem->content
1251 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
1252 selectSpanItems.emplace_back(newSpanItem);
1253 }
1254
1255 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
1256 TLVUtil::WriteInt32(buff, selectSpanItems.size());
1257 for (auto it = selectSpanItems.begin(); it != selectSpanItems.end(); ++it) {
1258 auto spanItem = (*it);
1259 if (spanItem->spanItemType == SpanItemType::CustomSpan) {
1260 TLVUtil::WriteInt32(buff, static_cast<int32_t>(SpanItemType::NORMAL));
1261 auto placeHolderSpan = AceType::MakeRefPtr<NG::SpanItem>();
1262 placeHolderSpan->content = u" ";
1263 placeHolderSpan->interval = spanItem->interval;
1264 placeHolderSpan->EncodeTlv(buff);
1265 continue;
1266 }
1267 TLVUtil::WriteInt32(buff, static_cast<int32_t>(spanItem->spanItemType));
1268 spanItem->EncodeTlv(buff);
1269 }
1270 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
1271 TLVUtil::WriteString(buff, pasteData);
1272 TLVUtil::WriteUint8(buff, TLV_END);
1273 }
1274
HiddenMenu()1275 void TextPattern::HiddenMenu()
1276 {
1277 if (IsUsingMouse()) {
1278 CloseSelectOverlay();
1279 } else {
1280 selectOverlay_->HideMenu();
1281 }
1282 }
1283
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)1284 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
1285 {
1286 auto host = GetHost();
1287 // call SetTextSelectionMultiThread() by multi thread
1288 FREE_NODE_CHECK(host, SetTextSelection, selectionStart, selectionEnd);
1289 CHECK_NULL_VOID(host);
1290 if (SystemProperties::GetTextTraceEnabled()) {
1291 ACE_TEXT_SCOPED_TRACE("TextPattern::SetTextSelection[id:%d][selectionStart:%d][selectionStart:%d]",
1292 host->GetId(), selectionStart, selectionEnd);
1293 }
1294 auto eventHub = host->GetOrCreateEventHub<EventHub>();
1295 CHECK_NULL_VOID(eventHub);
1296 auto context = host->GetContext();
1297 if (context) {
1298 context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
1299 auto textPattern = weak.Upgrade();
1300 CHECK_NULL_VOID(textPattern);
1301 auto host = textPattern->GetHost();
1302 CHECK_NULL_VOID(host);
1303 auto geometryNode = host->GetGeometryNode();
1304 CHECK_NULL_VOID(geometryNode);
1305 auto frameRect = geometryNode->GetFrameRect();
1306 if (frameRect.IsEmpty()) {
1307 return;
1308 }
1309 auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
1310 CHECK_NULL_VOID(textLayoutProperty);
1311 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1312 if (mode == TextSelectableMode::UNSELECTABLE ||
1313 textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
1314 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE ||
1315 textPattern->GetTextEffect()) {
1316 return;
1317 }
1318 if (!textPattern->IsSetObscured() && eventHub->IsEnabled()) {
1319 textPattern->ActSetSelection(selectionStart, selectionEnd);
1320 }
1321 });
1322 }
1323 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
1324 }
1325
GetRenderContext()1326 RefPtr<RenderContext> TextPattern::GetRenderContext()
1327 {
1328 auto frameNode = GetHost();
1329 CHECK_NULL_RETURN(frameNode, nullptr);
1330 return frameNode->GetRenderContext();
1331 }
1332
1333 // ret: whether show aiMenuOption
PrepareAIMenuOptions(std::unordered_map<TextDataDetectType,AISpan> & aiMenuOptions)1334 bool TextPattern::PrepareAIMenuOptions(
1335 std::unordered_map<TextDataDetectType, AISpan>& aiMenuOptions)
1336 {
1337 aiMenuOptions.clear();
1338 CHECK_NULL_RETURN(IsSelected(), false);
1339 CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
1340 int selectedAiEntityNum = 0;
1341 auto baseOffset = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
1342 auto destinationOffset = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
1343 auto spanIter = dataDetectorAdapter_->aiSpanMap_.lower_bound(baseOffset);
1344
1345 for (;spanIter != dataDetectorAdapter_->aiSpanMap_.end(); spanIter++) {
1346 auto aiSpanStart = spanIter->first;
1347 auto aiSpanEnd = spanIter->second.end; // [start, end)
1348 if (aiSpanStart >= baseOffset && aiSpanEnd <= destinationOffset) {
1349 ++selectedAiEntityNum;
1350 } else {
1351 break;
1352 }
1353 if (selectedAiEntityNum > MAX_SELECTED_AI_ENTITY) {
1354 break;
1355 } else { // put ai span functions
1356 aiMenuOptions[spanIter->second.type] = spanIter->second;
1357 }
1358 }
1359 return selectedAiEntityNum == MAX_SELECTED_AI_ENTITY;
1360 }
1361
UpdateAIMenuOptions()1362 void TextPattern::UpdateAIMenuOptions()
1363 {
1364 if ((copyOption_ == CopyOptions::Local || copyOption_ == CopyOptions::Distributed) &&
1365 NeedShowAIDetect()) {
1366 isShowAIMenuOption_ = PrepareAIMenuOptions(aiMenuOptions_);
1367 } else {
1368 isShowAIMenuOption_ = false;
1369 }
1370 if (copyOption_ == CopyOptions::Local || copyOption_ == CopyOptions::Distributed) {
1371 if (NeedShowAIDetect()) {
1372 SetIsAskCeliaEnabled(!isShowAIMenuOption_);
1373 } else {
1374 SetIsAskCeliaEnabled(true);
1375 }
1376 } else {
1377 SetIsAskCeliaEnabled(false);
1378 }
1379 if (!IsSupportAskCelia()) {
1380 SetIsAskCeliaEnabled(false);
1381 }
1382 CHECK_NULL_VOID(GetDataDetectorAdapter());
1383 if (isAskCeliaEnabled_ && !NeedShowAIDetect() &&
1384 dataDetectorAdapter_->textDetectResult_.menuOptionAndAction.empty()) {
1385 dataDetectorAdapter_->GetAIEntityMenu();
1386 }
1387 }
1388
ProcessOverlay(const OverlayRequest & request)1389 void TextPattern::ProcessOverlay(const OverlayRequest& request)
1390 {
1391 selectOverlay_->ProcessOverlay(request);
1392 }
1393
ShowSelectOverlay(const OverlayRequest & request)1394 void TextPattern::ShowSelectOverlay(const OverlayRequest& request)
1395 {
1396 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1397 CHECK_NULL_VOID(textLayoutProperty);
1398 if (textLayoutProperty->GetMaxLines() == 0) {
1399 CloseSelectOverlay();
1400 ResetSelection();
1401 return;
1402 }
1403 UpdateAIMenuOptions();
1404 ProcessOverlay(request);
1405 }
1406
HandleOnSelectAll()1407 void TextPattern::HandleOnSelectAll()
1408 {
1409 auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
1410 HandleSelectionChange(0, textSize);
1411 CalculateHandleOffsetAndShowOverlay();
1412 CloseSelectOverlay(true);
1413 if (IsUsingMouse()) {
1414 if (IsSelected()) {
1415 selectOverlay_->SetSelectionHoldCallback();
1416 }
1417 } else {
1418 ShowSelectOverlay({ .animation = true });
1419 }
1420 auto host = GetHost();
1421 CHECK_NULL_VOID(host);
1422 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1423 ResetOriginCaretPosition();
1424 }
1425
IsShowTranslate()1426 bool TextPattern::IsShowTranslate()
1427 {
1428 auto host = GetHost();
1429 CHECK_NULL_RETURN(host, false);
1430 auto context = host->GetContext();
1431 CHECK_NULL_RETURN(context, false);
1432 auto textTheme = context->GetTheme<TextTheme>();
1433 CHECK_NULL_RETURN(textTheme, false);
1434 return textTheme->IsShowTranslate();
1435 }
1436
IsShowSearch()1437 bool TextPattern::IsShowSearch()
1438 {
1439 auto container = Container::Current();
1440 if (container && container->IsSceneBoardWindow()) {
1441 return false;
1442 }
1443 auto host = GetHost();
1444 CHECK_NULL_RETURN(host, false);
1445 auto context = host->GetContext();
1446 CHECK_NULL_RETURN(context, false);
1447 auto textTheme = context->GetTheme<TextTheme>();
1448 CHECK_NULL_RETURN(textTheme, false);
1449 return textTheme->IsShowSearch();
1450 }
1451
IsSupportAskCelia()1452 bool TextPattern::IsSupportAskCelia()
1453 {
1454 auto host = GetHost();
1455 CHECK_NULL_RETURN(host, false);
1456 auto context = host->GetContext();
1457 CHECK_NULL_RETURN(context, false);
1458 auto textTheme = context->GetTheme<TextTheme>();
1459 CHECK_NULL_RETURN(textTheme, false);
1460 return textTheme->IsSupportAskCelia();
1461 }
1462
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)1463 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
1464 {
1465 constexpr int32_t longPressDelay = 600;
1466 if (longPressEvent_) {
1467 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
1468 return;
1469 }
1470 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1471 auto pattern = weak.Upgrade();
1472 CHECK_NULL_VOID(pattern);
1473 pattern->sourceType_ = info.GetSourceDevice();
1474 pattern->HandleLongPress(info);
1475 };
1476 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
1477
1478 // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
1479 // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
1480 // be slightly longer to ensure that order.
1481 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
1482
1483 auto onTextSelectorChange = [weak = WeakClaim(this)]() {
1484 auto pattern = weak.Upgrade();
1485 CHECK_NULL_VOID(pattern);
1486 auto frameNode = pattern->GetHost();
1487 CHECK_NULL_VOID(frameNode);
1488 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
1489 };
1490 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
1491 }
1492
OnHandleTouchUp()1493 void TextPattern::OnHandleTouchUp()
1494 {
1495 CloseSelectOverlay();
1496 ResetSelection();
1497 }
1498
HandleClickEvent(GestureEvent & info)1499 void TextPattern::HandleClickEvent(GestureEvent& info)
1500 {
1501 CreateMultipleClickRecognizer();
1502 CHECK_NULL_VOID(GetDataDetectorAdapter());
1503 ResetAISelected(AIResetSelectionReason::CLICK);
1504 if ((selectOverlay_->IsClickAtHandle(info) && !multipleClickRecognizer_->IsRunning()) ||
1505 selectOverlay_->GetIsHandleDragging()) {
1506 return;
1507 }
1508 if (dataDetectorAdapter_->hasClickedAISpan_) {
1509 dataDetectorAdapter_->hasClickedAISpan_ = false;
1510 }
1511 multipleClickRecognizer_->Start(info);
1512 if (multipleClickRecognizer_->IsDoubleClick()) {
1513 HandleDoubleClickEvent(info);
1514 } else {
1515 HandleSingleClickEvent(info);
1516 }
1517 }
1518
HandleUrlClick()1519 bool TextPattern::HandleUrlClick()
1520 {
1521 if (LessNotEqual(clickedSpanPosition_, 0)) {
1522 return false;
1523 }
1524 auto iter = spans_.begin();
1525 std::advance(iter, clickedSpanPosition_);
1526 RefPtr<SpanItem> span;
1527 if (iter == spans_.end()) {
1528 span = spans_.back();
1529 } else {
1530 span = *iter;
1531 }
1532 if (span && span->urlOnRelease) {
1533 span->urlOnRelease();
1534 return true;
1535 }
1536 return false;
1537 }
1538
HandleSingleClickEvent(GestureEvent & info)1539 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
1540 {
1541 auto host = GetHost();
1542 CHECK_NULL_VOID(host);
1543 RectF textContentRect = contentRect_;
1544 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1545 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1546 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
1547 info.GetLocalLocation().GetY() - textContentRect.GetY() };
1548
1549 if (IsUsingMouse() && isMousePressed_ && leftMousePressed_ && moveOverClickThreshold_) {
1550 TAG_LOGI(ACE_TEXT, "not trigger click after mouse move");
1551 moveOverClickThreshold_ = false;
1552 return;
1553 }
1554
1555 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
1556 if (SystemProperties::GetTextTraceEnabled()) {
1557 ACE_TEXT_SCOPED_TRACE(
1558 "TextPattern::HandleSingleClickEvent id:%d clickedSpanPosition:%d", host->GetId(), clickedSpanPosition_);
1559 }
1560 if (HandleUrlClick()) {
1561 return;
1562 }
1563 if (selectOverlay_->SelectOverlayIsOn() && !selectOverlay_->IsUsingMouse() &&
1564 GlobalOffsetInSelectedArea(info.GetGlobalLocation())) {
1565 if (!IsLocationInFrameRegion(info.GetLocalLocation())) {
1566 return;
1567 }
1568 selectOverlay_->SwitchToOverlayMode();
1569 selectOverlay_->ToggleMenu();
1570 return;
1571 }
1572 if (!isMousePressed_ && !isTryEntityDragging_) {
1573 HandleClickAISpanEvent(textOffset);
1574 }
1575 if (GetDataDetectorAdapter()->hasClickedAISpan_) {
1576 selectOverlay_->HideMenu();
1577 return;
1578 }
1579 HandleClickOnTextAndSpan(info);
1580 }
1581
HandleClickOnTextAndSpan(GestureEvent & info)1582 void TextPattern::HandleClickOnTextAndSpan(GestureEvent& info)
1583 {
1584 if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE && !isMousePressed_) {
1585 CloseSelectOverlay(true);
1586 ResetSelection();
1587 }
1588 if (clickedSpanPosition_ == -1) {
1589 ActTextOnClick(info);
1590 return;
1591 }
1592 auto iter = spans_.begin();
1593 std::advance(iter, clickedSpanPosition_);
1594 RefPtr<SpanItem> span;
1595 if (iter == spans_.end()) {
1596 span = spans_.back();
1597 } else {
1598 span = *iter;
1599 }
1600 if (span && span->onClick) {
1601 GestureEvent spanClickinfo = info;
1602 EventTarget target = info.GetTarget();
1603 target.area.SetWidth(Dimension(0.0f));
1604 target.area.SetHeight(Dimension(0.0f));
1605 spanClickinfo.SetTarget(target);
1606 if (!TryLinkJump(span)) {
1607 span->onClick(spanClickinfo);
1608 // todo: RecordSpanClickEvent
1609 }
1610 } else {
1611 ActTextOnClick(info);
1612 }
1613 }
1614
1615 // return: whether execute link jump callback
TryLinkJump(const RefPtr<SpanItem> & span)1616 bool TextPattern::TryLinkJump(const RefPtr<SpanItem>& span)
1617 {
1618 auto host = GetHost();
1619 CHECK_NULL_RETURN(host, false);
1620 auto pipelineContext = host->GetContext();
1621 CHECK_NULL_RETURN(pipelineContext, false);
1622
1623 bool isCloudConfOpen = pipelineContext->GetIsLinkJumpOpen();
1624 if (isCloudConfOpen) {
1625 std::string spanContent = UtfUtils::Str16DebugToStr8(span->GetSpanContent()); // change for u16string
1626 auto isJumpLink = IsJumpLink(spanContent);
1627 TAG_LOGI(AceLogTag::ACE_TEXT, "TextPattern::TryLinkJump, spanContentLen: %{public}zu, isJumpLink: %{public}d",
1628 spanContent.size(), isJumpLink);
1629 if (isJumpLink) {
1630 pipelineContext->ExecuteLinkJumpCallback(spanContent);
1631 // todo: RecordSpanClickEvent
1632 return true;
1633 }
1634 }
1635 return false;
1636 }
1637
ActTextOnClick(GestureEvent & info)1638 void TextPattern::ActTextOnClick(GestureEvent& info)
1639 {
1640 auto host = GetHost();
1641 CHECK_NULL_VOID(host);
1642 if (SystemProperties::GetTextTraceEnabled()) {
1643 ACE_TEXT_SCOPED_TRACE("TextPattern::ActTextOnClick id:%d", host->GetId());
1644 }
1645 if (onClick_) {
1646 auto onClick = onClick_;
1647 onClick(info);
1648 }
1649 }
1650
GlobalOffsetInSelectedArea(const Offset & globalOffset)1651 bool TextPattern::GlobalOffsetInSelectedArea(const Offset& globalOffset)
1652 {
1653 auto host = GetHost();
1654 CHECK_NULL_RETURN(host, false);
1655 auto offset = host->GetPaintRectOffset(false, true);
1656 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
1657 if (selectOverlay_->HasRenderTransform()) {
1658 localOffset = ConvertGlobalToLocalOffset(globalOffset);
1659 }
1660 return LocalOffsetInSelectedArea(localOffset);
1661 }
1662
LocalOffsetInSelectedArea(const Offset & localOffset)1663 bool TextPattern::LocalOffsetInSelectedArea(const Offset& localOffset)
1664 {
1665 auto [start, end] = GetSelectedStartAndEnd();
1666 if (IsSelectableAndCopy() && GreatNotEqual(end, start)) {
1667 // Determine if the pan location is in the selected area
1668 return LocalOffsetInRange(localOffset, start, end);
1669 }
1670 return false;
1671 }
1672
LocalOffsetInRange(const Offset & localOffset,int32_t start,int32_t end)1673 bool TextPattern::LocalOffsetInRange(const Offset& localOffset, int32_t start, int32_t end)
1674 {
1675 auto selectedRects = pManager_->GetRects(start, end);
1676 TextBase::CalculateSelectedRect(selectedRects, contentRect_.Width());
1677 auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - contentRect_.GetOffset() +
1678 OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1679 for (const auto& selectedRect : selectedRects) {
1680 if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1681 return true;
1682 }
1683 }
1684 return false;
1685 }
1686
HandleClickAISpanEvent(const PointF & textOffset)1687 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
1688 {
1689 CHECK_NULL_VOID(GetDataDetectorAdapter());
1690 dataDetectorAdapter_->hasClickedAISpan_ = false;
1691 if (!NeedShowAIDetect() || mouseStatus_ == MouseStatus::MOVE || IsDragging()) {
1692 return;
1693 }
1694
1695 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
1696 auto& aiSpan = kv.second;
1697 ClickAISpan(textOffset, aiSpan);
1698 if (dataDetectorAdapter_->hasClickedAISpan_) {
1699 return;
1700 }
1701 }
1702 }
1703
CheckClickedOnSpanOrText(RectF textContentRect,const Offset & localLocation)1704 bool TextPattern::CheckClickedOnSpanOrText(RectF textContentRect, const Offset& localLocation)
1705 {
1706 clickedSpanPosition_ = -1;
1707 auto host = GetHost();
1708 CHECK_NULL_RETURN(host, false);
1709 auto renderContext = host->GetRenderContext();
1710 CHECK_NULL_RETURN(host, false);
1711 PointF textOffset = GetTextOffset(localLocation, textContentRect);
1712 auto clip = false;
1713 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1714 clip = true;
1715 }
1716 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value_or(clip) && overlayMod_) {
1717 textContentRect = overlayMod_->GetBoundsRect();
1718 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1719 }
1720 if (textContentRect.IsInRegion(
1721 PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
1722 !spans_.empty() && pManager_) {
1723 if (CalculateClickedSpanPosition(textOffset)) {
1724 return true;
1725 }
1726 }
1727 if (onClick_) {
1728 return true;
1729 }
1730 return false;
1731 }
1732
GetTextOffset(const Offset & localLocation,const RectF & contentRect)1733 PointF TextPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
1734 {
1735 PointF textOffset = {static_cast<float>(localLocation.GetX()) - contentRect.GetX(),
1736 static_cast<float>(localLocation.GetY()) - contentRect.GetY()};
1737 return textOffset;
1738 }
1739
CalculateClickedSpanPosition(const PointF & textOffset)1740 bool TextPattern::CalculateClickedSpanPosition(const PointF& textOffset)
1741 {
1742 int32_t start = 0;
1743 for (const auto& item : spans_) {
1744 clickedSpanPosition_++;
1745 if (!item) {
1746 continue;
1747 }
1748 auto end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
1749 auto selectedRects = GetSelectedRects(start, end);
1750 start = end;
1751 for (auto&& rect : selectedRects) {
1752 if (!rect.IsInRegion(textOffset)) {
1753 continue;
1754 }
1755 return CheckAndClick(item);
1756 }
1757 }
1758 clickedSpanPosition_ = -1;
1759 return false;
1760 }
1761
CheckAndClick(const RefPtr<SpanItem> & item)1762 bool TextPattern::CheckAndClick(const RefPtr<SpanItem>& item)
1763 {
1764 if (item->onClick || item->urlOnRelease) {
1765 return true;
1766 }
1767 clickedSpanPosition_ = -1;
1768 return false;
1769 }
1770
GetSelectedRects(int32_t start,int32_t end)1771 std::vector<RectF> TextPattern::GetSelectedRects(int32_t start, int32_t end)
1772 {
1773 return pManager_->GetRects(start, end);
1774 }
1775
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)1776 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
1777 {
1778 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1779 CHECK_NULL_RETURN(textLayoutProperty, false);
1780 CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
1781 int32_t start = aiSpan.start;
1782 int32_t end = aiSpan.end;
1783 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::ELLIPSIS) {
1784 auto range = pManager_->GetEllipsisTextRange();
1785 int32_t ellipsisStart = static_cast<int32_t>(range.first);
1786 int32_t ellipsisEnd = static_cast<int32_t>(range.second);
1787 if (ellipsisStart != -1 && ellipsisEnd > 0 && ellipsisStart < ellipsisEnd) {
1788 if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.end) {
1789 // ellipsisTextRange contains [aispan.start, aispan.end)
1790 return false;
1791 } else if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.start) {
1792 // ellipsisTextRange covers [aispan.start, ellipsisEnd)
1793 start = ellipsisEnd;
1794 } else if (ellipsisStart <= aiSpan.end && ellipsisEnd >= aiSpan.end) {
1795 // ellipsisTextRange covers [ellipsisStart, aiSpan.end);
1796 end = ellipsisStart;
1797 }
1798 }
1799 }
1800
1801 auto aiRects = pManager_->GetRects(start, end);
1802 for (auto&& rect : aiRects) {
1803 if (rect.IsInRegion(textOffset)) {
1804 dataDetectorAdapter_->hasClickedAISpan_ = true;
1805 if (leftMousePressed_) {
1806 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
1807 return true;
1808 }
1809 return ShowAIEntityMenu(aiSpan);
1810 }
1811 }
1812 return false;
1813 }
1814
InitUrlMouseEvent()1815 void TextPattern::InitUrlMouseEvent()
1816 {
1817 CHECK_NULL_VOID(!urlMouseEventInitialized_);
1818 auto host = GetHost();
1819 CHECK_NULL_VOID(host);
1820 auto eventHub = host->GetOrCreateEventHub<EventHub>();
1821 CHECK_NULL_VOID(eventHub);
1822 auto inputHub = eventHub->GetOrCreateInputEventHub();
1823 CHECK_NULL_VOID(inputHub);
1824 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1825 auto pattern = weak.Upgrade();
1826 if (pattern) {
1827 pattern->HandleUrlMouseEvent(info);
1828 }
1829 };
1830 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1831 inputHub->AddOnMouseEvent(mouseEvent);
1832 auto mouseHoverTask = [weak = WeakClaim(this)](bool isHover) {
1833 auto pattern = weak.Upgrade();
1834 CHECK_NULL_VOID(pattern);
1835 pattern->URLOnHover(isHover);
1836 };
1837 auto mouseHoverEvent = MakeRefPtr<InputEvent>(std::move(mouseHoverTask));
1838 inputHub->AddOnHoverEvent(mouseHoverEvent);
1839 urlMouseEventInitialized_ = true;
1840 }
1841
URLOnHover(bool isHover)1842 void TextPattern::URLOnHover(bool isHover)
1843 {
1844 CHECK_NULL_VOID(!isHover);
1845 auto host = GetHost();
1846 CHECK_NULL_VOID(host);
1847 auto nodeId = host->GetId();
1848 auto pipelineContext = host->GetContext();
1849 CHECK_NULL_VOID(pipelineContext);
1850 pipelineContext->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1851 pipelineContext->FreeMouseStyleHoldNode(nodeId);
1852 CHECK_NULL_VOID(overlayMod_);
1853 overlayMod_->ClearSelectedForegroundColorAndRects();
1854 MarkDirtySelf();
1855 }
1856
HandleUrlMouseEvent(const MouseInfo & info)1857 void TextPattern::HandleUrlMouseEvent(const MouseInfo& info)
1858 {
1859 if (isMousePressed_) {
1860 return;
1861 }
1862 RectF textContentRect = contentRect_;
1863 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1864 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1865 auto localLocation = info.GetLocalLocation();
1866 if (selectOverlay_->HasRenderTransform()) {
1867 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
1868 }
1869 auto host = GetHost();
1870 CHECK_NULL_VOID(host);
1871 auto hostId = host->GetId();
1872 auto renderContext = host->GetRenderContext();
1873 CHECK_NULL_VOID(renderContext);
1874 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
1875 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
1876 auto show = ShowShadow(textOffset, GetUrlHoverColor());
1877 auto pipelineContext = host->GetContext();
1878 CHECK_NULL_VOID(pipelineContext);
1879 if (show) {
1880 pipelineContext->SetMouseStyleHoldNode(hostId);
1881 pipelineContext->ChangeMouseStyle(hostId, MouseFormat::HAND_POINTING);
1882 } else {
1883 pipelineContext->ChangeMouseStyle(hostId, MouseFormat::DEFAULT);
1884 pipelineContext->FreeMouseStyleHoldNode(hostId);
1885 }
1886 }
1887
HandleUrlTouchEvent(const TouchEventInfo & info)1888 void TextPattern::HandleUrlTouchEvent(const TouchEventInfo& info)
1889 {
1890 CHECK_NULL_VOID(overlayMod_);
1891 CHECK_NULL_VOID(!IsDragging());
1892 if (selectOverlay_->IsTouchAtHandle(info)) {
1893 return;
1894 }
1895 auto touchType = info.GetTouches().front().GetTouchType();
1896 if (touchType != TouchType::DOWN && touchType != TouchType::UP) {
1897 return;
1898 }
1899 if (touchType == TouchType::DOWN) {
1900 RectF textContentRect = contentRect_;
1901 auto touchOffset = info.GetTouches().front().GetLocalLocation();
1902 PointF textOffset = { static_cast<float>(touchOffset.GetX()) - textContentRect.GetX(),
1903 static_cast<float>(touchOffset.GetY()) - textContentRect.GetY() };
1904 ShowShadow(textOffset, GetUrlPressColor());
1905 } else {
1906 overlayMod_->ClearSelectedForegroundColorAndRects();
1907 MarkDirtySelf();
1908 }
1909 }
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1910 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1911 const ShowSelectOverlayFunc& showSelectOverlayFunc)
1912
1913 {
1914 CHECK_NULL_VOID(GetDataDetectorAdapter());
1915 dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc,
1916 mainId = Container::CurrentIdSafelyWithCheck()](
1917 const std::string& action) {
1918 ContainerScope scope(mainId);
1919 auto pattern = weak.Upgrade();
1920 CHECK_NULL_VOID(pattern);
1921 pattern->CloseSelectOverlay();
1922 pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
1923 if (action == COPY) {
1924 pattern->HandleOnCopy();
1925 pattern->ResetSelection();
1926 } else if (action == SELECT_TEXT) {
1927 if (calculateHandleFunc == nullptr) {
1928 pattern->CalculateHandleOffsetAndShowOverlay();
1929 } else {
1930 calculateHandleFunc();
1931 }
1932 if (showSelectOverlayFunc == nullptr) {
1933 pattern->ShowSelectOverlay({ .animation = true });
1934 } else {
1935 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
1936 }
1937 auto frameNode = pattern->GetHost();
1938 CHECK_NULL_VOID(frameNode);
1939 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1940 }
1941 };
1942 }
1943
CalcAIMenuPosition(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc)1944 RectF TextPattern::CalcAIMenuPosition(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc)
1945 {
1946 RectF aiRect;
1947 auto host = GetHost();
1948 CHECK_NULL_RETURN(host, aiRect);
1949 auto context = host->GetRenderContext();
1950 CHECK_NULL_RETURN(context, aiRect);
1951 // save information
1952 auto baseOffset = textSelector_.baseOffset;
1953 auto destinationOffset = textSelector_.destinationOffset;
1954 // calculate result
1955 textSelector_.Update(aiSpan.start, aiSpan.end);
1956 if (calculateHandleFunc == nullptr) {
1957 CalculateHandleOffsetAndShowOverlay();
1958 } else {
1959 parentGlobalOffset_ = GetParentGlobalOffset();
1960 calculateHandleFunc();
1961 }
1962 if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
1963 auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
1964 auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
1965 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
1966 auto left = textContentGlobalOffset.GetX();
1967 auto right = textContentGlobalOffset.GetX() + contentRect_.Width();
1968 aiRect = RectT(left, top, right - left, bottom - top);
1969 AdjustAIEntityRect(aiRect);
1970 } else {
1971 aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
1972 }
1973 RectF viewPort;
1974 if (selectOverlay_->GetClipHandleViewPort(viewPort) &&
1975 GreatNotEqual(aiRect.GetY() + aiRect.Height(), viewPort.GetY() + viewPort.Height()) &&
1976 context->GetClipEdge().value_or(false)) {
1977 aiRect = viewPort;
1978 }
1979 // restore textSelector_
1980 textSelector_.Update(baseOffset, destinationOffset);
1981 if (calculateHandleFunc == nullptr) {
1982 CalculateHandleOffsetAndShowOverlay();
1983 } else {
1984 calculateHandleFunc();
1985 }
1986 return aiRect;
1987 }
GetCopyAndSelectable()1988 std::pair<bool, bool> TextPattern::GetCopyAndSelectable()
1989 {
1990 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1991 CHECK_NULL_RETURN(textLayoutProperty, std::make_pair(false, false));
1992 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1993 bool isShowCopy = true;
1994 bool isShowSelectText = true;
1995 if (copyOption_ == CopyOptions::None || textEffect_) {
1996 isShowCopy = false;
1997 isShowSelectText = false;
1998 } else if (mode == TextSelectableMode::UNSELECTABLE) {
1999 isShowSelectText = false;
2000 }
2001 return std::make_pair(isShowCopy, isShowSelectText);
2002 }
2003
ShowAIEntityMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)2004 bool TextPattern::ShowAIEntityMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
2005 const ShowSelectOverlayFunc& showSelectOverlayFunc)
2006 {
2007 auto host = GetHost();
2008 CHECK_NULL_RETURN(host, false);
2009 auto context = host->GetContext();
2010 CHECK_NULL_RETURN(context, false);
2011 auto safeAreaManager = context->GetSafeAreaManager();
2012 CHECK_NULL_RETURN(safeAreaManager, false);
2013 SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
2014 RectF aiRect = CalcAIMenuPosition(aiSpan, calculateHandleFunc);
2015 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2016 CHECK_NULL_RETURN(textLayoutProperty, false);
2017 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
2018 if (!NearEqual(safeAreaManager->GetKeyboardInset().Length(), 0)
2019 && mode == TextSelectableMode::SELECTABLE_FOCUSABLE) {
2020 aiRect.SetTop(aiRect.GetY() - safeAreaManager->GetKeyboardOffset());
2021 }
2022 auto [isShowCopy, isShowSelectText] = GetCopyAndSelectable();
2023 CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
2024 return dataDetectorAdapter_->ShowAIEntityMenu(aiSpan, aiRect, host, {isShowCopy, isShowSelectText});
2025 }
2026
HandleDoubleClickEvent(GestureEvent & info)2027 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
2028 {
2029 CheckOnClickEvent(info);
2030 auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
2031 if (!IsSelectableAndCopy() || (textSize == 0)) {
2032 return;
2033 }
2034 auto host = GetHost();
2035 CHECK_NULL_VOID(host);
2036 auto hub = host->GetOrCreateEventHub<EventHub>();
2037 CHECK_NULL_VOID(hub);
2038 auto gestureHub = hub->GetOrCreateGestureEventHub();
2039 CHECK_NULL_VOID(gestureHub);
2040 isDoubleClick_ = true;
2041 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
2042 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
2043 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
2044 InitSelection(textOffset);
2045 textResponseType_ = TextResponseType::NONE;
2046 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
2047 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
2048 CalculateHandleOffsetAndShowOverlay();
2049 if (!isMousePressed_) {
2050 ShowSelectOverlay({ .animation = true });
2051 }
2052 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2053 }
2054
CheckOnClickEvent(GestureEvent & info)2055 void TextPattern::CheckOnClickEvent(GestureEvent& info)
2056 {
2057 RectF textContentRect = contentRect_;
2058 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2059 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2060 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
2061 info.GetLocalLocation().GetY() - textContentRect.GetY() };
2062 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
2063 HandleClickOnTextAndSpan(info);
2064 }
2065
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)2066 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
2067 {
2068 gestureHub->SetNodeClickDistance(distanceThreshold_);
2069 CHECK_NULL_VOID(!clickEventInitialized_);
2070 CreateMultipleClickRecognizer();
2071 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
2072 auto pattern = weak.Upgrade();
2073 CHECK_NULL_VOID(pattern);
2074 pattern->sourceType_ = info.GetSourceDevice();
2075 pattern->HandleClickEvent(info);
2076 };
2077 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
2078 clickListener->SetSysJudge([weak = WeakClaim(this)](const RefPtr<GestureInfo>& gestureInfo,
2079 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
2080 auto textPattern = weak.Upgrade();
2081 CHECK_NULL_RETURN(textPattern, GestureJudgeResult::CONTINUE);
2082 if (info->GetFingerList().empty()) {
2083 return GestureJudgeResult::CONTINUE;
2084 }
2085 auto localLocation = info->GetFingerList().begin()->localLocation_;
2086 auto contentRect = textPattern->GetTextContentRect();
2087 auto baselineOffset = textPattern->GetBaselineOffset();
2088
2089 RectF textContentRect = contentRect;
2090 textContentRect.SetTop(contentRect.GetY() - std::min(baselineOffset, 0.0f));
2091 textContentRect.SetHeight(contentRect.Height() - std::max(baselineOffset, 0.0f));
2092 if (textPattern->GetCopyOptions() == CopyOptions::None && !textPattern->NeedShowAIDetect() &&
2093 !textPattern->CheckClickedOnSpanOrText(textContentRect, localLocation)) {
2094 return GestureJudgeResult::REJECT;
2095 }
2096 return GestureJudgeResult::CONTINUE;
2097 });
2098 gestureHub->AddClickEvent(clickListener);
2099 clickEventInitialized_ = true;
2100 }
2101
InitAISpanHoverEvent()2102 void TextPattern::InitAISpanHoverEvent()
2103 {
2104 CHECK_NULL_VOID(!aiSpanHoverEventInitialized_);
2105 auto host = GetHost();
2106 CHECK_NULL_VOID(host);
2107 auto eventHub = host->GetOrCreateEventHub<EventHub>();
2108 CHECK_NULL_VOID(eventHub);
2109 auto inputHub = eventHub->GetOrCreateInputEventHub();
2110 CHECK_NULL_VOID(inputHub);
2111
2112 auto aiSpanHoverTask = [weak = WeakClaim(this)](MouseInfo& info) {
2113 auto pattern = weak.Upgrade();
2114 CHECK_NULL_VOID(pattern);
2115 pattern->HandleAISpanHoverEvent(info);
2116 };
2117 auto aiSpanHoverEvent = MakeRefPtr<InputEvent>(std::move(aiSpanHoverTask));
2118 inputHub->AddOnMouseEvent(aiSpanHoverEvent);
2119 aiSpanHoverEventInitialized_ = true;
2120 }
2121
HandleAISpanHoverEvent(const MouseInfo & info)2122 void TextPattern::HandleAISpanHoverEvent(const MouseInfo& info)
2123 {
2124 CHECK_NULL_VOID(GetDataDetectorAdapter());
2125 if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect() || !isHover_) {
2126 return;
2127 }
2128 if (dataDetectorAdapter_->aiSpanRects_.empty()) {
2129 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
2130 auto& aiSpan = kv.second;
2131 const auto& aiRects = pManager_->GetRects(aiSpan.start, aiSpan.end);
2132 dataDetectorAdapter_->aiSpanRects_.insert(
2133 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
2134 }
2135 }
2136
2137 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
2138 PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
2139 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
2140 auto host = GetHost();
2141 CHECK_NULL_VOID(host);
2142 auto pipeline = GetContext();
2143 CHECK_NULL_VOID(pipeline);
2144 auto nodeId = host->GetId();
2145 pipeline->SetMouseStyleHoldNode(nodeId);
2146 for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
2147 if (!rect.IsInRegion(textOffset)) {
2148 continue;
2149 }
2150 if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
2151 bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
2152 CHECK_NULL_VOID(changeSuccess);
2153 currentMouseStyle_ = MouseFormat::HAND_POINTING;
2154 }
2155 return;
2156 }
2157 if (currentMouseStyle_ != MouseFormat::DEFAULT) {
2158 bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
2159 CHECK_NULL_VOID(changeSuccess);
2160 currentMouseStyle_ = MouseFormat::DEFAULT;
2161 }
2162 }
2163
OnHover(bool isHover)2164 void TextPattern::OnHover(bool isHover)
2165 {
2166 isHover_ = isHover;
2167 TAG_LOGI(AceLogTag::ACE_TEXT, "isHover=%{public}d", isHover);
2168 auto host = GetHost();
2169 CHECK_NULL_VOID(host);
2170 auto pipeline = GetContext();
2171 CHECK_NULL_VOID(pipeline);
2172 auto nodeId = host->GetId();
2173 if (isHover) {
2174 pipeline->SetMouseStyleHoldNode(nodeId);
2175 pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
2176 currentMouseStyle_ = MouseFormat::DEFAULT;
2177 } else {
2178 pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
2179 currentMouseStyle_ = MouseFormat::DEFAULT;
2180 pipeline->FreeMouseStyleHoldNode(nodeId);
2181 }
2182 }
2183
InitSpanMouseEvent()2184 void TextPattern::InitSpanMouseEvent()
2185 {
2186 CHECK_NULL_VOID(!spanMouseEventInitialized_);
2187 auto host = GetHost();
2188 CHECK_NULL_VOID(host);
2189 auto eventHub = host->GetEventHub<EventHub>();
2190 CHECK_NULL_VOID(eventHub);
2191 auto inputHub = eventHub->GetOrCreateInputEventHub();
2192 CHECK_NULL_VOID(inputHub);
2193
2194 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
2195 auto pattern = weak.Upgrade();
2196 CHECK_NULL_VOID(pattern);
2197 pattern->HandleSpanMouseEvent(info);
2198 };
2199 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
2200 inputHub->AddOnMouseEvent(mouseEvent);
2201
2202 auto hoverTask = [weak = WeakClaim(this)](bool isHover, HoverInfo& info) {
2203 TAG_LOGI(AceLogTag::ACE_TEXT, "on hover event isHover=%{public}d", isHover);
2204 auto pattern = weak.Upgrade();
2205 if (pattern) {
2206 if (!isHover) {
2207 pattern->ExitSpansForOnHoverEvent(info);
2208 }
2209 }
2210 };
2211 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
2212 inputHub->AddOnHoverEvent(hoverEvent);
2213 spanMouseEventInitialized_ = true;
2214 }
2215
ConvertHoverInfoFromMouseInfo(const MouseInfo & info) const2216 HoverInfo TextPattern::ConvertHoverInfoFromMouseInfo(const MouseInfo& info) const
2217 {
2218 HoverInfo result;
2219 result.SetGlobalLocation(info.GetGlobalLocation());
2220 result.SetScreenLocation(info.GetScreenLocation());
2221 result.SetLocalLocation(info.GetLocalLocation());
2222 result.SetGlobalDisplayLocation(info.GetGlobalDisplayLocation());
2223 result.SetTimeStamp(info.GetTimeStamp());
2224 result.SetTarget(info.GetTarget());
2225 result.SetDeviceId(info.GetDeviceId());
2226 result.SetTargetDisplayId(info.GetTargetDisplayId());
2227 result.SetSourceDevice(info.GetSourceDevice());
2228 if (info.GetTiltX().has_value()) {
2229 result.SetTiltX(info.GetTiltX().value());
2230 }
2231 if (info.GetTiltY().has_value()) {
2232 result.SetTiltY(info.GetTiltY().value());
2233 }
2234 if (info.GetRollAngle().has_value()) {
2235 result.SetRollAngle(info.GetRollAngle().value());
2236 }
2237 result.SetStopPropagation(info.IsStopPropagation());
2238 result.SetPreventDefault(info.IsPreventDefault());
2239 return result;
2240 }
2241
HandleSpanMouseEvent(const MouseInfo & info)2242 void TextPattern::HandleSpanMouseEvent(const MouseInfo& info)
2243 {
2244 RectF textContentRect = contentRect_;
2245 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2246 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2247 auto localLocation = info.GetLocalLocation();
2248 if (selectOverlay_->HasRenderTransform()) {
2249 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
2250 }
2251 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
2252 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
2253 TriggerSpansOnHover(ConvertHoverInfoFromMouseInfo(info), textOffset);
2254 }
2255
TriggerSpanOnHoverEvent(const HoverInfo & info,const RefPtr<SpanItem> & item,bool isOnHover)2256 void TextPattern::TriggerSpanOnHoverEvent(const HoverInfo& info, const RefPtr<SpanItem>& item, bool isOnHover)
2257 {
2258 TAG_LOGI(AceLogTag::ACE_TEXT, "on span hover event isHover=%{public}d", isOnHover);
2259 item->isOnHover = isOnHover;
2260 if (item->onHover) {
2261 item->onHover(isOnHover, const_cast<HoverInfo&>(info));
2262 }
2263 }
2264
TriggerSpansOnHover(const HoverInfo & info,const PointF & textOffset)2265 void TextPattern::TriggerSpansOnHover(const HoverInfo& info, const PointF& textOffset)
2266 {
2267 CHECK_NULL_VOID(!spans_.empty());
2268 // check exit
2269 RefPtr<SpanItem> exitItem;
2270 RefPtr<SpanItem> enterItem;
2271 for (const auto& item : spans_) {
2272 if (!item || !item->onHover) {
2273 continue;
2274 }
2275 int32_t end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
2276 int32_t start = end - static_cast<int32_t>(item->content.length());
2277 auto selectedRects = GetSelectedRects(start, end);
2278 bool isOnHover = false;
2279 for (auto&& rect : selectedRects) {
2280 isOnHover = rect.IsInRegion(textOffset);
2281 if (isOnHover) {
2282 break;
2283 }
2284 }
2285 if (!isOnHover && item->isOnHover != isOnHover) {
2286 exitItem = item;
2287 } else if (isOnHover && item->isOnHover != isOnHover) {
2288 enterItem = item;
2289 }
2290 if (exitItem && enterItem) {
2291 break;
2292 }
2293 }
2294 if (exitItem) {
2295 TriggerSpanOnHoverEvent(info, exitItem, false);
2296 }
2297 if (enterItem) {
2298 TriggerSpanOnHoverEvent(info, enterItem, true);
2299 }
2300 }
2301
ExitSpansForOnHoverEvent(const HoverInfo & info)2302 void TextPattern::ExitSpansForOnHoverEvent(const HoverInfo& info)
2303 {
2304 CHECK_NULL_VOID(!spans_.empty());
2305 for (const auto& item : spans_) {
2306 if (!item || !item->onHover) {
2307 continue;
2308 }
2309 bool isOnHover = false;
2310 if (item->isOnHover == isOnHover) {
2311 continue;
2312 }
2313 TriggerSpanOnHoverEvent(info, item, isOnHover);
2314 return;
2315 }
2316 }
2317
HasSpanOnHoverEvent()2318 bool TextPattern::HasSpanOnHoverEvent()
2319 {
2320 CHECK_NULL_RETURN(!spanMouseEventInitialized_, false);
2321 CHECK_NULL_RETURN(!spans_.empty(), false);
2322 for (const auto& item : spans_) {
2323 if (item && item->onHover) {
2324 return true;
2325 }
2326 }
2327 return false;
2328 }
2329
InitMouseEvent()2330 void TextPattern::InitMouseEvent()
2331 {
2332 CHECK_NULL_VOID(!mouseEventInitialized_);
2333 auto host = GetHost();
2334 CHECK_NULL_VOID(host);
2335 auto eventHub = host->GetOrCreateEventHub<EventHub>();
2336 CHECK_NULL_VOID(eventHub);
2337 auto inputHub = eventHub->GetOrCreateInputEventHub();
2338 CHECK_NULL_VOID(inputHub);
2339
2340 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
2341 auto pattern = weak.Upgrade();
2342 CHECK_NULL_VOID(pattern);
2343 pattern->HandleMouseEvent(info);
2344 };
2345 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
2346 inputHub->AddOnMouseEvent(mouseEvent);
2347
2348 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
2349 TAG_LOGI(AceLogTag::ACE_TEXT, "on hover event isHover=%{public}d", isHover);
2350 auto pattern = weak.Upgrade();
2351 if (pattern) {
2352 pattern->OnHover(isHover);
2353 }
2354 };
2355 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
2356 inputHub->AddOnHoverEvent(hoverEvent);
2357 mouseEventInitialized_ = true;
2358 }
2359
InitFocusEvent()2360 void TextPattern::InitFocusEvent()
2361 {
2362 CHECK_NULL_VOID(!focusInitialized_);
2363 auto host = GetHost();
2364 auto focusHub = host->GetFocusHub();
2365 CHECK_NULL_VOID(focusHub);
2366 auto focusTask = [weak = WeakClaim(this)](FocusReason reason) {
2367 auto pattern = weak.Upgrade();
2368 CHECK_NULL_VOID(pattern);
2369 auto contentModifier = pattern->GetContentModifier();
2370 CHECK_NULL_VOID(contentModifier);
2371 contentModifier->SetIsFocused(true);
2372 pattern->AddIsFocusActiveUpdateEvent();
2373 };
2374 focusHub->SetOnFocusInternal(focusTask);
2375
2376 auto blurTask = [weak = WeakClaim(this)]() {
2377 auto pattern = weak.Upgrade();
2378 CHECK_NULL_VOID(pattern);
2379 auto contentModifier = pattern->GetContentModifier();
2380 CHECK_NULL_VOID(contentModifier);
2381 contentModifier->SetIsFocused(false);
2382 pattern->RemoveIsFocusActiveUpdateEvent();
2383 pattern->ResetOriginCaretPosition();
2384 };
2385 focusHub->SetOnBlurInternal(blurTask);
2386
2387 focusInitialized_ = true;
2388 }
2389
AddIsFocusActiveUpdateEvent()2390 void TextPattern::AddIsFocusActiveUpdateEvent()
2391 {
2392 if (!isFocusActiveUpdateEvent_) {
2393 isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusAcitve) {
2394 auto pattern = weak.Upgrade();
2395 CHECK_NULL_VOID(pattern);
2396 pattern->OnIsFocusActiveUpdate(isFocusAcitve);
2397 };
2398 }
2399
2400 auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
2401 CHECK_NULL_VOID(pipline);
2402 pipline->AddIsFocusActiveUpdateEvent(GetHost(), isFocusActiveUpdateEvent_);
2403 }
2404
RemoveIsFocusActiveUpdateEvent()2405 void TextPattern::RemoveIsFocusActiveUpdateEvent()
2406 {
2407 auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
2408 CHECK_NULL_VOID(pipline);
2409 pipline->RemoveIsFocusActiveUpdateEvent(GetHost());
2410 }
2411
OnIsFocusActiveUpdate(bool isFocusAcitve)2412 void TextPattern::OnIsFocusActiveUpdate(bool isFocusAcitve)
2413 {
2414 auto host = GetHost();
2415 CHECK_NULL_VOID(host);
2416 auto pattern = host->GetPattern<TextPattern>();
2417 CHECK_NULL_VOID(pattern);
2418 auto contentModifier = pattern->GetContentModifier();
2419 CHECK_NULL_VOID(contentModifier);
2420 contentModifier->SetIsFocused(isFocusAcitve);
2421 }
2422
InitHoverEvent()2423 void TextPattern::InitHoverEvent()
2424 {
2425 CHECK_NULL_VOID(!hoverInitialized_);
2426 auto host = GetHost();
2427 CHECK_NULL_VOID(host);
2428 auto eventHub = host->GetOrCreateEventHub<EventHub>();
2429 CHECK_NULL_VOID(eventHub);
2430 auto inputHub = eventHub->GetOrCreateInputEventHub();
2431 CHECK_NULL_VOID(inputHub);
2432
2433 auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
2434 auto pattern = weak.Upgrade();
2435 CHECK_NULL_VOID(pattern);
2436 auto contentModifier = pattern->GetContentModifier();
2437 CHECK_NULL_VOID(contentModifier);
2438 contentModifier->SetIsHovered(isHover);
2439 };
2440 auto mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
2441 inputHub->AddOnHoverEvent(mouseEvent_);
2442
2443 hoverInitialized_ = true;
2444 }
2445
RecoverCopyOption()2446 void TextPattern::RecoverCopyOption()
2447 {
2448 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2449 CHECK_NULL_VOID(textLayoutProperty);
2450 auto host = GetHost();
2451 auto contentHost = GetContentHost();
2452 CHECK_NULL_VOID(host && contentHost);
2453
2454 copyOption_ = textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE
2455 ? CopyOptions::None
2456 : textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
2457
2458 const auto& children = contentHost->GetChildren();
2459 if (children.empty()) {
2460 if (IsSetObscured() && !isSpanStringMode_) {
2461 copyOption_ = CopyOptions::None;
2462 }
2463 }
2464 auto gestureEventHub = host->GetOrCreateGestureEventHub();
2465 CHECK_NULL_VOID(gestureEventHub);
2466 auto eventHub = host->GetOrCreateEventHub<EventHub>();
2467 CHECK_NULL_VOID(eventHub);
2468 if (copyOption_ == CopyOptions::None && !textDetectEnable_ && !textLayoutProperty->GetTextOverflow() &&
2469 !onClick_ && !longPressEvent_) { // performance prune
2470 if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
2471 gestureEventHub->SetTextDraggable(false);
2472 eventHub->SetDefaultOnDragStart(nullptr);
2473 if (!eventHub->HasOnDragStart() && IsTextNode()) {
2474 gestureEventHub->RemoveDragEvent();
2475 }
2476 }
2477 return;
2478 }
2479 if (copyOption_ == CopyOptions::None) {
2480 CloseSelectOverlay();
2481 ResetSelection();
2482 }
2483 if ((children.empty() || isSpanStringMode_) &&
2484 CanStartAITask() && !GetDataDetectorAdapter()->aiDetectInitialized_) {
2485 dataDetectorAdapter_->textForAI_ = textForDisplay_;
2486 dataDetectorAdapter_->StartAITask();
2487 }
2488 ProcessMarqueeVisibleAreaCallback();
2489 InitCopyOption(gestureEventHub, eventHub);
2490 bool enabledCache = eventHub->IsEnabled();
2491 selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
2492 selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
2493 selectOverlay_->UpdateHandleColor();
2494 if (textDetectEnable_ && enabledCache != enabled_) {
2495 enabled_ = enabledCache;
2496 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
2497 }
2498 }
2499
InitCopyOption(const RefPtr<GestureEventHub> & gestureEventHub,const RefPtr<EventHub> & eventHub)2500 void TextPattern::InitCopyOption(const RefPtr<GestureEventHub>& gestureEventHub, const RefPtr<EventHub>& eventHub)
2501 {
2502 CHECK_NULL_VOID(gestureEventHub);
2503 CHECK_NULL_VOID(eventHub);
2504 auto host = GetHost();
2505 CHECK_NULL_VOID(host);
2506 if (IsSelectableAndCopy()) {
2507 auto context = host->GetContext();
2508 CHECK_NULL_VOID(context);
2509 if (!clipboard_ && context) {
2510 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
2511 }
2512 InitLongPressEvent(gestureEventHub);
2513 if (host->IsDraggable() && !shiftFlag_) {
2514 InitDragEvent();
2515 }
2516 InitKeyEvent();
2517 InitMouseEvent();
2518 InitTouchEvent();
2519 SetAccessibilityAction();
2520 } else {
2521 if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
2522 gestureEventHub->SetTextDraggable(false);
2523 eventHub->SetDefaultOnDragStart(nullptr);
2524 if (!eventHub->HasOnDragStart() && IsTextNode()) {
2525 gestureEventHub->RemoveDragEvent();
2526 }
2527 }
2528 if (longPressEvent_ && !hasSpanStringLongPressEvent_) {
2529 gestureEventHub->SetLongPressEvent(nullptr);
2530 longPressEvent_ = nullptr;
2531 }
2532 }
2533 if (onClick_ || IsSelectableAndCopy() || CanStartAITask()) {
2534 InitClickEvent(gestureEventHub);
2535 if (CanStartAITask()) {
2536 auto context = host->GetContext();
2537 CHECK_NULL_VOID(context);
2538 if (!clipboard_ && context) {
2539 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
2540 }
2541 InitMouseEvent();
2542 InitAISpanHoverEvent();
2543 }
2544 }
2545 }
2546
HandleMouseEvent(const MouseInfo & info)2547 void TextPattern::HandleMouseEvent(const MouseInfo& info)
2548 {
2549 auto localLocation = info.GetLocalLocation();
2550 if (isAutoScrollByMouse_ && GetHost()) {
2551 NG::PointF localPoint(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
2552 NG::NGGestureRecognizer::Transform(localPoint, GetHost(), true);
2553 localLocation.SetX(localPoint.GetX());
2554 localLocation.SetY(localPoint.GetY());
2555 }
2556 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
2557 Offset textOffset = { localLocation.GetX() - textPaintOffset.GetX(),
2558 localLocation.GetY() - textPaintOffset.GetY() };
2559 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
2560 lastLeftMouseMoveLocation_ = info.GetGlobalLocation();
2561 HandleMouseLeftButton(info, textOffset);
2562 if (IsSelected()) {
2563 selectOverlay_->SetSelectionHoldCallback();
2564 }
2565 sourceType_ = info.GetSourceDevice();
2566 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
2567 HandleMouseRightButton(info, textOffset);
2568 sourceType_ = info.GetSourceDevice();
2569 }
2570 if (!IsSelected()) {
2571 ResetOriginCaretPosition();
2572 }
2573 }
2574
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)2575 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
2576 {
2577 if (info.GetAction() == MouseAction::PRESS) {
2578 HandleMouseLeftPressAction(info, textOffset);
2579 } else if (info.GetAction() == MouseAction::MOVE) {
2580 HandleMouseLeftMoveAction(info, textOffset);
2581 } else if (info.GetAction() == MouseAction::RELEASE) {
2582 HandleMouseLeftReleaseAction(info, textOffset);
2583 }
2584
2585 auto host = GetHost();
2586 CHECK_NULL_VOID(host);
2587 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2588 }
2589
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)2590 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
2591 {
2592 isMousePressed_ = true;
2593 CheckPressedSpanPosition(textOffset);
2594 leftMousePressed_ = true;
2595 ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlPressColor());
2596 if (BetweenSelectedPosition(info.GetGlobalLocation())) {
2597 blockPress_ = true;
2598 return;
2599 }
2600 mouseStatus_ = MouseStatus::PRESSED;
2601 lastLeftMouseClickStyle_ = currentMouseStyle_;
2602 CHECK_NULL_VOID(pManager_);
2603 if (shiftFlag_) {
2604 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
2605 HandleSelectionChange(textSelector_.lastValidStart, end);
2606 } else {
2607 auto start = pManager_->GetGlyphIndexByCoordinate(textOffset);
2608 textSelector_.Update(start, start);
2609 }
2610 // auto scroll.
2611 scrollableParent_ = selectOverlay_->FindScrollableParent();
2612 auto host = GetHost();
2613 if (scrollableParent_.Upgrade() && host) {
2614 host->RegisterNodeChangeListener();
2615 }
2616 }
2617
CheckPressedSpanPosition(const Offset & textOffset)2618 void TextPattern::CheckPressedSpanPosition(const Offset& textOffset)
2619 {
2620 leftMousePressedOffset_ = textOffset;
2621 }
2622
ResetMouseLeftPressedState()2623 void TextPattern::ResetMouseLeftPressedState()
2624 {
2625 isMousePressed_ = false;
2626 leftMousePressed_ = false;
2627 }
2628
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)2629 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
2630 {
2631 bool pressBetweenSelectedPosition = blockPress_;
2632 blockPress_ = false;
2633 auto oldMouseStatus = mouseStatus_;
2634 mouseStatus_ = MouseStatus::RELEASED;
2635 auto oldEntityDragging = isTryEntityDragging_;
2636 isTryEntityDragging_ = false;
2637 lastLeftMouseClickStyle_ = MouseFormat::DEFAULT;
2638 ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlHoverColor());
2639 if (isDoubleClick_) {
2640 isDoubleClick_ = false;
2641 ResetMouseLeftPressedState();
2642 return;
2643 }
2644 if (oldMouseStatus != MouseStatus::MOVE && oldMouseStatus == MouseStatus::PRESSED &&
2645 !IsDragging() && !oldEntityDragging) {
2646 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
2647 if (GetDataDetectorAdapter()->hasClickedAISpan_) {
2648 selectOverlay_->DisableMenu();
2649 ResetMouseLeftPressedState();
2650 return;
2651 }
2652 }
2653
2654 CHECK_NULL_VOID(pManager_);
2655 auto start = textSelector_.baseOffset;
2656 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
2657 if (!IsSelected() || (pressBetweenSelectedPosition && !mouseUpAndDownPointChange_)) {
2658 start = -1;
2659 end = -1;
2660 }
2661 if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE || shiftFlag_) {
2662 HandleSelectionChange(start, end);
2663 }
2664
2665 if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
2666 selectOverlay_->SetMouseMenuOffset(OffsetF(
2667 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
2668 textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
2669 ShowSelectOverlay({ .animation = true });
2670 }
2671 ResetMouseLeftPressedState();
2672 moveOverClickThreshold_ = false;
2673 mouseUpAndDownPointChange_ = false;
2674 // stop auto scroll.
2675 auto host = GetHost();
2676 if (host && scrollableParent_.Upgrade() && !selectOverlay_->SelectOverlayIsOn()) {
2677 host->UnregisterNodeChangeListener();
2678 }
2679 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), true);
2680 isAutoScrollByMouse_ = false;
2681 }
2682
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)2683 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
2684 {
2685 if (!IsSelectableAndCopy()) {
2686 isTryEntityDragging_ = lastLeftMouseClickStyle_ == MouseFormat::HAND_POINTING;
2687 isMousePressed_ = false;
2688 leftMousePressed_ = false;
2689 return;
2690 }
2691 if (isMousePressed_) {
2692 mouseStatus_ = MouseStatus::MOVE;
2693 CHECK_NULL_VOID(pManager_);
2694 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
2695 HandleSelectionChange(textSelector_.baseOffset, end);
2696 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), false);
2697 auto distance = (textOffset - leftMousePressedOffset_).GetDistance();
2698 if (distance >= CLICK_THRESHOLD.ConvertToPx()) {
2699 moveOverClickThreshold_ = true;
2700 mouseUpAndDownPointChange_ = true;
2701 }
2702 }
2703 }
2704
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)2705 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
2706 {
2707 if (info.GetAction() == MouseAction::RELEASE) {
2708 selectOverlay_->SetMouseMenuOffset(OffsetF(
2709 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
2710 if (!BetweenSelectedPosition(info.GetGlobalLocation())) {
2711 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
2712 if (GetDataDetectorAdapter()->hasClickedAISpan_) {
2713 isMousePressed_ = false;
2714 return;
2715 }
2716 }
2717 if (!IsSelectableAndCopy()) {
2718 return;
2719 }
2720
2721 CalculateHandleOffsetAndShowOverlay(true);
2722 if (selectOverlay_->SelectOverlayIsOn()) {
2723 CloseSelectOverlay(true);
2724 }
2725 textResponseType_ = TextResponseType::RIGHT_CLICK;
2726 if (!IsSelected()) {
2727 auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
2728 if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
2729 selectedType_ = TextSpanType::IMAGE;
2730 } else {
2731 selectedType_ = TextSpanType::TEXT;
2732 }
2733 }
2734 ShowSelectOverlay({ .animation = true });
2735 isMousePressed_ = false;
2736 auto host = GetHost();
2737 CHECK_NULL_VOID(host);
2738 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2739 } else if (info.GetAction() == MouseAction::PRESS) {
2740 isMousePressed_ = true;
2741 CloseSelectOverlay(true);
2742 }
2743 }
2744
InitTouchEvent()2745 void TextPattern::InitTouchEvent()
2746 {
2747 CHECK_NULL_VOID(!touchEventInitialized_);
2748 auto host = GetHost();
2749 CHECK_NULL_VOID(host);
2750 auto gesture = host->GetOrCreateGestureEventHub();
2751 CHECK_NULL_VOID(gesture);
2752
2753 auto touchTask = [weak = WeakClaim(this)](TouchEventInfo& info) {
2754 auto pattern = weak.Upgrade();
2755 CHECK_NULL_VOID(pattern);
2756 pattern->sourceType_ = info.GetSourceDevice();
2757 pattern->HandleTouchEvent(info);
2758 };
2759 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2760 gesture->AddTouchEvent(touchListener);
2761 touchEventInitialized_ = true;
2762 }
2763
InitUrlTouchEvent()2764 void TextPattern::InitUrlTouchEvent()
2765 {
2766 CHECK_NULL_VOID(!urlTouchEventInitialized_);
2767 auto host = GetHost();
2768 CHECK_NULL_VOID(host);
2769 auto gesture = host->GetOrCreateGestureEventHub();
2770 CHECK_NULL_VOID(gesture);
2771
2772 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
2773 auto pattern = weak.Upgrade();
2774 CHECK_NULL_VOID(pattern);
2775 pattern->HandleUrlTouchEvent(info);
2776 };
2777 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2778 gesture->AddTouchEvent(touchListener);
2779 urlTouchEventInitialized_ = true;
2780 }
2781
InitSpanStringTouchEvent()2782 void TextPattern::InitSpanStringTouchEvent()
2783 {
2784 CHECK_NULL_VOID(!spanStringTouchInitialized_);
2785 auto host = GetHost();
2786 CHECK_NULL_VOID(host);
2787 auto gesture = host->GetOrCreateGestureEventHub();
2788 CHECK_NULL_VOID(gesture);
2789
2790 auto touchTask = [weak = WeakClaim(this)](TouchEventInfo& info) {
2791 auto pattern = weak.Upgrade();
2792 CHECK_NULL_VOID(pattern);
2793 pattern->HandleSpanStringTouchEvent(info);
2794 };
2795 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2796 gesture->AddTouchEvent(touchListener);
2797 spanStringTouchInitialized_ = true;
2798 }
2799
MarkDirtySelf()2800 void TextPattern::MarkDirtySelf()
2801 {
2802 auto host = GetHost();
2803 CHECK_NULL_VOID(host);
2804 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2805 }
2806
HandleTouchEvent(const TouchEventInfo & info)2807 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
2808 {
2809 DoGestureSelection(info);
2810 ResetOriginCaretPosition();
2811 }
2812
HandleSpanStringTouchEvent(TouchEventInfo & info)2813 void TextPattern::HandleSpanStringTouchEvent(TouchEventInfo& info)
2814 {
2815 CHECK_NULL_VOID(!info.GetTouches().empty());
2816 auto touchOffset = info.GetTouches().front().GetLocalLocation();
2817 auto contentRect = GetTextRect();
2818 PointF textOffset = { static_cast<float>(touchOffset.GetX()) - contentRect.GetX(),
2819 static_cast<float>(touchOffset.GetY()) - contentRect.GetY() };
2820 auto touchedSpan = FindSpanItemByOffset(textOffset);
2821 if (touchedSpan && touchedSpan->onTouch) {
2822 touchedSpan->onTouch(info);
2823 }
2824 }
2825
FindSpanItemByOffset(const PointF & textOffset)2826 RefPtr<SpanItem> TextPattern::FindSpanItemByOffset(const PointF& textOffset)
2827 {
2828 int32_t start = 0;
2829 for (const auto& item : spans_) {
2830 if (!item) {
2831 continue;
2832 }
2833 auto end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
2834 auto selectedRects = GetSelectedRects(start, end);
2835 start = end;
2836 for (auto&& rect : selectedRects) {
2837 if (rect.IsInRegion(textOffset)) {
2838 return item;
2839 }
2840 }
2841 }
2842 return nullptr;
2843 }
2844
InitKeyEvent()2845 void TextPattern::InitKeyEvent()
2846 {
2847 CHECK_NULL_VOID(!keyEventInitialized_);
2848 auto host = GetHost();
2849 CHECK_NULL_VOID(host);
2850 auto focusHub = host->GetOrCreateFocusHub();
2851 CHECK_NULL_VOID(focusHub);
2852
2853 auto keyTask = [weak = WeakClaim(this)](const KeyEvent& event) -> bool {
2854 auto pattern = weak.Upgrade();
2855 CHECK_NULL_RETURN(pattern, false);
2856 return pattern->HandleKeyEvent(event);
2857 };
2858 focusHub->SetOnKeyEventInternal(std::move(keyTask));
2859 keyEventInitialized_ = true;
2860 }
2861
UpdateShiftFlag(const KeyEvent & keyEvent)2862 void TextPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
2863 {
2864 bool flag = false;
2865 if (keyEvent.action == KeyAction::DOWN) {
2866 if (keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT)) {
2867 flag = true;
2868 }
2869 }
2870 if (flag != shiftFlag_) {
2871 shiftFlag_ = flag;
2872 if (!shiftFlag_) {
2873 // open drag
2874 InitDragEvent();
2875 } else {
2876 // close drag
2877 ClearDragEvent();
2878 }
2879 }
2880 }
2881
HandleKeyEvent(const KeyEvent & keyEvent)2882 bool TextPattern::HandleKeyEvent(const KeyEvent& keyEvent)
2883 {
2884 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2885 CHECK_NULL_RETURN(textLayoutProperty, false);
2886 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
2887 return false;
2888 }
2889 CHECK_NULL_RETURN(!textEffect_, false);
2890 UpdateShiftFlag(keyEvent);
2891 auto host = GetHost();
2892 CHECK_NULL_RETURN(host, false);
2893 if (SystemProperties::GetTextTraceEnabled()) {
2894 ACE_TEXT_SCOPED_TRACE("TextPattern::HandleKeyEvent[id:%d][action:%d]", host->GetId(), keyEvent.action);
2895 }
2896 if (keyEvent.action != KeyAction::DOWN) {
2897 return false;
2898 }
2899
2900 if (keyEvent.IsCtrlWith(KeyCode::KEY_C)) {
2901 HandleOnCopy();
2902 return true;
2903 }
2904
2905 if (keyEvent.IsCtrlWith(KeyCode::KEY_A)) {
2906 auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
2907 HandleSelectionChange(0, textSize);
2908 CloseSelectOverlay();
2909 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2910 return true;
2911 }
2912
2913 if (keyEvent.IsShiftWith(keyEvent.code)) {
2914 HandleOnSelect(keyEvent.code);
2915 return true;
2916 }
2917 return false;
2918 }
2919
HandleOnSelect(KeyCode code)2920 void TextPattern::HandleOnSelect(KeyCode code)
2921 {
2922 auto end = textSelector_.GetEnd();
2923 switch (code) {
2924 case KeyCode::KEY_DPAD_LEFT: {
2925 HandleSelection(true, end - 1);
2926 break;
2927 }
2928 case KeyCode::KEY_DPAD_RIGHT: {
2929 HandleSelection(false, end + 1);
2930 break;
2931 }
2932 case KeyCode::KEY_DPAD_UP: {
2933 HandleSelectionUp();
2934 break;
2935 }
2936 case KeyCode::KEY_DPAD_DOWN: {
2937 HandleSelectionDown();
2938 break;
2939 }
2940 default:
2941 break;
2942 }
2943 if (!(shiftFlag_ && (code == KeyCode::KEY_DPAD_UP ||
2944 code == KeyCode::KEY_DPAD_DOWN))) {
2945 ResetOriginCaretPosition();
2946 }
2947 }
2948
HandleSelectionUp()2949 void TextPattern::HandleSelectionUp()
2950 {
2951 auto end = textSelector_.GetEnd();
2952 auto line = pManager_->GetLineCount();
2953 if (line == 1) {
2954 HandleSelection(true, 0);
2955 return;
2956 }
2957 CaretMetricsF secondHandleMetrics;
2958 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2959 auto secondOffsetX = secondHandleMetrics.offset.GetX();
2960 auto secondOffsetY = secondHandleMetrics.offset.GetY();
2961 RecordOriginCaretPosition({ secondOffsetX, secondOffsetY });
2962 OffsetF originCaretPosition;
2963 auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2964 originCaretPosition.GetX() : secondOffsetX;
2965 double height = GetTextHeight(end, false);
2966 Offset offset = { caretXPosition, secondOffsetY - height * 0.5 };
2967 auto caculateIndex = GetHandleIndex(offset);
2968 if (end == caculateIndex) {
2969 caculateIndex = 0;
2970 }
2971 HandleSelection(true, caculateIndex);
2972 }
2973
HandleSelectionDown()2974 void TextPattern::HandleSelectionDown()
2975 {
2976 auto end = textSelector_.GetEnd();
2977 auto line = pManager_->GetLineCount();
2978 auto lastIndex = GetActualTextLength();
2979 if (line == 1) {
2980 HandleSelection(true, lastIndex);
2981 return;
2982 }
2983 CaretMetricsF secondHandleMetrics;
2984 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2985 auto secondOffsetX = secondHandleMetrics.offset.GetX();
2986 RecordOriginCaretPosition({ secondOffsetX, secondHandleMetrics.offset.GetY() });
2987 OffsetF originCaretPosition;
2988 auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2989 originCaretPosition.GetX() : secondOffsetX;
2990 double height = GetTextHeight(end, true);
2991 auto caculateIndex = GetHandleIndex({ caretXPosition, height });
2992 if (NearZero(height) || caculateIndex == end || caculateIndex > lastIndex) {
2993 caculateIndex = lastIndex;
2994 }
2995 HandleSelection(true, caculateIndex);
2996 }
2997
HandleSelection(bool isEmojiStart,int32_t end)2998 void TextPattern::HandleSelection(bool isEmojiStart, int32_t end)
2999 {
3000 auto host = GetHost();
3001 CHECK_NULL_VOID(host);
3002 auto start = textSelector_.GetStart();
3003 auto lastIndex = GetActualTextLength();
3004 if (SystemProperties::GetTextTraceEnabled()) {
3005 TAG_LOGI(AceLogTag::ACE_TEXT,
3006 "HandleSelection[id:%{public}d][start:%{public}d][end:%{public}d][isEmojiStart:%{public}d]", host->GetId(),
3007 start, end, isEmojiStart);
3008 }
3009 if (start < 0 || start > lastIndex || end < 0 || end > lastIndex) {
3010 return;
3011 }
3012 int32_t emojiStartIndex;
3013 int32_t emojiEndIndex;
3014 bool isIndexInEmoji = TextEmojiProcessor::IsIndexInEmoji(end, GetSelectedText(0, lastIndex),
3015 emojiStartIndex, emojiEndIndex);
3016 if (isIndexInEmoji) {
3017 end = isEmojiStart ? emojiStartIndex : emojiEndIndex;
3018 }
3019 HandleSelectionChange(start, end);
3020 CalculateHandleOffsetAndShowOverlay();
3021 CloseSelectOverlay(true);
3022 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3023 }
3024
GetTextHeight(int32_t index,bool isNextLine)3025 double TextPattern::GetTextHeight(int32_t index, bool isNextLine)
3026 {
3027 double lineHeight = 0.0;
3028 auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
3029 for (auto lineNumber = 0; lineNumber < lineCount; lineNumber++) {
3030 auto lineMetrics = GetLineMetrics(lineNumber);
3031 auto startIndex = static_cast<int32_t>(lineMetrics.startIndex);
3032 auto endIndex = static_cast<int32_t>(lineMetrics.endIndex);
3033 lineHeight += lineMetrics.height;
3034 if (isNextLine) {
3035 if (index <= endIndex && endIndex != GetActualTextLength()) {
3036 return lineHeight;
3037 }
3038 } else {
3039 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3040 CHECK_NULL_RETURN(textLayoutProperty, 0);
3041 auto maxLines = textLayoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
3042 if ((index <= endIndex && startIndex != 0) ||
3043 ((lineNumber + 1) == static_cast<int32_t>(maxLines) && lineNumber != 0)) {
3044 return GetLineMetrics(lineNumber - 1).height;
3045 }
3046 }
3047 }
3048 return 0.0;
3049 }
3050
GetActualTextLength()3051 int32_t TextPattern::GetActualTextLength()
3052 {
3053 auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
3054 return GetLineMetrics(lineCount - 1).endIndex;
3055 }
3056
SetTextSelectableMode(TextSelectableMode value)3057 void TextPattern::SetTextSelectableMode(TextSelectableMode value)
3058 {
3059 auto host = GetHost();
3060 CHECK_NULL_VOID(host);
3061 auto focusHub = host->GetOrCreateFocusHub();
3062 CHECK_NULL_VOID(focusHub);
3063 if (value == TextSelectableMode::SELECTABLE_FOCUSABLE) {
3064 focusHub->SetFocusable(true);
3065 focusHub->SetIsFocusOnTouch(true);
3066 } else {
3067 focusHub->SetFocusable(false);
3068 focusHub->SetIsFocusOnTouch(false);
3069 }
3070 }
3071
IsSelectableAndCopy()3072 bool TextPattern::IsSelectableAndCopy()
3073 {
3074 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3075 CHECK_NULL_RETURN(textLayoutProperty, false);
3076 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
3077 return mode != TextSelectableMode::UNSELECTABLE && copyOption_ != CopyOptions::None && !textEffect_;
3078 }
3079
IsDraggable(const Offset & offset)3080 bool TextPattern::IsDraggable(const Offset& offset)
3081 {
3082 auto host = GetHost();
3083 CHECK_NULL_RETURN(host, false);
3084 auto eventHub = host->GetOrCreateEventHub<EventHub>();
3085 bool draggable = eventHub->HasOnDragStart();
3086 return draggable && LocalOffsetInSelectedArea(offset);
3087 }
3088
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)3089 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
3090 {
3091 DragDropInfo itemInfo;
3092 auto host = GetHost();
3093 CHECK_NULL_RETURN(host, itemInfo);
3094 if (overlayMod_) {
3095 overlayMod_->ClearSelectedForegroundColorAndRects();
3096 }
3097 auto hub = host->GetOrCreateEventHub<EventHub>();
3098 auto gestureHub = hub->GetOrCreateGestureEventHub();
3099 auto [start, end] = GetSelectedStartAndEnd();
3100 recoverStart_ = start;
3101 recoverEnd_ = end;
3102 auto textSelectInfo = GetSpansInfo(start, end, GetSpansMethod::ONSELECT);
3103 dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
3104 ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
3105 dragBoxes_ = GetTextBoxes();
3106 ResetAISelected(AIResetSelectionReason::DRAG_START);
3107 status_ = Status::DRAGGING;
3108 if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
3109 return itemInfo;
3110 }
3111 auto data = event->GetData();
3112 if (!data) {
3113 AddUdmfData(event);
3114 }
3115 CloseOperate();
3116 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3117 return itemInfo;
3118 }
3119
AddUdmfTxtPreProcessor(const ResultObject src,ResultObject & result,bool isAppend)3120 void TextPattern::AddUdmfTxtPreProcessor(const ResultObject src, ResultObject& result, bool isAppend)
3121 {
3122 auto valueString = GetSelectedSpanText(src.valueString,
3123 src.offsetInSpan[RichEditorSpanRange::RANGESTART], src.offsetInSpan[RichEditorSpanRange::RANGEEND],
3124 false, true, false);
3125 if (isAppend) {
3126 result.valueString = result.valueString + valueString;
3127 } else {
3128 result.valueString = valueString;
3129 }
3130 }
3131
AddUdmfData(const RefPtr<Ace::DragEvent> & event)3132 void TextPattern::AddUdmfData(const RefPtr<Ace::DragEvent>& event)
3133 {
3134 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
3135 if (isSpanStringMode_) {
3136 std::vector<uint8_t> arr;
3137 auto dragSpanString = styledString_->GetSubSpanString(recoverStart_, recoverEnd_ - recoverStart_,
3138 false, true, false);
3139 dragSpanString->EncodeTlv(arr);
3140 UdmfClient::GetInstance()->AddSpanStringRecord(unifiedData, arr);
3141 } else {
3142 ProcessNormalUdmfData(unifiedData);
3143 }
3144 event->SetData(unifiedData);
3145 }
3146
ProcessNormalUdmfData(const RefPtr<UnifiedData> & unifiedData)3147 void TextPattern::ProcessNormalUdmfData(const RefPtr<UnifiedData>& unifiedData)
3148 {
3149 std::list<ResultObject> finalResult;
3150 auto type = SelectSpanType::TYPESPAN;
3151 for (const auto& resultObj : dragResultObjects_) {
3152 if (finalResult.empty() || resultObj.type != SelectSpanType::TYPESPAN || type != SelectSpanType::TYPESPAN) {
3153 type = resultObj.type;
3154 finalResult.emplace_back(resultObj);
3155 if (resultObj.type == SelectSpanType::TYPESPAN) {
3156 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), false);
3157 }
3158 } else {
3159 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), true);
3160 }
3161 }
3162 auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
3163 auto pattern = weak.Upgrade();
3164 CHECK_NULL_VOID(pattern);
3165 std::string u8ValueString = UtfUtils::Str16DebugToStr8(result.valueString);
3166 if (result.type == SelectSpanType::TYPESPAN) {
3167 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, u8ValueString);
3168 return;
3169 }
3170 if (result.type == SelectSpanType::TYPEIMAGE) {
3171 if (result.valuePixelMap) {
3172 pattern->AddPixelMapToUdmfData(result.valuePixelMap, unifiedData);
3173 } else if (u8ValueString.size() > 1) {
3174 UdmfClient::GetInstance()->AddImageRecord(unifiedData, u8ValueString);
3175 } else {
3176 // builder span, fill pixelmap data
3177 auto builderNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(result.spanPosition.spanIndex));
3178 CHECK_NULL_VOID(builderNode);
3179 pattern->AddPixelMapToUdmfData(builderNode->GetDragPixelMap(), unifiedData);
3180 }
3181 }
3182 };
3183 for (const auto& resultObj : finalResult) {
3184 resultProcessor(resultObj);
3185 }
3186 }
3187
AddPixelMapToUdmfData(const RefPtr<PixelMap> & pixelMap,const RefPtr<UnifiedData> & unifiedData)3188 void TextPattern::AddPixelMapToUdmfData(const RefPtr<PixelMap>& pixelMap, const RefPtr<UnifiedData>& unifiedData)
3189 {
3190 CHECK_NULL_VOID(pixelMap && unifiedData);
3191 const uint8_t* pixels = pixelMap->GetPixels();
3192 CHECK_NULL_VOID(pixels);
3193 int32_t length = pixelMap->GetByteCount();
3194 std::vector<uint8_t> data(pixels, pixels + length);
3195 PixelMapRecordDetails details = { pixelMap->GetWidth(), pixelMap->GetHeight(),
3196 pixelMap->GetPixelFormat(), pixelMap->GetAlphaType() };
3197 UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
3198 }
3199
CloseOperate()3200 void TextPattern::CloseOperate()
3201 {
3202 UpdateSpanItemDragStatus(dragResultObjects_, true);
3203 recoverDragResultObjects_ = dragResultObjects_;
3204 AceEngineExt::GetInstance().DragStartExt();
3205 CloseKeyboard(true);
3206 CloseSelectOverlay();
3207 ResetSelection();
3208 }
3209
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)3210 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
3211 {
3212 auto weakPtr = WeakClaim(this);
3213 DragDropInfo itemInfo;
3214 auto pattern = weakPtr.Upgrade();
3215 auto host = pattern->GetHost();
3216 auto hub = host->GetOrCreateEventHub<EventHub>();
3217 auto gestureHub = hub->GetOrCreateGestureEventHub();
3218 CHECK_NULL_RETURN(gestureHub, itemInfo);
3219 if (!gestureHub->GetIsTextDraggable()) {
3220 return itemInfo;
3221 }
3222 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
3223 dragBoxes_ = GetTextBoxes();
3224 pattern->status_ = Status::DRAGGING;
3225 pattern->contentMod_->ChangeDragStatus();
3226 pattern->showSelect_ = false;
3227 auto [start, end] = GetSelectedStartAndEnd();
3228 pattern->recoverStart_ = start;
3229 pattern->recoverEnd_ = end;
3230 auto beforeStr = GetSelectedText(0, start, false, true);
3231 auto selectedStr = GetSelectedText(start, end, false, true);
3232 auto afterStr = GetSelectedText(end, textForDisplay_.length(), false, true);
3233 pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
3234 auto selectedUtf8Str = UtfUtils::Str16DebugToStr8(selectedStr);
3235 itemInfo.extraInfo = selectedUtf8Str;
3236 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
3237 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedUtf8Str);
3238 event->SetData(unifiedData);
3239 host->MarkDirtyWithOnProChange(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1
3240 ? PROPERTY_UPDATE_MEASURE_SELF
3241 : PROPERTY_UPDATE_MEASURE);
3242
3243 CloseSelectOverlay();
3244 ResetSelection();
3245 ResetAISelected(AIResetSelectionReason::DRAG_START_ON_CHILDREN);
3246 return itemInfo;
3247 }
3248
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)3249 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
3250 {
3251 if (resultObjects.empty()) {
3252 return;
3253 }
3254 auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
3255 auto pattern = weakPtr.Upgrade();
3256 CHECK_NULL_VOID(pattern && !pattern->spans_.empty());
3257 auto it = pattern->spans_.begin();
3258 if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
3259 std::advance(it, !pattern->spans_.empty() ? static_cast<int32_t>(pattern->spans_.size()) - 1 : 0);
3260 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
3261 } else {
3262 std::advance(it, resultObj.spanPosition.spanIndex);
3263 }
3264 auto spanItem = *it;
3265 CHECK_NULL_VOID(spanItem);
3266 if (resultObj.type == SelectSpanType::TYPESPAN) {
3267 if (pattern->isSpanStringMode_) {
3268 spanItem = resultObj.span.Upgrade();
3269 CHECK_NULL_VOID(spanItem);
3270 }
3271 spanItem->MarkDirty();
3272 if (isDragging) {
3273 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
3274 resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
3275 pattern->dragSpanItems_.emplace_back(spanItem);
3276 } else {
3277 spanItem->EndDrag();
3278 }
3279 return;
3280 }
3281 spanItem->MarkDirty();
3282 if (resultObj.type == SelectSpanType::TYPEIMAGE) {
3283 if (isDragging) {
3284 pattern->dragSpanItems_.emplace_back(spanItem);
3285 }
3286 auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
3287 CHECK_NULL_VOID(imageNode);
3288 auto renderContext = imageNode->GetRenderContext();
3289 CHECK_NULL_VOID(renderContext);
3290 renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
3291 imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3292 }
3293 };
3294 for (const auto& resultObj : resultObjects) {
3295 dragStatusUpdateAction(resultObj);
3296 }
3297 }
3298
OnDragEnd(const RefPtr<Ace::DragEvent> & event)3299 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
3300 {
3301 ResetDragRecordSize(-1);
3302 auto wk = WeakClaim(this);
3303 auto pattern = wk.Upgrade();
3304 CHECK_NULL_VOID(pattern);
3305 auto host = GetHost();
3306 CHECK_NULL_VOID(host);
3307 isMousePressed_ = false;
3308 if (status_ == Status::DRAGGING) {
3309 status_ = Status::NONE;
3310 }
3311 dragSpanItems_.clear();
3312 if (dragResultObjects_.empty()) {
3313 return;
3314 }
3315 UpdateSpanItemDragStatus(dragResultObjects_, false);
3316 dragResultObjects_.clear();
3317 if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
3318 HandleSelectionChange(recoverStart_, recoverEnd_);
3319 isShowMenu_ = false;
3320 if (GetCurrentDragTool() == SourceTool::FINGER) {
3321 CalculateHandleOffsetAndShowOverlay();
3322 ShowSelectOverlay({ .menuIsShow = false });
3323 }
3324 }
3325 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3326 }
3327
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)3328 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
3329 {
3330 auto wk = WeakClaim(this);
3331 auto pattern = wk.Upgrade();
3332 CHECK_NULL_VOID(pattern);
3333 auto host = pattern->GetHost();
3334 CHECK_NULL_VOID(host);
3335 isMousePressed_ = false;
3336 if (pattern->status_ == Status::DRAGGING) {
3337 pattern->status_ = Status::NONE;
3338 pattern->MarkContentChange();
3339 pattern->contentMod_->ChangeDragStatus();
3340 if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
3341 HandleSelectionChange(recoverStart_, recoverEnd_);
3342 isShowMenu_ = false;
3343 if (GetCurrentDragTool() == SourceTool::FINGER) {
3344 CalculateHandleOffsetAndShowOverlay();
3345 ShowSelectOverlay({ .menuIsShow = false });
3346 }
3347 }
3348 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
3349 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3350 }
3351 }
3352
OnDragMove(const RefPtr<Ace::DragEvent> & event)3353 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
3354 {
3355 auto weakPtr = WeakClaim(this);
3356 auto pattern = weakPtr.Upgrade();
3357 if (pattern->status_ == Status::DRAGGING) {
3358 CloseSelectOverlay();
3359 pattern->showSelect_ = false;
3360 }
3361 }
3362
InitDragEvent()3363 void TextPattern::InitDragEvent()
3364 {
3365 auto host = GetHost();
3366 CHECK_NULL_VOID(host);
3367 auto eventHub = host->GetOrCreateEventHub<EventHub>();
3368 CHECK_NULL_VOID(eventHub);
3369 auto gestureHub = host->GetOrCreateGestureEventHub();
3370 CHECK_NULL_VOID(gestureHub);
3371 gestureHub->InitDragDropEvent();
3372 gestureHub->SetTextDraggable(true);
3373 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
3374 auto onDragStart = [weakPtr = WeakClaim(this)](
3375 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
3376 NG::DragDropInfo itemInfo;
3377 auto pattern = weakPtr.Upgrade();
3378 CHECK_NULL_RETURN(pattern, itemInfo);
3379 auto eventHub = pattern->GetOrCreateEventHub<EventHub>();
3380 CHECK_NULL_RETURN(eventHub, itemInfo);
3381 pattern->SetCurrentDragTool(event->GetSourceTool());
3382 if (pattern->spans_.empty() && !pattern->isSpanStringMode_) {
3383 return pattern->OnDragStartNoChild(event, extraParams);
3384 }
3385 return pattern->OnDragStart(event, extraParams);
3386 };
3387 eventHub->SetDefaultOnDragStart(std::move(onDragStart));
3388 auto onDragMove = [weakPtr = WeakClaim(this)](
3389 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
3390 auto pattern = weakPtr.Upgrade();
3391 CHECK_NULL_VOID(pattern);
3392 pattern->OnDragMove(event);
3393 };
3394 eventHub->SetOnDragMove(std::move(onDragMove));
3395 auto onDragEnd = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event) {
3396 auto pattern = weakPtr.Upgrade();
3397 CHECK_NULL_VOID(pattern);
3398 // 拖拽框架强引用导致退出页面后还能够运行到这里
3399 if (pattern->isDetachFromMainTree_) {
3400 return;
3401 }
3402 ContainerScope scope(pattern->GetHostInstanceId());
3403 pattern->showSelect_ = true;
3404 if (pattern->spans_.empty()) {
3405 pattern->OnDragEndNoChild(event);
3406 } else {
3407 pattern->OnDragEnd(event);
3408 }
3409 };
3410 eventHub->SetOnDragEnd(std::move(onDragEnd));
3411 }
3412
ClearDragEvent()3413 void TextPattern::ClearDragEvent()
3414 {
3415 auto host = GetHost();
3416 CHECK_NULL_VOID(host);
3417 auto eventHub = host->GetOrCreateEventHub<EventHub>();
3418 CHECK_NULL_VOID(eventHub);
3419 auto gestureHub = host->GetOrCreateGestureEventHub();
3420 CHECK_NULL_VOID(gestureHub);
3421 gestureHub->SetTextDraggable(false);
3422 gestureHub->SetIsTextDraggable(false);
3423 gestureHub->SetThumbnailCallback(nullptr);
3424 eventHub->SetDefaultOnDragStart(nullptr);
3425 eventHub->SetOnDragMove(nullptr);
3426 eventHub->SetOnDragEnd(nullptr);
3427 }
3428
GetThumbnailCallback()3429 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
3430 {
3431 return [wk = WeakClaim(this)](const Offset& point) {
3432 auto pattern = wk.Upgrade();
3433 CHECK_NULL_VOID(pattern);
3434 pattern->InitAiSelection(point);
3435 if (pattern->BetweenSelectedPosition(point) || pattern->IsAiSelected()) {
3436 const auto& children = pattern->GetChildNodes();
3437 std::list<RefPtr<FrameNode>> imageChildren;
3438 for (const auto& child : children) {
3439 auto node = DynamicCast<FrameNode>(child);
3440 if (!node) {
3441 continue;
3442 }
3443 auto image = node->GetPattern<ImagePattern>();
3444 if (image) {
3445 imageChildren.emplace_back(node);
3446 }
3447 }
3448 auto info = pattern->CreateTextDragInfo();
3449 pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren, info);
3450 auto textDragPattern = pattern->dragNode_->GetPattern<TextDragPattern>();
3451 if (textDragPattern) {
3452 auto option = pattern->GetHost()->GetDragPreviewOption();
3453 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
3454 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
3455 ShadowStyle::OuterFloatingSM);
3456 pattern->GetHost()->SetDragPreviewOptions(option);
3457 }
3458 FrameNode::ProcessOffscreenNode(pattern->dragNode_);
3459 }
3460 };
3461 }
3462
CreateTextDragInfo()3463 TextDragInfo TextPattern::CreateTextDragInfo()
3464 {
3465 TextDragInfo info;
3466 auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
3467 CHECK_NULL_RETURN(context, info);
3468 auto theme = context->GetTheme<TextTheme>();
3469 CHECK_NULL_RETURN(theme, info);
3470 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3471 CHECK_NULL_RETURN(textLayoutProperty, info);
3472 info.handleColor = theme->GetCaretColor();
3473 info.selectedBackgroundColor = theme->GetSelectedColor();
3474 selectOverlay_->GetVisibleDragViewHandles(info.firstHandle, info.secondHandle);
3475 if (IsAiSelected()) {
3476 info.isFirstHandleAnimation = false;
3477 info.isSecondHandleAnimation = false;
3478 }
3479 return info;
3480 }
3481
GetAllChildren() const3482 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
3483 {
3484 return childNodes_;
3485 }
3486
GetSelectedSpanText(std::u16string value,int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const3487 std::u16string TextPattern::GetSelectedSpanText(std::u16string value, int32_t start, int32_t end, bool includeStartHalf,
3488 bool includeEndHalf, bool getSubstrDirectly) const
3489 {
3490 if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
3491 return u"";
3492 }
3493 auto min = std::min(start, end);
3494 auto max = std::max(start, end);
3495 if (getSubstrDirectly) {
3496 min = std::clamp(min, 0, static_cast<int32_t>(value.length()));
3497 return value.substr(min, max - min);
3498 } else {
3499 return TextEmojiProcessor::SubU16string(min, max - min, value, includeStartHalf, includeEndHalf);
3500 }
3501 }
3502
GetTextStyleObject(const RefPtr<SpanNode> & node)3503 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
3504 {
3505 TextStyleResult textStyle;
3506 textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
3507 textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
3508 textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
3509 std::string fontFamilyValue;
3510 const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
3511 auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
3512 for (const auto& str : fontFamily) {
3513 fontFamilyValue += str;
3514 fontFamilyValue += ",";
3515 }
3516 fontFamilyValue =
3517 fontFamilyValue.substr(0, !fontFamilyValue.empty() ? static_cast<int32_t>(fontFamilyValue.size()) - 1 : 0);
3518 textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
3519 textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationFirst());
3520 textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
3521 textStyle.decorationStyle = static_cast<int32_t>(node->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
3522 textStyle.lineThicknessScale = node->GetLineThicknessScaleValue(1.0f);
3523 textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
3524 auto lm = node->GetLeadingMarginValue({});
3525 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
3526 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToFp();
3527 textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToFp();
3528 textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToFp();
3529 textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToFp();
3530 } else {
3531 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
3532 textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToVp();
3533 textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToVp();
3534 textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToVp();
3535 }
3536 textStyle.optimizeTrailingSpace = node->GetOptimizeTrailingSpaceValue(false);
3537 textStyle.halfLeading = node->GetHalfLeadingValue(false);
3538 textStyle.fontFeature = node->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1"));
3539 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.size.Width().ToString();
3540 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.size.Height().ToString();
3541 textStyle.wordBreak = static_cast<int32_t>(node->GetWordBreakValue(WordBreak::BREAK_WORD));
3542 textStyle.lineBreakStrategy = static_cast<int32_t>(node->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY));
3543 textStyle.textShadows = node->GetTextShadowValue({});
3544 textStyle.textBackgroundStyle = node->GetTextBackgroundStyle();
3545 textStyle.paragraphSpacing = node->GetParagraphSpacing();
3546 auto textVerticalAlign = node->GetTextVerticalAlign();
3547 if (textVerticalAlign.has_value()) {
3548 textStyle.textVerticalAlign =static_cast<int32_t>(textVerticalAlign.value());
3549 }
3550 return textStyle;
3551 }
3552
GetChildByIndex(int32_t index) const3553 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
3554 {
3555 const auto& children = childNodes_;
3556 int32_t size = static_cast<int32_t>(children.size());
3557 if (index < 0 || index >= size) {
3558 return nullptr;
3559 }
3560 auto pos = children.begin();
3561 std::advance(pos, index);
3562 return *pos;
3563 }
3564
GetSpanItemByIndex(int32_t index) const3565 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
3566 {
3567 int32_t size = static_cast<int32_t>(spans_.size());
3568 if (index < 0 || index >= size) {
3569 return nullptr;
3570 }
3571 auto pos = spans_.begin();
3572 std::advance(pos, index);
3573 return *pos;
3574 }
3575
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3576 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3577 {
3578 bool selectFlag = false;
3579 ResultObject resultObject;
3580 if (!DynamicCast<SpanNode>(uinode)) {
3581 return resultObject;
3582 }
3583 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
3584 int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
3585 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
3586 int32_t startPosition = endPosition - itemLength;
3587
3588 if (startPosition >= start && endPosition <= end) {
3589 selectFlag = true;
3590 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3591 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3592 } else if (startPosition < start && endPosition <= end && endPosition > start) {
3593 selectFlag = true;
3594 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3595 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3596 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
3597 selectFlag = true;
3598 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3599 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3600 } else if (startPosition <= start && endPosition >= end) {
3601 selectFlag = true;
3602 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3603 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3604 }
3605 if (selectFlag) {
3606 resultObject.spanPosition.spanIndex = index;
3607 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3608 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3609 resultObject.type = SelectSpanType::TYPESPAN;
3610 SetResultObjectText(resultObject, spanItem);
3611 auto spanNode = DynamicCast<SpanNode>(uinode);
3612 resultObject.textStyle = GetTextStyleObject(spanNode);
3613 }
3614 return resultObject;
3615 }
3616
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)3617 void TextPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
3618 {
3619 CHECK_NULL_VOID(spanItem);
3620 resultObject.valueString = spanItem->content;
3621 }
3622
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3623 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3624 {
3625 bool selectFlag = false;
3626 ResultObject resultObject;
3627 resultObject.isDraggable = false;
3628 if (!DynamicCast<SpanNode>(uinode)) {
3629 return resultObject;
3630 }
3631 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
3632 int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
3633 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
3634 int32_t startPosition = endPosition - itemLength;
3635
3636 if (startPosition >= start && endPosition <= end) {
3637 selectFlag = true;
3638 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3639 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3640 } else if (startPosition < start && endPosition <= end && endPosition > start) {
3641 selectFlag = true;
3642 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3643 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3644 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
3645 selectFlag = true;
3646 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3647 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3648 } else if (startPosition <= start && endPosition >= end) {
3649 selectFlag = true;
3650 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
3651 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
3652 }
3653 if (selectFlag) {
3654 resultObject.valueResource = spanItem->GetResourceObject();
3655 resultObject.spanPosition.spanIndex = index;
3656 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3657 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3658 resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
3659 resultObject.valueString = UtfUtils::Str8ToStr16(std::to_string(spanItem->unicode));
3660 auto spanNode = DynamicCast<SpanNode>(uinode);
3661 resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
3662 }
3663 return resultObject;
3664 }
3665
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)3666 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
3667 {
3668 SymbolSpanStyle symbolSpanStyle;
3669 std::string symbolColorValue;
3670 auto symbolColors = node->GetSymbolColorList();
3671 if (symbolColors.has_value()) {
3672 for (const auto& color : *symbolColors) {
3673 symbolColorValue += color.ColorToString() + ",";
3674 }
3675 }
3676 symbolColorValue =
3677 symbolColorValue.substr(0, !symbolColorValue.empty() ? static_cast<int32_t>(symbolColorValue.size()) - 1 : 0);
3678 symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
3679 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
3680 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToFp();
3681 } else {
3682 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
3683 }
3684 symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
3685 symbolSpanStyle.renderingStrategy = static_cast<int32_t>(node->GetSymbolRenderingStrategyValue(0));
3686 symbolSpanStyle.effectStrategy = static_cast<int32_t>(node->GetSymbolEffectStrategyValue(0));
3687 return symbolSpanStyle;
3688 }
3689
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)3690 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
3691 {
3692 int32_t itemLength = 1;
3693 ResultObject resultObject;
3694 if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
3695 return resultObject;
3696 }
3697 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
3698 int32_t startPosition = endPosition - itemLength;
3699 if ((start <= startPosition) && (end >= endPosition)) {
3700 auto imageNode = DynamicCast<FrameNode>(uinode);
3701 CHECK_NULL_RETURN(imageNode, resultObject);
3702 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
3703 CHECK_NULL_RETURN(imageLayoutProperty, resultObject);
3704 resultObject.spanPosition.spanIndex = index;
3705 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
3706 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
3707 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
3708 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
3709 resultObject.type = SelectSpanType::TYPEIMAGE;
3710 if (imageLayoutProperty->GetImageSourceInfo() && !imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
3711 resultObject.valueString = UtfUtils::Str8DebugToStr16(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
3712 } else {
3713 resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
3714 }
3715 auto geometryNode = imageNode->GetGeometryNode();
3716 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
3717 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
3718 if (imageLayoutProperty->HasImageFit()) {
3719 resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
3720 }
3721 if (imageLayoutProperty->HasVerticalAlign()) {
3722 resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
3723 }
3724 if (imageLayoutProperty->GetMarginProperty()) {
3725 resultObject.imageStyle.margin = imageLayoutProperty->GetMarginProperty()->ToString();
3726 }
3727 auto imageRenderCtx = imageNode->GetRenderContext();
3728 if (imageRenderCtx->GetBorderRadius()) {
3729 BorderRadiusProperty brp;
3730 auto jsonObject = JsonUtil::Create(true);
3731 auto jsonBorder = JsonUtil::Create(true);
3732 InspectorFilter emptyFilter;
3733 imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, emptyFilter);
3734 resultObject.imageStyle.borderRadius = jsonObject->GetValue("borderRadius")->IsObject()
3735 ? jsonObject->GetValue("borderRadius")->ToString()
3736 : jsonObject->GetString("borderRadius");
3737 }
3738 }
3739 return resultObject;
3740 }
3741
3742 // ===========================================================
3743 // TextDragBase implementations
GetLineHeight() const3744 float TextPattern::GetLineHeight() const
3745 {
3746 auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3747 CHECK_NULL_RETURN(selectedRects.size(), {});
3748 return selectedRects.front().Height();
3749 }
3750
GetTextBoxes()3751 std::vector<RectF> TextPattern::GetTextBoxes()
3752 {
3753 if (IsAiSelected()) {
3754 return pManager_->GetRects(textSelector_.aiStart.value(), textSelector_.aiEnd.value());
3755 }
3756 return pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
3757 }
3758
GetParentGlobalOffset() const3759 OffsetF TextPattern::GetParentGlobalOffset() const
3760 {
3761 selectOverlay_->UpdateHandleGlobalOffset();
3762 auto host = GetHost();
3763 CHECK_NULL_RETURN(host, {});
3764 auto pipeline = host->GetContext();
3765 CHECK_NULL_RETURN(pipeline, {});
3766 auto rootOffset = pipeline->GetRootRect().GetOffset();
3767 return host->GetPaintRectOffsetNG(false, true) - rootOffset;
3768 }
3769
CreateHandles()3770 void TextPattern::CreateHandles()
3771 {
3772 if (IsDragging() || IsAiSelected()) {
3773 TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging or AISpan selected");
3774 return;
3775 }
3776 ShowSelectOverlay({ .menuIsShow = false });
3777 }
3778
BetweenSelectedPosition(const Offset & globalOffset)3779 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
3780 {
3781 auto host = GetHost();
3782 CHECK_NULL_RETURN(host, false);
3783 auto offset = host->GetPaintRectOffset(false, true);
3784 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
3785 if (selectOverlay_->HasRenderTransform()) {
3786 localOffset = ConvertGlobalToLocalOffset(globalOffset);
3787 }
3788 auto result = IsDraggable(localOffset);
3789 return result;
3790 }
3791
LogForFormRender(const std::string & logTag)3792 void TextPattern::LogForFormRender(const std::string& logTag)
3793 {
3794 auto host = GetHost();
3795 CHECK_NULL_VOID(host);
3796 auto pipeline = host->GetContext();
3797 CHECK_NULL_VOID(pipeline);
3798 if (pipeline->IsFormRender() && !IsSetObscured() && !IsSensitiveEnable()) {
3799 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3800 CHECK_NULL_VOID(textLayoutProperty);
3801 auto content = textLayoutProperty->GetContent().value_or(u"");
3802 if (content.length() == 1) {
3803 TAG_LOGI(AceLogTag::ACE_TEXT, "%{public}s, content:%{public}s id:%{public}d", logTag.c_str(),
3804 UtfUtils::Str16ToStr8(content).c_str(), host->GetId());
3805 }
3806 }
3807 }
3808
3809 // end of TextDragBase implementations
3810 // ===========================================================
OnModifyDone()3811 void TextPattern::OnModifyDone()
3812 {
3813 auto host = GetHost();
3814 Pattern::OnModifyDone();
3815 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3816 CHECK_NULL_VOID(textLayoutProperty);
3817 auto contentHost = GetContentHost();
3818 CHECK_NULL_VOID(host && contentHost);
3819 auto renderContext = host->GetRenderContext();
3820 CHECK_NULL_VOID(renderContext);
3821 auto nowTime = static_cast<unsigned long long>(GetSystemTimestamp());
3822 ACE_TEXT_SCOPED_TRACE("OnModifyDone[Text][id:%d][time:%llu]", host->GetId(), nowTime);
3823 auto pipeline = host->GetContext();
3824 if (!(pipeline && pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
3825 bool shouldClipToContent =
3826 textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
3827 host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
3828 }
3829 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
3830 if (!renderContext->GetClipEdge().has_value()) {
3831 renderContext->UpdateClipEdge(true);
3832 renderContext->SetClipToFrame(true);
3833 }
3834 UpdateMarqueeStartPolicy();
3835 }
3836 const auto& children = contentHost->GetChildren();
3837 if (children.empty()) {
3838 std::u16string textCache = textForDisplay_;
3839 if (!isSpanStringMode_) {
3840 textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
3841 }
3842 if (textCache != textForDisplay_) {
3843 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
3844 UtfUtils::Str16DebugToStr8(textForDisplay_));
3845 if (dataDetectorAdapter_) {
3846 dataDetectorAdapter_->aiDetectInitialized_ = false;
3847 }
3848 }
3849 if (CanStartAITask() && !GetDataDetectorAdapter()->aiDetectInitialized_) {
3850 ParseOriText(textForDisplay_);
3851 }
3852 if (textCache != textForDisplay_) { // textForDisplay_ is updated by ParseOriText
3853 CloseSelectOverlay();
3854 ResetSelection();
3855 }
3856 }
3857 ResetTextEffectBeforeLayout();
3858 RecoverCopyOption();
3859 RegisterFormVisibleChangeCallback();
3860 RegisterVisibleAreaChangeCallback();
3861 }
3862
UpdateMarqueeStartPolicy()3863 void TextPattern::UpdateMarqueeStartPolicy()
3864 {
3865 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3866 CHECK_NULL_VOID(textLayoutProperty);
3867 if (!textLayoutProperty->HasTextMarqueeStartPolicy()) {
3868 auto host = GetHost();
3869 CHECK_NULL_VOID(host);
3870 auto context = host->GetContext();
3871 CHECK_NULL_VOID(context);
3872 auto theme = context->GetTheme<TextTheme>();
3873 CHECK_NULL_VOID(theme);
3874 textLayoutProperty->UpdateTextMarqueeStartPolicy(theme->GetMarqueeStartPolicy());
3875 }
3876 if (textLayoutProperty->GetTextMarqueeStartPolicyValue(MarqueeStartPolicy::DEFAULT) ==
3877 MarqueeStartPolicy::ON_FOCUS) {
3878 InitFocusEvent();
3879 InitHoverEvent();
3880 }
3881 }
3882
SetActionExecSubComponent()3883 bool TextPattern::SetActionExecSubComponent()
3884 {
3885 auto host = GetHost();
3886 CHECK_NULL_RETURN(host, false);
3887 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3888 CHECK_NULL_RETURN(accessibilityProperty, false);
3889 accessibilityProperty->SetActionExecSubComponent([weakPtr = WeakClaim(this)](int32_t spanId) -> bool {
3890 const auto& pattern = weakPtr.Upgrade();
3891 CHECK_NULL_RETURN(pattern, false);
3892 return pattern->ExecSubComponent(spanId);
3893 });
3894 return true;
3895 }
3896
GetSubComponentInfos(std::vector<SubComponentInfo> & subComponentInfos)3897 size_t TextPattern::GetSubComponentInfos(std::vector<SubComponentInfo>& subComponentInfos)
3898 {
3899 subComponentInfos.clear();
3900 subComponentInfos_.clear();
3901 if (spans_.empty()) {
3902 GetSubComponentInfosForAISpans(subComponentInfos);
3903 } else {
3904 GetSubComponentInfosForSpans(subComponentInfos);
3905 }
3906 SetActionExecSubComponent();
3907 return subComponentInfos.size();
3908 }
3909
GetSubComponentInfosForAISpans(std::vector<SubComponentInfo> & subComponentInfos)3910 void TextPattern::GetSubComponentInfosForAISpans(std::vector<SubComponentInfo>& subComponentInfos)
3911 {
3912 CHECK_NULL_VOID(dataDetectorAdapter_);
3913 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
3914 auto& aiSpan = kv.second;
3915 AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3916 }
3917 }
3918
GetSubComponentInfosForSpans(std::vector<SubComponentInfo> & subComponentInfos)3919 void TextPattern::GetSubComponentInfosForSpans(std::vector<SubComponentInfo>& subComponentInfos)
3920 {
3921 for (const auto& span : spans_) {
3922 if (span == nullptr) {
3923 continue; // skip null
3924 }
3925 if ((span->spanItemType == SpanItemType::IMAGE) || (span->unicode > 0)) {
3926 continue; // skip ImageSpan and SymbolSpan
3927 }
3928 if (span->spanItemType == SpanItemType::CustomSpan) {
3929 continue; // skip CustomSpan
3930 }
3931 auto placeholderSpan = DynamicCast<PlaceholderSpanItem>(span);
3932 if ((placeholderSpan != nullptr) && (placeholderSpan->placeholderSpanNodeId >=0)) {
3933 continue; // skip PlaceholderSpan
3934 }
3935 if (span->content.empty()) {
3936 continue; // skip empty text
3937 }
3938 AddSubComponentInfoForSpan(subComponentInfos, UtfUtils::Str16DebugToStr8(span->content), span);
3939 AddSubComponentInfosByDataDetectorForSpan(subComponentInfos, span);
3940 }
3941 }
3942
AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo> & subComponentInfos,const RefPtr<SpanItem> & span)3943 void TextPattern::AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo>& subComponentInfos,
3944 const RefPtr<SpanItem>& span)
3945 {
3946 CHECK_NULL_VOID(span);
3947 CHECK_NULL_VOID(dataDetectorAdapter_);
3948 int32_t wSpanContentLength = static_cast<int32_t>(span->content.length());
3949 int32_t spanStart = span->position - wSpanContentLength;
3950 if (span->needRemoveNewLine) {
3951 spanStart -= 1;
3952 }
3953 int32_t preEnd = spanStart;
3954 auto aiSpanMap = dataDetectorAdapter_->aiSpanMap_;
3955 while (!aiSpanMap.empty()) {
3956 auto aiSpan = aiSpanMap.begin()->second;
3957 if (aiSpan.start >= span->position || preEnd >= span->position) {
3958 break;
3959 }
3960 int32_t aiSpanStartInSpan = std::max(spanStart, aiSpan.start);
3961 int32_t aiSpanEndInSpan = std::min(span->position, aiSpan.end);
3962 if (aiSpan.end <= spanStart || aiSpanStartInSpan < preEnd) {
3963 TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
3964 aiSpanMap.erase(aiSpanMap.begin());
3965 continue;
3966 }
3967 AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3968 preEnd = aiSpanEndInSpan;
3969 if (aiSpan.end > span->position) {
3970 return;
3971 } else {
3972 aiSpanMap.erase(aiSpanMap.begin());
3973 }
3974 }
3975 }
3976
ExecSubComponent(int32_t spanId)3977 bool TextPattern::ExecSubComponent(int32_t spanId)
3978 {
3979 if ((spanId < 0) || (spanId >= static_cast<int32_t>(subComponentInfos_.size()))) {
3980 return false;
3981 }
3982 auto subComponentInfo = subComponentInfos_[spanId];
3983 if (subComponentInfo.aiSpan.has_value()) {
3984 CHECK_NULL_RETURN(GetDataDetectorAdapter(), false);
3985 dataDetectorAdapter_->ResponseBestMatchItem(subComponentInfo.aiSpan.value());
3986 return true;
3987 }
3988 const auto& span = subComponentInfo.span.Upgrade();
3989 CHECK_NULL_RETURN(span, false);
3990 CHECK_NULL_RETURN(span->onClick, false);
3991 GestureEvent info;
3992 std::chrono::microseconds microseconds(GetMicroTickCount());
3993 TimeStamp time(microseconds);
3994 info.SetTimeStamp(time);
3995 span->onClick(info);
3996 return true;
3997 }
3998
AddSubComponentInfoForSpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const RefPtr<SpanItem> & span)3999 void TextPattern::AddSubComponentInfoForSpan(std::vector<SubComponentInfo>& subComponentInfos,
4000 const std::string& content, const RefPtr<SpanItem>& span)
4001 {
4002 CHECK_NULL_VOID(span);
4003 CHECK_NULL_VOID(span->onClick); // skip null onClick
4004 SubComponentInfo subComponentInfo;
4005 subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
4006 subComponentInfo.spanText = content;
4007 if (span->accessibilityProperty == nullptr) {
4008 subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
4009 } else {
4010 subComponentInfo.accessibilityText = span->accessibilityProperty->GetAccessibilityText();
4011 subComponentInfo.accessibilityDescription =
4012 span->accessibilityProperty->GetAccessibilityDescription();
4013 subComponentInfo.accessibilityLevel = span->accessibilityProperty->GetAccessibilityLevel();
4014 }
4015 subComponentInfos.emplace_back(subComponentInfo);
4016
4017 SubComponentInfoEx subComponentInfoEx;
4018 subComponentInfoEx.span = span;
4019 subComponentInfos_.emplace_back(subComponentInfoEx);
4020 }
4021
AddSubComponentInfoForAISpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const AISpan & aiSpan)4022 void TextPattern::AddSubComponentInfoForAISpan(std::vector<SubComponentInfo>& subComponentInfos,
4023 const std::string& content, const AISpan& aiSpan)
4024 {
4025 SubComponentInfo subComponentInfo;
4026 subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
4027 subComponentInfo.spanText = content;
4028 subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
4029 subComponentInfos.emplace_back(subComponentInfo);
4030
4031 SubComponentInfoEx subComponentInfoEx;
4032 subComponentInfoEx.aiSpan = aiSpan;
4033 subComponentInfos_.emplace_back(subComponentInfoEx);
4034 }
4035
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const4036 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
4037 {
4038 json->PutFixedAttr("content", UtfUtils::Str16ToStr8(textForDisplay_).c_str(), filter, FIXED_ATTR_CONTENT);
4039 /* no fixed attr below, just return */
4040 if (filter.IsFastFilter()) {
4041 return;
4042 }
4043 json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
4044 json->PutExtAttr("dataDetectorConfig",
4045 dataDetectorAdapter_ ? dataDetectorAdapter_->textDetectConfigStr_.c_str() : "", filter);
4046 const auto& selector = GetTextSelector();
4047 auto result = "[" + std::to_string(selector.GetTextStart()) + "," + std::to_string(selector.GetTextEnd()) + "]";
4048 json->PutExtAttr("selection", result.c_str(), filter);
4049 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4050 CHECK_NULL_VOID(textLayoutProp);
4051 json->PutExtAttr("fontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
4052 if (textStyle_.has_value() && textStyle_->GetAdaptTextSize()) {
4053 auto adaptedFontSize = textStyle_->GetFontSize();
4054 json->PutExtAttr("actualFontSize", adaptedFontSize.ToString().c_str(), filter);
4055 } else {
4056 json->PutExtAttr("actualFontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
4057 }
4058 json->PutExtAttr("font", GetFontInJson().c_str(), filter);
4059 json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
4060 json->PutExtAttr("caretColor", GetCaretColor().c_str(), filter);
4061 json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().c_str(), filter);
4062 json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
4063 json->PutExtAttr("shaderStyle", GetShaderStyleInJson(), filter);
4064 }
4065
GetShaderStyleInJson() const4066 std::unique_ptr<JsonValue> TextPattern::GetShaderStyleInJson() const
4067 {
4068 auto resultJson = JsonUtil::Create(true);
4069 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4070 CHECK_NULL_RETURN(layoutProperty, resultJson);
4071 auto shaderStyle = layoutProperty->GetShaderStyle();
4072 if (shaderStyle.has_value() && shaderStyle->size() > 0) {
4073 resultJson = ::OHOS::Ace::NG::GetShaderStyleInJson(shaderStyle);
4074 return resultJson;
4075 }
4076 if (layoutProperty->HasGradientShaderStyle()) {
4077 auto propGradient = layoutProperty->GetGradientShaderStyle().value_or(Gradient());
4078 auto type = propGradient.GetType();
4079 if (type == GradientType::LINEAR) {
4080 return GradientJsonUtils::LinearGradientToJson(propGradient);
4081 } else if (type == GradientType::RADIAL) {
4082 return GradientJsonUtils::RadialGradientToJson(propGradient);
4083 }
4084 } else if (layoutProperty->HasColorShaderStyle()) {
4085 resultJson->Put(
4086 "color", layoutProperty->GetColorShaderStyle().value_or(Color::TRANSPARENT).ColorToString().c_str());
4087 return resultJson;
4088 }
4089 return resultJson;
4090 }
4091
GetBindSelectionMenuInJson() const4092 std::string TextPattern::GetBindSelectionMenuInJson() const
4093 {
4094 auto jsonArray = JsonUtil::CreateArray(true);
4095 for (auto& [spanResponsePair, params] : selectionMenuMap_) {
4096 auto& [spanType, responseType] = spanResponsePair;
4097 auto jsonItem = JsonUtil::Create(true);
4098 jsonItem->Put("spanType", TextSpanTypeMapper::GetJsSpanType(spanType, params->isValid));
4099 jsonItem->Put("responseType", static_cast<int32_t>(responseType));
4100 jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::SELECTION_MENU));
4101 jsonArray->Put(jsonItem);
4102 }
4103 FillPreviewMenuInJson(jsonArray);
4104 return StringUtils::RestoreBackslash(jsonArray->ToString());
4105 }
4106
GetFontInJson() const4107 std::string TextPattern::GetFontInJson() const
4108 {
4109 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4110 CHECK_NULL_RETURN(textLayoutProp, "");
4111 auto jsonValue = JsonUtil::Create(true);
4112 jsonValue->Put("style", GetFontStyleInJson(textLayoutProp->GetItalicFontStyle()).c_str());
4113 jsonValue->Put("size", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str());
4114 jsonValue->Put("weight", GetFontWeightInJson(textLayoutProp->GetFontWeight()).c_str());
4115 jsonValue->Put("variableFontWeight", std::to_string(textLayoutProp->GetVariableFontWeight().value_or(0)).c_str());
4116 jsonValue->Put("enableVariableFontWeight",
4117 textLayoutProp->GetEnableVariableFontWeight().value_or(false) ? "true" : "false");
4118 jsonValue->Put("family", GetFontFamilyInJson(textLayoutProp->GetFontFamily()).c_str());
4119 return jsonValue->ToString();
4120 }
4121
GetFontSizeWithThemeInJson(const std::optional<Dimension> & value) const4122 std::string TextPattern::GetFontSizeWithThemeInJson(const std::optional<Dimension>& value) const
4123 {
4124 auto host = GetHost();
4125 CHECK_NULL_RETURN(host, "");
4126 auto pipeline = host->GetContext();
4127 CHECK_NULL_RETURN(pipeline, "");
4128 auto theme = pipeline->GetTheme<TextTheme>();
4129 CHECK_NULL_RETURN(theme, "");
4130 return value.value_or(theme->GetTextStyle().GetFontSize()).ToString();
4131 }
4132
ToTreeJson(std::unique_ptr<JsonValue> & json,const InspectorConfig & config) const4133 void TextPattern::ToTreeJson(std::unique_ptr<JsonValue>& json, const InspectorConfig& config) const
4134 {
4135 Pattern::ToTreeJson(json, config);
4136 if (!textForDisplay_.empty()) {
4137 json->Put(TreeKey::CONTENT, UtfUtils::Str16DebugToStr8(textForDisplay_).c_str());
4138 }
4139 }
4140
OnAfterModifyDone()4141 void TextPattern::OnAfterModifyDone()
4142 {
4143 auto host = GetHost();
4144 CHECK_NULL_VOID(host);
4145 auto inspectorId = host->GetInspectorId().value_or("");
4146 if (!inspectorId.empty()) {
4147 auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
4148 Recorder::NodeDataCache::Get().PutString(host, inspectorId, prop->GetText());
4149 }
4150 }
4151
ActSetSelection(int32_t start,int32_t end)4152 void TextPattern::ActSetSelection(int32_t start, int32_t end)
4153 {
4154 auto host = GetHost();
4155 CHECK_NULL_VOID(host);
4156 int32_t min = 0;
4157 int32_t textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
4158 start = start < min ? min : start;
4159 end = end < min ? min : end;
4160 start = start > textSize ? textSize : start;
4161 end = end > textSize ? textSize : end;
4162 if (SystemProperties::GetTextTraceEnabled()) {
4163 ACE_TEXT_SCOPED_TRACE("TextPattern::ActSetSelection[id:%d][start:%d][end:%d][textSize:%d][placeholderCount:%d]",
4164 host->GetId(), start, end, textSize, placeholderCount_);
4165 }
4166 if (start >= end) {
4167 ResetSelection();
4168 CloseSelectOverlay();
4169 return;
4170 }
4171 HandleSelectionChange(start, end);
4172 CalculateHandleOffsetAndShowOverlay();
4173 if (textSelector_.firstHandle == textSelector_.secondHandle && pManager_) {
4174 ResetSelection();
4175 CloseSelectOverlay();
4176 return;
4177 }
4178 if (IsShowHandle()) {
4179 ShowSelectOverlay();
4180 } else {
4181 CloseSelectOverlay();
4182 if (IsSelected()) {
4183 selectOverlay_->SetSelectionHoldCallback();
4184 }
4185 }
4186 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
4187 }
4188
IsShowHandle()4189 bool TextPattern::IsShowHandle()
4190 {
4191 auto host = GetHost();
4192 CHECK_NULL_RETURN(host, false);
4193 auto pipeline = host->GetContext();
4194 CHECK_NULL_RETURN(pipeline, false);
4195 auto theme = pipeline->GetTheme<TextTheme>(GetThemeScopeId());
4196 CHECK_NULL_RETURN(theme, false);
4197 return !theme->IsShowHandle();
4198 }
4199
GetUrlHoverColor()4200 Color TextPattern::GetUrlHoverColor()
4201 {
4202 auto host = GetHost();
4203 CHECK_NULL_RETURN(host, Color());
4204 auto pipeline = host->GetContext();
4205 CHECK_NULL_RETURN(pipeline, Color());
4206 auto theme = pipeline->GetTheme<TextTheme>();
4207 CHECK_NULL_RETURN(theme, Color());
4208 return theme->GetUrlHoverColor();
4209 }
4210
GetUrlPressColor()4211 Color TextPattern::GetUrlPressColor()
4212 {
4213 auto host = GetHost();
4214 CHECK_NULL_RETURN(host, Color());
4215 auto pipeline = host->GetContext();
4216 CHECK_NULL_RETURN(pipeline, Color());
4217 auto theme = pipeline->GetTheme<TextTheme>();
4218 CHECK_NULL_RETURN(theme, Color());
4219 return theme->GetUrlPressColor();
4220 }
4221
GetUrlSpanColor()4222 Color TextPattern::GetUrlSpanColor()
4223 {
4224 auto host = GetHost();
4225 CHECK_NULL_RETURN(host, Color());
4226 auto pipeline = host->GetContext();
4227 CHECK_NULL_RETURN(pipeline, Color());
4228 auto theme = pipeline->GetTheme<TextTheme>();
4229 CHECK_NULL_RETURN(theme, Color());
4230
4231 auto eventHub = host->GetOrCreateEventHub<EventHub>();
4232 CHECK_NULL_RETURN(eventHub, Color());
4233
4234 if (eventHub && !eventHub->IsEnabled()) {
4235 return theme->GetUrlDisabledColor();
4236 } else {
4237 return theme->GetUrlDefaultColor();
4238 }
4239 }
4240
4241 // Deprecated: Use the TextSelectOverlay::ProcessOverlay() instead.
4242 // It is currently used by RichEditorPattern.
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)4243 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
4244 {
4245 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
4246 SelectHandleInfo firstHandleInfo;
4247 firstHandleInfo.paintRect = textSelector_.firstHandle;
4248 CheckHandles(firstHandleInfo);
4249
4250 SelectHandleInfo secondHandleInfo;
4251 secondHandleInfo.paintRect = textSelector_.secondHandle;
4252 CheckHandles(secondHandleInfo);
4253
4254 auto start = textSelector_.GetTextStart();
4255 auto end = textSelector_.GetTextEnd();
4256 selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
4257 if (selectInfo.isNewAvoid) {
4258 selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
4259 }
4260 selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
4261 selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
4262 } else {
4263 auto host = GetHost();
4264 CHECK_NULL_VOID(host);
4265 auto pipeline = host->GetContext();
4266 CHECK_NULL_VOID(pipeline);
4267 pipeline->AddOnAreaChangeNode(host->GetId());
4268 selectInfo.callerFrameNode = GetHost();
4269 selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
4270 if (!selectInfo.isUsingMouse) {
4271 CheckHandles(selectInfo.firstHandle);
4272 CheckHandles(selectInfo.secondHandle);
4273 }
4274 selectOverlayProxy_ =
4275 pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
4276 CHECK_NULL_VOID(selectOverlayProxy_);
4277 auto start = textSelector_.GetTextStart();
4278 auto end = textSelector_.GetTextEnd();
4279 selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
4280 }
4281 }
4282
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)4283 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
4284 {
4285 if (config.skipMeasure || dirty->SkipMeasureContent()) {
4286 return false;
4287 }
4288
4289 contentRect_ = dirty->GetGeometryNode()->GetContentRect();
4290 if (dataDetectorAdapter_) {
4291 dataDetectorAdapter_->aiSpanRects_.clear();
4292 }
4293
4294 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
4295 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
4296 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
4297 CHECK_NULL_RETURN(textLayoutAlgorithm, false);
4298 baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
4299 contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
4300 textStyle_ = textLayoutAlgorithm->GetTextStyle();
4301 ProcessOverlayAfterLayout();
4302 return true;
4303 }
4304
ProcessOverlayAfterLayout()4305 void TextPattern::ProcessOverlayAfterLayout()
4306 {
4307 if (selectOverlay_->SelectOverlayIsOn()) {
4308 CalculateHandleOffsetAndShowOverlay();
4309 selectOverlay_->UpdateAllHandlesOffset();
4310 selectOverlay_->UpdateViewPort();
4311 }
4312 }
4313
PreCreateLayoutWrapper()4314 void TextPattern::PreCreateLayoutWrapper()
4315 {
4316 auto host = GetContentHost();
4317 CHECK_NULL_VOID(host);
4318
4319 auto paintProperty = GetPaintProperty<PaintProperty>();
4320 CHECK_NULL_VOID(paintProperty);
4321 auto flag = paintProperty->GetPropertyChangeFlag();
4322 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4323 CHECK_NULL_VOID(textLayoutProperty);
4324 auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
4325 if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
4326 return;
4327 }
4328 auto beforeSpanSize = spans_.size();
4329 spans_.clear();
4330 childNodes_.clear();
4331
4332 // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
4333 const auto& children = host->GetChildren();
4334 if (children.empty()) {
4335 placeholderCount_ = 0;
4336 return;
4337 }
4338
4339 // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
4340 // tree.
4341 std::stack<SpanNodeInfo> nodes;
4342 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
4343 nodes.push({ .node = *iter });
4344 }
4345
4346 InitSpanItem(nodes);
4347 CHECK_NULL_VOID(beforeSpanSize != spans_.size());
4348 textLayoutProperty->OnPropertyChangeMeasure();
4349 }
4350
InitSpanItemEvent(bool & isSpanHasClick,bool & isSpanHasLongPress)4351 void TextPattern::InitSpanItemEvent(bool& isSpanHasClick, bool& isSpanHasLongPress)
4352 {
4353 auto host = GetHost();
4354 CHECK_NULL_VOID(host);
4355 auto gestureEventHub = host->GetOrCreateGestureEventHub();
4356 if (isSpanHasClick) {
4357 InitClickEvent(gestureEventHub);
4358 }
4359 if (isSpanHasLongPress) {
4360 InitLongPressEvent(gestureEventHub);
4361 }
4362 }
4363
InitSpanItem(std::stack<SpanNodeInfo> nodes)4364 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
4365 {
4366 auto host = GetHost();
4367 CHECK_NULL_VOID(host);
4368 // span perf can be optimize via create as requirement
4369 CHECK_NULL_VOID(GetDataDetectorAdapter());
4370 std::u16string textCache;
4371 std::u16string textForAICache;
4372 int32_t oldPlaceholderCount = placeholderCount_;
4373 placeholderCount_ = 0;
4374 if (!nodes.empty()) {
4375 textCache = textForDisplay_;
4376 textForAICache = dataDetectorAdapter_->textForAI_;
4377 textForDisplay_.clear();
4378 dataDetectorAdapter_->textForAI_.clear();
4379 }
4380
4381 bool isSpanHasClick = false;
4382 bool isSpanHasLongPress = false;
4383 CollectSpanNodes(nodes, isSpanHasClick, isSpanHasLongPress);
4384 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4385 CHECK_NULL_VOID(textLayoutProperty);
4386 if (childNodes_.empty()) {
4387 textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
4388 }
4389 if (oldPlaceholderCount != placeholderCount_) {
4390 CloseSelectOverlay();
4391 ResetSelection();
4392 }
4393
4394 if (textCache != textForDisplay_) {
4395 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
4396 UtfUtils::Str16DebugToStr8(textForDisplay_));
4397 OnAfterModifyDone();
4398 for (const auto& item : spans_) {
4399 if (item->inspectId.empty()) {
4400 continue;
4401 }
4402 Recorder::NodeDataCache::Get().PutString(host, item->inspectId, UtfUtils::Str16DebugToStr8(item->content));
4403 }
4404 ResetAfterTextChange();
4405 }
4406 InitSpanItemEvent(isSpanHasClick, isSpanHasLongPress);
4407 if (textForAICache != dataDetectorAdapter_->textForAI_) {
4408 dataDetectorAdapter_->aiDetectInitialized_ = false;
4409 }
4410 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
4411 ParseOriText(textLayoutProperty->GetContent().value_or(u""));
4412 if (!dataDetectorAdapter_->aiDetectInitialized_) {
4413 dataDetectorAdapter_->StartAITask();
4414 }
4415 }
4416 }
4417
ResetAfterTextChange()4418 void TextPattern::ResetAfterTextChange()
4419 {
4420 CloseSelectOverlay();
4421 ResetSelection();
4422 ResetOriginCaretPosition();
4423 }
4424
ParseOriText(const std::u16string & currentText)4425 void TextPattern::ParseOriText(const std::u16string& currentText)
4426 {
4427 CHECK_NULL_VOID(GetDataDetectorAdapter());
4428 auto entityJson = JsonUtil::ParseJsonString(UtfUtils::Str16DebugToStr8(currentText));
4429 bool entityIsJson = !entityJson->IsNull();
4430 TAG_LOGI(AceLogTag::ACE_TEXT, "text content is the json format: %{public}d", entityIsJson);
4431 if (entityIsJson && !entityJson->GetValue("bundleName")->IsNull() &&
4432 dataDetectorAdapter_->ParseOriText(entityJson, textForDisplay_)) {
4433 if (childNodes_.empty()) {
4434 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4435 CHECK_NULL_VOID(textLayoutProperty);
4436 textLayoutProperty->UpdateContent(textForDisplay_);
4437 }
4438 }
4439 }
4440
BeforeCreateLayoutWrapper()4441 void TextPattern::BeforeCreateLayoutWrapper()
4442 {
4443 auto host = GetHost();
4444 CHECK_NULL_VOID(host);
4445 CHECK_NULL_VOID(host->GetTag() != V2::SYMBOL_ETS_TAG);
4446 if (!isSpanStringMode_) {
4447 PreCreateLayoutWrapper();
4448 }
4449 selectOverlay_->MarkOverlayDirty();
4450 if (HasSpanOnHoverEvent()) {
4451 InitSpanMouseEvent();
4452 }
4453 }
4454
ResetTextEffectBeforeLayout(bool onlyReset)4455 bool TextPattern::ResetTextEffectBeforeLayout(bool onlyReset)
4456 {
4457 if (onlyReset && !textEffect_) {
4458 return true;
4459 }
4460 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4461 CHECK_NULL_RETURN(textLayoutProperty, true);
4462 if (textLayoutProperty->GetTextEffectStrategyValue(TextEffectStrategy::NONE) == TextEffectStrategy::NONE ||
4463 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE || !spans_.empty() ||
4464 isSpanStringMode_ || externalParagraph_ || IsSetObscured() || IsSensitiveEnable()) {
4465 ResetTextEffect();
4466 return true;
4467 }
4468 return false;
4469 }
4470
RelayoutResetOrUpdateTextEffect()4471 void TextPattern::RelayoutResetOrUpdateTextEffect()
4472 {
4473 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4474 CHECK_NULL_VOID(textLayoutProperty);
4475 ResetTextEffectBeforeLayout();
4476 // 重排版动效config切换
4477 if (textEffect_) {
4478 textEffect_->UpdateEffectConfig(textLayoutProperty->GetTextFlipDirectionValue(TextFlipDirection::DOWN),
4479 textLayoutProperty->GetTextFlipEnableBlurValue(false));
4480 }
4481 }
4482
ResetTextEffect()4483 void TextPattern::ResetTextEffect()
4484 {
4485 CHECK_NULL_VOID(textEffect_);
4486 textEffect_->StopEffect();
4487 std::vector<RefPtr<Paragraph>> paragraphs;
4488 textEffect_->RemoveTypography(paragraphs);
4489 textEffect_ = nullptr;
4490 }
4491
GetOrCreateTextEffect(const std::u16string & content,bool & needUpdateTypography)4492 RefPtr<TextEffect> TextPattern::GetOrCreateTextEffect(const std::u16string& content, bool& needUpdateTypography)
4493 {
4494 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4495 CHECK_NULL_RETURN(textLayoutProperty, nullptr);
4496 if (textLayoutProperty->GetTextEffectStrategyValue(TextEffectStrategy::NONE) == TextEffectStrategy::NONE) {
4497 ResetTextEffect();
4498 return nullptr;
4499 }
4500 if (ResetTextEffectBeforeLayout(false)) {
4501 return nullptr;
4502 }
4503 auto isNumber = RegularMatchNumbers(content);
4504 if (!isNumber) {
4505 ResetTextEffect();
4506 return nullptr;
4507 }
4508 if (!textEffect_) {
4509 auto host = GetHost();
4510 CHECK_NULL_RETURN(host, textEffect_);
4511 textEffect_ = TextEffect::CreateTextEffect();
4512 if (textSelector_.IsValid()) {
4513 CloseSelectOverlay();
4514 ResetSelection();
4515 }
4516 TAG_LOGI(AceLogTag::ACE_TEXT, "TextPattern::GetOrCreateTextEffect create textEffeect [id:%{public}d]",
4517 host->GetId());
4518 } else {
4519 // 上一次与此次的paragraph都满足翻牌要求需要重新更新textEffect中的paragraph
4520 needUpdateTypography = true;
4521 }
4522 if (textEffect_) {
4523 textEffect_->UpdateEffectConfig(textLayoutProperty->GetTextFlipDirectionValue(TextFlipDirection::DOWN),
4524 textLayoutProperty->GetTextFlipEnableBlurValue(false));
4525 }
4526 return textEffect_;
4527 }
4528
RegularMatchNumbers(const std::u16string & content)4529 bool TextPattern::RegularMatchNumbers(const std::u16string& content)
4530 {
4531 if (content.empty()) {
4532 return false;
4533 }
4534 for (const auto& c : content) {
4535 if (c < u'0' || c > u'9') {
4536 return false;
4537 }
4538 }
4539 return true;
4540 }
4541
SetSpanEventFlagValue(const RefPtr<UINode> & node,bool & isSpanHasClick,bool & isSpanHasLongPress)4542 void TextPattern::SetSpanEventFlagValue(
4543 const RefPtr<UINode>& node, bool& isSpanHasClick, bool& isSpanHasLongPress)
4544 {
4545 auto spanNode = DynamicCast<FrameNode>(node);
4546 CHECK_NULL_VOID(spanNode);
4547 auto focus_hub = spanNode->GetOrCreateFocusHub();
4548 if (focus_hub && focus_hub->GetOnClickCallback()) {
4549 isSpanHasClick = true;
4550 }
4551 if (focus_hub && focus_hub->GetOnLongPressCallback()) {
4552 isSpanHasLongPress = true;
4553 }
4554 }
4555
CollectSymbolSpanNodes(const RefPtr<SpanNode> & spanNode,const RefPtr<UINode> & node)4556 void TextPattern::CollectSymbolSpanNodes(const RefPtr<SpanNode>& spanNode, const RefPtr<UINode>& node)
4557 {
4558 CHECK_NULL_VOID(GetDataDetectorAdapter());
4559 spanNode->CleanSpanItemChildren();
4560 spanNode->MountToParagraph();
4561 textForDisplay_.append(u" ");
4562 dataDetectorAdapter_->textForAI_.append(SYMBOL_TRANS);
4563 childNodes_.push_back(node);
4564 }
4565
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick,bool & isSpanHasLongPress)4566 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick, bool& isSpanHasLongPress)
4567 {
4568 while (!nodes.empty()) {
4569 auto current = nodes.top();
4570 nodes.pop();
4571 if (!current.node) {
4572 continue;
4573 }
4574 UpdateContainerChildren(current.containerSpanNode, current.node);
4575 auto spanNode = DynamicCast<SpanNode>(current.node);
4576 auto tag = current.node->GetTag();
4577 if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG && spanNode->GetSpanItem()->GetSymbolUnicode() != 0) {
4578 CollectSymbolSpanNodes(spanNode, current.node);
4579 } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
4580 CollectTextSpanNodes(spanNode, isSpanHasClick, isSpanHasLongPress);
4581 childNodes_.push_back(current.node);
4582 } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
4583 placeholderCount_++;
4584 AddChildSpanItem(current.node);
4585 GetDataDetectorAdapter()->textForAI_.append(u"\n");
4586 auto imageNode = DynamicCast<FrameNode>(current.node);
4587 if (!imageNode) {
4588 continue;
4589 }
4590 SetSpanEventFlagValue(imageNode, isSpanHasClick, isSpanHasLongPress);
4591 childNodes_.push_back(current.node);
4592 } else if (tag == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
4593 placeholderCount_++;
4594 AddChildSpanItem(current.node);
4595 GetDataDetectorAdapter()->textForAI_.append(u"\n");
4596 childNodes_.emplace_back(current.node);
4597 auto customNode = DynamicCast<FrameNode>(current.node);
4598 if (!customNode) {
4599 continue;
4600 }
4601 SetSpanEventFlagValue(customNode, isSpanHasClick, isSpanHasLongPress);
4602 }
4603 if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
4604 continue;
4605 }
4606 const auto& nextChildren = current.node->GetChildren();
4607 if (nextChildren.empty()) {
4608 continue;
4609 }
4610 auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
4611 for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
4612 nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
4613 }
4614 }
4615 }
4616
CollectTextSpanNodes(const RefPtr<SpanNode> & spanNode,bool & isSpanHasClick,bool & isSpanHasLongPress)4617 void TextPattern::CollectTextSpanNodes(const RefPtr<SpanNode>& spanNode, bool& isSpanHasClick, bool& isSpanHasLongPress)
4618 {
4619 spanNode->CleanSpanItemChildren();
4620 spanNode->MountToParagraph();
4621 textForDisplay_.append(spanNode->GetSpanItem()->content);
4622 GetDataDetectorAdapter()->textForAI_.append(spanNode->GetSpanItem()->content);
4623 if (spanNode->GetSpanItem()->onClick) {
4624 isSpanHasClick = true;
4625 }
4626 if (spanNode->GetSpanItem()->onLongPress) {
4627 isSpanHasLongPress = true;
4628 }
4629 }
4630
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)4631 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
4632 {
4633 CHECK_NULL_VOID(child);
4634 auto parent = DynamicCast<ContainerSpanNode>(parentNode);
4635 CHECK_NULL_VOID(parent);
4636 auto baseSpan = DynamicCast<BaseSpan>(child);
4637 if (baseSpan) {
4638 if (baseSpan->HasTextBackgroundStyle()) {
4639 return;
4640 }
4641 baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
4642 return;
4643 }
4644 if (child->GetTag() == V2::IMAGE_ETS_TAG) {
4645 auto imageNode = DynamicCast<FrameNode>(child);
4646 CHECK_NULL_VOID(imageNode);
4647 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
4648 CHECK_NULL_VOID(imageLayoutProperty);
4649 if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
4650 return;
4651 }
4652 if (parent->GetTextBackgroundStyle().has_value()) {
4653 imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
4654 }
4655 }
4656 }
4657
GetGlobalOffset(Offset & offset)4658 void TextPattern::GetGlobalOffset(Offset& offset)
4659 {
4660 auto host = GetHost();
4661 CHECK_NULL_VOID(host);
4662 auto pipeline = host->GetContext();
4663 CHECK_NULL_VOID(pipeline);
4664 auto rootOffset = pipeline->GetRootRect().GetOffset();
4665 auto globalOffset = host->GetPaintRectOffsetNG(false, true) - rootOffset;
4666 offset = Offset(globalOffset.GetX(), globalOffset.GetY());
4667 }
4668
OnVisibleChange(bool isVisible)4669 void TextPattern::OnVisibleChange(bool isVisible)
4670 {
4671 if (!isVisible) {
4672 if (textSelector_.IsValid()) {
4673 CloseSelectOverlay();
4674 ResetSelection();
4675 }
4676 if (textDetectEnable_) {
4677 GetDataDetectorAdapter()->aiDetectDelayTask_.Cancel();
4678 }
4679 PauseSymbolAnimation();
4680 } else {
4681 if (CanStartAITask()) {
4682 GetDataDetectorAdapter()->StartAITask();
4683 }
4684 ResumeSymbolAnimation();
4685 }
4686 }
4687
PauseSymbolAnimation()4688 void TextPattern::PauseSymbolAnimation()
4689 {
4690 auto host = GetHost();
4691 CHECK_NULL_VOID(host);
4692 if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
4693 return;
4694 }
4695 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4696 CHECK_NULL_VOID(layoutProperty);
4697 if (!layoutProperty->GetIsLoopAnimation()) {
4698 return;
4699 }
4700 auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
4701 if (!symbolEffectOptions.GetIsTxtActive()) {
4702 return;
4703 }
4704 symbolEffectOptions.SetIsTxtActive(false);
4705 layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
4706 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4707 }
4708
ResumeSymbolAnimation()4709 void TextPattern::ResumeSymbolAnimation()
4710 {
4711 auto host = GetHost();
4712 CHECK_NULL_VOID(host);
4713 if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
4714 return;
4715 }
4716 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4717 CHECK_NULL_VOID(layoutProperty);
4718 if (!layoutProperty->GetIsLoopAnimation()) {
4719 return;
4720 }
4721 auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
4722 if (symbolEffectOptions.GetIsTxtActive()) {
4723 return;
4724 }
4725 symbolEffectOptions.SetIsTxtActive(true);
4726 layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
4727 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4728 }
4729
InitSurfaceChangedCallback()4730 void TextPattern::InitSurfaceChangedCallback()
4731 {
4732 auto host = GetHost();
4733 CHECK_NULL_VOID(host);
4734 auto pipeline = host->GetContext();
4735 CHECK_NULL_VOID(pipeline);
4736 if (!HasSurfaceChangedCallback()) {
4737 auto callbackId = pipeline->RegisterSurfaceChangedCallback(
4738 [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
4739 WindowSizeChangeReason type) {
4740 auto pattern = weak.Upgrade();
4741 if (pattern) {
4742 pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
4743 }
4744 });
4745 UpdateSurfaceChangedCallbackId(callbackId);
4746 }
4747 }
4748
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight,WindowSizeChangeReason type)4749 void TextPattern::HandleSurfaceChanged(
4750 int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
4751 {
4752 if (newWidth == prevWidth && newHeight == prevHeight) {
4753 return;
4754 }
4755 if (type != WindowSizeChangeReason::DRAG) {
4756 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4757 CHECK_NULL_VOID(textLayoutProperty);
4758 textLayoutProperty->OnPropertyChangeMeasure();
4759 }
4760 CHECK_NULL_VOID(selectOverlay_->SelectOverlayIsOn());
4761 if (selectOverlay_->IsShowMouseMenu()) {
4762 CloseSelectOverlay();
4763 } else {
4764 auto host = GetHost();
4765 CHECK_NULL_VOID(host);
4766 auto context = host->GetContext();
4767 if (context) {
4768 context->AddAfterLayoutTask([weak = WeakClaim(this)]() {
4769 auto pattern = weak.Upgrade();
4770 CHECK_NULL_VOID(pattern);
4771 pattern->CalculateHandleOffsetAndShowOverlay();
4772 pattern->ShowSelectOverlay({ .menuIsShow = false });
4773 });
4774 }
4775 }
4776 }
4777
InitSurfacePositionChangedCallback()4778 void TextPattern::InitSurfacePositionChangedCallback()
4779 {
4780 auto host = GetHost();
4781 CHECK_NULL_VOID(host);
4782 auto pipeline = host->GetContext();
4783 CHECK_NULL_VOID(pipeline);
4784 if (!HasSurfacePositionChangedCallback()) {
4785 auto callbackId =
4786 pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
4787 auto pattern = weak.Upgrade();
4788 if (pattern) {
4789 pattern->HandleSurfacePositionChanged(posX, posY);
4790 }
4791 });
4792 UpdateSurfacePositionChangedCallbackId(callbackId);
4793 }
4794 }
4795
AddChildSpanItem(const RefPtr<UINode> & child)4796 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
4797 {
4798 CHECK_NULL_VOID(child);
4799 auto chidNode = DynamicCast<FrameNode>(child);
4800 if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
4801 return;
4802 }
4803 if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
4804 auto spanNode = DynamicCast<SpanNode>(child);
4805 if (spanNode) {
4806 spans_.emplace_back(spanNode->GetSpanItem());
4807 }
4808 } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
4809 AddImageToSpanItem(child);
4810 } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
4811 auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
4812 if (placeholderSpanNode) {
4813 auto placeholderSpan = placeholderSpanNode->GetSpanItem();
4814 placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
4815 spans_.emplace_back(placeholderSpan);
4816 }
4817 } else if (child->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
4818 auto customSpanNode = DynamicCast<CustomSpanNode>(child);
4819 if (customSpanNode) {
4820 auto customSpan = customSpanNode->GetSpanItem();
4821 customSpan->placeholderSpanNodeId = customSpanNode->GetId();
4822 auto focus_hub = chidNode->GetOrCreateFocusHub();
4823 CHECK_NULL_VOID(focus_hub);
4824 SetSpanItemEvent(customSpan, focus_hub);
4825 spans_.emplace_back(customSpan);
4826 }
4827 }
4828 }
4829
SetSpanItemEvent(const RefPtr<SpanItem> & spanItem,RefPtr<FocusHub> & focusHub)4830 void TextPattern::SetSpanItemEvent(const RefPtr<SpanItem>& spanItem, RefPtr<FocusHub>& focusHub)
4831 {
4832 CHECK_NULL_VOID(focusHub);
4833 auto clickCall = focusHub->GetOnClickCallback();
4834 if (clickCall) {
4835 spanItem->SetOnClickEvent(std::move(clickCall));
4836 }
4837 auto longPressCallback = focusHub->GetOnLongPressCallback();
4838 if (longPressCallback) {
4839 spanItem->SetLongPressEvent(std::move(longPressCallback));
4840 }
4841 }
4842
AddImageToSpanItem(const RefPtr<UINode> & child)4843 void TextPattern::AddImageToSpanItem(const RefPtr<UINode>& child)
4844 {
4845 auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
4846 if (imageSpanNode) {
4847 auto host = GetHost();
4848 CHECK_NULL_VOID(host);
4849 auto imageSpanItem = imageSpanNode->GetSpanItem();
4850 if (host->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
4851 auto focus_hub = imageSpanNode->GetOrCreateFocusHub();
4852 SetSpanItemEvent(imageSpanItem, focus_hub);
4853 auto gesture = imageSpanNode->GetOrCreateGestureEventHub();
4854 CHECK_NULL_VOID(gesture);
4855 gesture->SetHitTestMode(HitTestMode::HTMNONE);
4856 }
4857 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageSpanNode);
4858 spans_.emplace_back(imageSpanItem);
4859 spans_.back()->nodeId_ = imageSpanNode->GetId();
4860 return;
4861 }
4862 auto imageNode = DynamicCast<FrameNode>(child);
4863 if (imageNode) {
4864 auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
4865 imageSpanItem->nodeId_ = imageNode->GetId();
4866 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
4867 auto focus_hub = imageNode->GetOrCreateFocusHub();
4868 SetSpanItemEvent(imageSpanItem, focus_hub);
4869 spans_.emplace_back(imageSpanItem);
4870 auto gesture = imageNode->GetOrCreateGestureEventHub();
4871 CHECK_NULL_VOID(gesture);
4872 gesture->SetHitTestMode(HitTestMode::HTMNONE);
4873 return;
4874 }
4875 }
4876
DumpSimplifyInfo(std::shared_ptr<JsonValue> & json)4877 void TextPattern::DumpSimplifyInfo(std::shared_ptr<JsonValue>& json)
4878 {
4879 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4880 CHECK_NULL_VOID(textLayoutProp);
4881 if (IsSetObscured()) {
4882 json->Put("content", "");
4883 return;
4884 }
4885 auto textValue = UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u""));
4886 if (!textValue.empty()) {
4887 json->Put("content", textValue.c_str());
4888 } else {
4889 CHECK_NULL_VOID(pManager_);
4890 auto paragraphs = pManager_->GetParagraphs();
4891 if (paragraphs.empty()) {
4892 return;
4893 }
4894
4895 std::string text;
4896 for (auto&& info : paragraphs) {
4897 auto paragraph = info.paragraph;
4898 if (paragraph) {
4899 text += StringUtils::Str16ToStr8(paragraph->GetParagraphText());
4900 }
4901 }
4902 json->Put("content", text.c_str());
4903 }
4904 }
4905
DumpAdvanceInfo()4906 void TextPattern::DumpAdvanceInfo()
4907 {
4908 DumpLog::GetInstance().AddDesc(std::string("-----DumpAdvanceInfo-----"));
4909 DumpLog::GetInstance().AddDesc(
4910 std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
4911 DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
4912 }
4913
DumpInfo()4914 void TextPattern::DumpInfo()
4915 {
4916 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4917 CHECK_NULL_VOID(textLayoutProp);
4918 auto& dumpLog = DumpLog::GetInstance();
4919 auto nowTime = GetSystemTimestamp();
4920 dumpLog.AddDesc(std::string("frameRecord: ").append(frameRecord_));
4921 dumpLog.AddDesc(std::string("time: ").append(std::to_string(nowTime)));
4922 if (!IsSetObscured() && !IsSensitiveEnable()) {
4923 dumpLog.AddDesc(std::string("Content: ").append(
4924 UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" "))));
4925 }
4926 dumpLog.AddDesc(std::string("isSpanStringMode: ").append(std::to_string(isSpanStringMode_)));
4927 dumpLog.AddDesc(std::string("externalParagraph: ").append(std::to_string(externalParagraph_.has_value())));
4928 DumpTextStyleInfo();
4929 if (contentMod_) {
4930 contentMod_->ContentModifierDump();
4931 }
4932 dumpLog.AddDesc(
4933 std::string("HeightAdaptivePolicy: ")
4934 .append(V2::ConvertWrapTextHeightAdaptivePolicyToString(
4935 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))));
4936 if (pManager_) {
4937 auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
4938 dumpLog.AddDesc(std::string("Paragraphs num: ").append(std::to_string(num)));
4939 dumpLog.AddDesc(std::string("PaintInfo: ").append(paintInfo_));
4940 }
4941 DumpScaleInfo();
4942 DumpTextEngineInfo();
4943 if (SystemProperties::GetDebugEnabled()) {
4944 DumpAdvanceInfo();
4945 }
4946 DumpSpanItem();
4947 }
4948
DumpSpanItem()4949 void TextPattern::DumpSpanItem()
4950 {
4951 CHECK_NULL_VOID(isSpanStringMode_);
4952 auto& dumpLog = DumpLog::GetInstance();
4953 dumpLog.AddDesc(std::string("-----SpanDumpInfo-----"));
4954 for (const auto& item : spans_) {
4955 if (!item) {
4956 continue;
4957 }
4958 item->SpanDumpInfo();
4959 }
4960 }
4961
DumpTextStyleInfo()4962 void TextPattern::DumpTextStyleInfo()
4963 {
4964 auto& dumpLog = DumpLog::GetInstance();
4965 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4966 CHECK_NULL_VOID(textLayoutProp);
4967 auto host = GetHost();
4968 CHECK_NULL_VOID(host);
4969 auto renderContext = host->GetRenderContext();
4970 CHECK_NULL_VOID(renderContext);
4971 dumpLog.AddDesc(
4972 std::string("FontColor: ")
4973 .append((textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString())
4974 .append(" pro: ")
4975 .append(
4976 textLayoutProp->HasTextColor() ? textLayoutProp->GetTextColorValue(Color::BLACK).ColorToString() : "Na")
4977 .append(" ForegroundColor: ")
4978 .append(
4979 renderContext->HasForegroundColor() ? renderContext->GetForegroundColorValue().ColorToString() : "Na"));
4980 if (renderContext->HasForegroundColorStrategy()) {
4981 auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
4982 DumpLog::GetInstance().AddDesc(std::string("ForegroundColorStrategy: ").append(std::to_string(strategy)));
4983 }
4984 dumpLog.AddDesc(
4985 std::string("FontSize: ")
4986 .append((textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
4987 .ToString())
4988 .append(" pro: ")
4989 .append(textLayoutProp->HasFontSize()
4990 ? textLayoutProp->GetFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4991 : "Na")
4992 .append(" actual: ")
4993 .append(std::to_string(textStyle_.has_value() ? textStyle_->GetFontSizeActual() : 0.0f)));
4994 if (textStyle_.has_value()) {
4995 dumpLog.AddDesc(
4996 std::string("MaxFontSize: ")
4997 .append(textStyle_->GetAdaptMaxFontSize().ToString())
4998 .append(" pro: ")
4999 .append(textLayoutProp->HasAdaptMaxFontSize()
5000 ? textLayoutProp->GetAdaptMaxFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5001 : "Na")
5002 .append(std::string(" MinFontSize: "))
5003 .append(textStyle_->GetAdaptMinFontSize().ToString())
5004 .append(" pro: ")
5005 .append(textLayoutProp->HasAdaptMinFontSize()
5006 ? textLayoutProp->GetAdaptMinFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5007 : "Na"));
5008 }
5009 DumpTextStyleInfo2();
5010 DumpTextStyleInfo3();
5011 DumpTextStyleInfo4();
5012 }
5013
DumpTextStyleInfo2()5014 void TextPattern::DumpTextStyleInfo2()
5015 {
5016 auto& dumpLog = DumpLog::GetInstance();
5017 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5018 CHECK_NULL_VOID(textLayoutProp);
5019 if (textStyle_.has_value()) {
5020 dumpLog.AddDesc(std::string("FontWeight: ")
5021 .append(StringUtils::ToString(textStyle_->GetFontWeight()))
5022 .append(" pro: ")
5023 .append(textLayoutProp->HasFontWeight()
5024 ? StringUtils::ToString(textLayoutProp->GetFontWeightValue(FontWeight::NORMAL))
5025 : "Na")
5026 .append(" EnableVariableFontWeight: ")
5027 .append(std::to_string(textStyle_->GetEnableVariableFontWeight()))
5028 .append(" pro: ")
5029 .append(textLayoutProp->HasEnableVariableFontWeight()
5030 ? std::to_string(textLayoutProp->GetEnableVariableFontWeightValue(false))
5031 : "Na")
5032 .append(" VariableFontWeight: ")
5033 .append(std::to_string(textStyle_->GetVariableFontWeight()))
5034 .append(" pro: ")
5035 .append(textLayoutProp->HasVariableFontWeight()
5036 ? std::to_string(textLayoutProp->GetVariableFontWeightValue(0))
5037 : "Na")
5038 .append(std::string(" FontStyle: "))
5039 .append(StringUtils::ToString(textStyle_->GetFontStyle())));
5040 auto hasIsOnlyBetweenLines = (textLayoutProp->GetIsOnlyBetweenLinesValue(false)) ? "true" : "false";
5041 dumpLog.AddDesc(
5042 std::string("LineHeight: ")
5043 .append(textStyle_->GetLineHeight().ToString())
5044 .append(" pro: ")
5045 .append(textLayoutProp->HasLineHeight()
5046 ? textLayoutProp->GetLineHeightValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5047 : "Na")
5048 .append(std::string(" LineSpacing: "))
5049 .append(textStyle_->GetLineSpacing().ToString())
5050 .append(" pro: ")
5051 .append(textLayoutProp->HasLineSpacing()
5052 ? textLayoutProp->GetLineSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5053 : "Na")
5054 .append(" isOnlyBetweenLines: ")
5055 .append((textStyle_->GetIsOnlyBetweenLines()) ? "true" : "false")
5056 .append(" pro: ")
5057 .append(textLayoutProp->HasIsOnlyBetweenLines() ? hasIsOnlyBetweenLines : "Na"));
5058
5059 }
5060 }
5061
DumpTextStyleInfo3()5062 void TextPattern::DumpTextStyleInfo3()
5063 {
5064 auto& dumpLog = DumpLog::GetInstance();
5065 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5066 CHECK_NULL_VOID(textLayoutProp);
5067 if (textStyle_.has_value()) {
5068 dumpLog.AddDesc(
5069 std::string("maxLines: ")
5070 .append(std::to_string(textStyle_->GetMaxLines()))
5071 .append(" pro: ")
5072 .append(
5073 textLayoutProp->HasMaxLines() ? std::to_string(textLayoutProp->GetMaxLinesValue(UINT32_MAX)) : "Na")
5074 .append(std::string(" BaselineOffset: "))
5075 .append(textStyle_->GetBaselineOffset().ToString())
5076 .append(" pro: ")
5077 .append(textLayoutProp->HasBaselineOffset()
5078 ? textLayoutProp->GetBaselineOffsetValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5079 : "Na"));
5080 dumpLog.AddDesc(
5081 std::string("TextIndent: ")
5082 .append(textStyle_->GetTextIndent().ToString())
5083 .append(" pro: ")
5084 .append(textLayoutProp->HasTextIndent()
5085 ? textLayoutProp->GetTextIndentValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5086 : "Na"));
5087 dumpLog.AddDesc(
5088 std::string("fontFamily: ")
5089 .append(GetFontFamilyInJson(textStyle_->GetFontFamilies()))
5090 .append(" pro: ")
5091 .append(textLayoutProp->HasFontFamily() ? GetFontFamilyInJson(textLayoutProp->GetFontFamily().value())
5092 : "Na"));
5093 dumpLog.AddDesc(
5094 std::string("LetterSpacing: ")
5095 .append(textStyle_->GetLetterSpacing().ToString())
5096 .append(" pro: ")
5097 .append(textLayoutProp->HasLetterSpacing()
5098 ? textLayoutProp->GetLetterSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
5099 : "Na"));
5100 dumpLog.AddDesc(
5101 std::string("TextOverflow: ")
5102 .append(StringUtils::ToString(textStyle_->GetTextOverflow()))
5103 .append(" pro: ")
5104 .append(textLayoutProp->HasTextOverflow()
5105 ? StringUtils::ToString(textLayoutProp->GetTextOverflowValue(TextOverflow::NONE))
5106 : "Na"));
5107 dumpLog.AddDesc(std::string("TextAlign: ")
5108 .append(StringUtils::ToString(textStyle_->GetTextAlign()))
5109 .append(" pro: ")
5110 .append(textLayoutProp->HasTextAlign()
5111 ? StringUtils::ToString(textLayoutProp->GetTextAlignValue(TextAlign::START))
5112 : "Na"));
5113 }
5114 }
5115
DumpTextStyleInfo4()5116 void TextPattern::DumpTextStyleInfo4()
5117 {
5118 auto& dumpLog = DumpLog::GetInstance();
5119 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5120 CHECK_NULL_VOID(textLayoutProp);
5121 if (textStyle_.has_value()) {
5122 dumpLog.AddDesc(std::string("WordBreak: ")
5123 .append(StringUtils::ToString(textStyle_->GetWordBreak()))
5124 .append(std::string(" TextCase: "))
5125 .append(StringUtils::ToString(textStyle_->GetTextCase()))
5126 .append(std::string(" EllipsisMode: "))
5127 .append(StringUtils::ToString(textStyle_->GetEllipsisMode())));
5128 dumpLog.AddDesc(std::string("LineBreakStrategy: ")
5129 .append(GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy()))
5130 .append(" pro: ")
5131 .append(textLayoutProp->HasLineBreakStrategy()
5132 ? GetLineBreakStrategyInJson(
5133 textLayoutProp->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY))
5134 : "Na"));
5135 dumpLog.AddDesc(std::string("SymbolColorList: ")
5136 .append(StringUtils::SymbolColorListToString(textStyle_->GetSymbolColorList())));
5137 dumpLog.AddDesc(std::string("CopyOption: ")
5138 .append(StringUtils::ToString(copyOption_))
5139 .append(" pro: ")
5140 .append(textLayoutProp->HasCopyOption() ? textLayoutProp->GetCopyOptionString() : "Na")
5141 .append(" SelectableMode:")
5142 .append(StringUtils::ToString(textLayoutProp->GetTextSelectableModeValue(
5143 TextSelectableMode::SELECTABLE_UNFOCUSABLE))));
5144 dumpLog.AddDesc(
5145 std::string("Decoration: ")
5146 .append(StringUtils::ToString(textStyle_->GetTextDecorationStyle()))
5147 .append(" ")
5148 .append(StringUtils::ToString(textStyle_->GetTextDecorationFirst()))
5149 .append(" ")
5150 .append(textStyle_->GetTextDecorationColor().ColorToString())
5151 .append(" self: ")
5152 .append(
5153 textLayoutProp->HasTextDecorationStyle()
5154 ? StringUtils::ToString(textLayoutProp->GetTextDecorationStyleValue(TextDecorationStyle::SOLID))
5155 : "Na")
5156 .append(" ")
5157 .append(textLayoutProp->HasTextDecoration()
5158 ? StringUtils::ToString(textLayoutProp->GetTextDecorationFirst())
5159 : "Na")
5160 .append(" ")
5161 .append(textLayoutProp->HasTextDecorationColor()
5162 ? textLayoutProp->GetTextDecorationColorValue(Color::BLACK).ColorToString()
5163 : "Na"));
5164 }
5165 }
5166
DumpScaleInfo()5167 void TextPattern::DumpScaleInfo()
5168 {
5169 auto& dumpLog = DumpLog::GetInstance();
5170 dumpLog.AddDesc(std::string("-----DumpScaleInfo-----"));
5171 auto host = GetHost();
5172 CHECK_NULL_VOID(host);
5173 auto pipeline = host->GetContext();
5174 CHECK_NULL_VOID(pipeline);
5175 auto fontScale = pipeline->GetFontScale();
5176 auto fontWeightScale = pipeline->GetFontWeightScale();
5177 auto followSystem = pipeline->IsFollowSystem();
5178 float maxFontScale = pipeline->GetMaxAppFontScale();
5179 auto halfLeading = pipeline->GetHalfLeading();
5180 dumpLog.AddDesc(std::string("fontScale: ").append(std::to_string(fontScale))
5181 .append(std::string(", fontWeightScale: ")).append(std::to_string(fontWeightScale))
5182 .append(std::string(", IsFollowSystem: ")).append(std::to_string(followSystem))
5183 .append(std::string(", maxFontScale: ")).append(std::to_string(maxFontScale))
5184 .append(std::string(", ConfigHalfLeading: ")).append(std::to_string(halfLeading)));
5185 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5186 CHECK_NULL_VOID(textLayoutProp);
5187 auto minFontScale = textLayoutProp->GetMinFontScale().value_or(0.0f);
5188 auto maxfontScale = textLayoutProp->GetMaxFontScale().value_or(static_cast<float>(INT32_MAX));
5189 dumpLog.AddDesc(std::string("minFontScale: ").append(std::to_string(minFontScale))
5190 .append(std::string(", maxFontScale: ")).append(std::to_string(maxfontScale)));
5191 auto flag = textLayoutProp->HasHalfLeading();
5192 dumpLog.AddDesc(
5193 std::string("HalfLeading: ").append(flag ? std::to_string(textLayoutProp->GetHalfLeadingValue(false)) : "NA"));
5194 }
5195
DumpTextEngineInfo()5196 void TextPattern::DumpTextEngineInfo()
5197 {
5198 auto& dumpLog = DumpLog::GetInstance();
5199 dumpLog.AddDesc(std::string("-----TextEngine paragraphs_ info-----"));
5200 dumpLog.AddDesc(std::string("contentRect :").append(contentRect_.ToString()));
5201 if (pManager_) {
5202 dumpLog.AddDesc(std::string("from TextEngine paragraphs_ info :").append(pManager_->GetDumpInfo()));
5203 auto paragraphs = pManager_->GetParagraphs();
5204 if (paragraphs.empty()) {
5205 dumpLog.AddDesc(std::string("paragraphs is empty!"));
5206 return;
5207 }
5208 dumpLog.AddDesc(std::string("DidExceedMaxLines:").append(std::to_string(pManager_->DidExceedMaxLines()))
5209 .append(" DidExceedMaxLinesInner:")
5210 .append(std::to_string(pManager_->DidExceedMaxLinesInner())));
5211 dumpLog.AddDesc(std::string("GetTextWidth:")
5212 .append(std::to_string(pManager_->GetTextWidth()))
5213 .append(" GetHeight:")
5214 .append(std::to_string(pManager_->GetHeight()))
5215 .append(" GetMaxWidth:")
5216 .append(std::to_string(pManager_->GetMaxWidth()))
5217 .append(" GetMaxIntrinsicWidth:")
5218 .append(std::to_string(pManager_->GetMaxIntrinsicWidth())));
5219 dumpLog.AddDesc(std::string("GetLineCount:")
5220 .append(std::to_string(pManager_->GetLineCount()))
5221 .append(" GetLongestLine:")
5222 .append(std::to_string(pManager_->GetLongestLine()))
5223 .append(std::to_string(pManager_->GetLongestLineWithIndent())));
5224 }
5225 dumpLog.AddDesc(std::string("spans size :").append(std::to_string(spans_.size())));
5226 if (!IsSetObscured() && !IsSensitiveEnable()) {
5227 DumpParagraphsInfo();
5228 }
5229 }
5230
DumpParagraphsInfo()5231 void TextPattern::DumpParagraphsInfo()
5232 {
5233 CHECK_NULL_VOID(pManager_);
5234 auto& dumpLog = DumpLog::GetInstance();
5235 auto paragraphs = pManager_->GetParagraphs();
5236 if (paragraphs.empty()) {
5237 dumpLog.AddDesc(std::string("paragraphs is empty!"));
5238 return;
5239 }
5240 dumpLog.AddDesc(std::string("paragraphs size:").append(std::to_string(paragraphs.size())));
5241 for (auto&& info : paragraphs) {
5242 auto paragraph = info.paragraph;
5243 if (paragraph) {
5244 auto text = StringUtils::Str16ToStr8(paragraph->GetParagraphText());
5245 auto paraStyle = paragraph->GetParagraphStyle();
5246 auto direction = V2::ConvertTextDirectionToString(paraStyle.direction);
5247 dumpLog.AddDesc(std::string("paragraph: ")
5248 .append(text)
5249 .append("; direction:")
5250 .append(direction)
5251 .append(";fontlocate:")
5252 .append(paraStyle.fontLocale)
5253 .append(";fontSize:")
5254 .append(std::to_string(paraStyle.fontSize))
5255 .append(";maxLines:")
5256 .append(std::to_string(paraStyle.maxLines))
5257 .append(";align:")
5258 .append(StringUtils::ToString(paraStyle.align))
5259 .append(";isEndAddParagraphSpacing:")
5260 .append(std::to_string(paraStyle.isEndAddParagraphSpacing)));
5261 }
5262 }
5263 }
5264
SetAccessibilityAction()5265 void TextPattern::SetAccessibilityAction()
5266 {
5267 auto host = GetHost();
5268 CHECK_NULL_VOID(host);
5269 auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
5270 CHECK_NULL_VOID(textAccessibilityProperty);
5271 textAccessibilityProperty->SetActionSetSelection(
5272 [weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
5273 const auto& pattern = weakPtr.Upgrade();
5274 CHECK_NULL_VOID(pattern);
5275 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
5276 CHECK_NULL_VOID(textLayoutProperty);
5277 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
5278 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
5279 mode != TextSelectableMode::UNSELECTABLE) {
5280 pattern->ActSetSelection(start, end);
5281 }
5282 });
5283
5284 textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
5285 const auto& pattern = weakPtr.Upgrade();
5286 CHECK_NULL_VOID(pattern);
5287 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
5288 CHECK_NULL_VOID(textLayoutProperty);
5289 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
5290 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
5291 mode != TextSelectableMode::UNSELECTABLE) {
5292 pattern->CloseSelectOverlay(true);
5293 pattern->ResetSelection();
5294 }
5295 });
5296
5297 textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
5298 const auto& pattern = weakPtr.Upgrade();
5299 CHECK_NULL_VOID(pattern);
5300 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
5301 CHECK_NULL_VOID(textLayoutProperty);
5302 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
5303 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
5304 mode != TextSelectableMode::UNSELECTABLE) {
5305 pattern->HandleOnCopy();
5306 pattern->CloseSelectOverlay(true);
5307 pattern->ResetSelection();
5308 }
5309 });
5310 }
5311
OnColorConfigurationUpdate()5312 void TextPattern::OnColorConfigurationUpdate()
5313 {
5314 auto host = GetHost();
5315 CHECK_NULL_VOID(host);
5316 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5317 CHECK_NULL_VOID(textLayoutProperty);
5318 if (!textLayoutProperty->HasTextColor()) {
5319 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
5320 }
5321 if (GetOrCreateMagnifier()) {
5322 magnifierController_->SetColorModeChange(true);
5323 }
5324 if (isSpanStringMode_) {
5325 for (const auto& item : spans_) {
5326 if (!item) {
5327 continue;
5328 }
5329 item->fontStyle->UpdateColorByResourceId();
5330 if (item->backgroundStyle) {
5331 item->backgroundStyle->UpdateColorByResourceId();
5332 }
5333 }
5334 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
5335 }
5336 ACE_TEXT_SCOPED_TRACE("OnColorConfigurationUpdate[Text][self:%d]", host->GetId());
5337 }
5338
OnThemeScopeUpdate(int32_t themeScopeId)5339 bool TextPattern::OnThemeScopeUpdate(int32_t themeScopeId)
5340 {
5341 auto host = GetHost();
5342 CHECK_NULL_RETURN(host, false);
5343 auto contex = host->GetRenderContext();
5344 CHECK_NULL_RETURN(contex, false);
5345 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5346 CHECK_NULL_RETURN(textLayoutProperty, false);
5347
5348 if (!textLayoutProperty->HasTextColor() && !contex->HasForegroundColor()) {
5349 auto pipeline = host->GetContext();
5350 CHECK_NULL_RETURN(pipeline, false);
5351 auto textTheme = pipeline->GetTheme<TextTheme>(themeScopeId);
5352 CHECK_NULL_RETURN(textTheme, false);
5353 UpdateFontColor(textTheme->GetTextStyle().GetTextColor());
5354 }
5355 return false;
5356 }
5357
5358 // return: whether the offset is valid, return false if invalid
GetOriginCaretPosition(OffsetF & offset) const5359 bool TextPattern::GetOriginCaretPosition(OffsetF& offset) const
5360 {
5361 if (!originCaretPosition_.NonNegative()) {
5362 return false;
5363 }
5364 offset = originCaretPosition_;
5365 return true;
5366 }
5367
ResetOriginCaretPosition()5368 void TextPattern::ResetOriginCaretPosition()
5369 {
5370 originCaretPosition_ = DEFAULT_NEGATIVE_CARET_OFFSET;
5371 }
5372
5373 // Record current caret position if originCaretPosition_ is invalid
5374 // return: whether the current offset is recorded and valid
RecordOriginCaretPosition(const OffsetF & offset)5375 bool TextPattern::RecordOriginCaretPosition(const OffsetF& offset)
5376 {
5377 if (originCaretPosition_.NonNegative() || !offset.NonNegative()) {
5378 return false;
5379 }
5380 originCaretPosition_ = offset;
5381 return true;
5382 }
5383
ResetCustomFontColor()5384 void TextPattern::ResetCustomFontColor()
5385 {
5386 auto host = GetHost();
5387 CHECK_NULL_VOID(host);
5388 auto pipeline = host->GetContext();
5389 CHECK_NULL_VOID(pipeline);
5390 auto textTheme = pipeline->GetTheme<TextTheme>(host->GetThemeScopeId());
5391 CHECK_NULL_VOID(textTheme);
5392 auto color = textTheme->GetTextStyle().GetTextColor();
5393 UpdateFontColor(color);
5394 }
5395
GetDragUpperLeftCoordinates()5396 OffsetF TextPattern::GetDragUpperLeftCoordinates()
5397 {
5398 auto dragBoxes = GetTextBoxes();
5399 if (dragBoxes.empty()) {
5400 return { 0.0f, 0.0f };
5401 }
5402 auto startY = dragBoxes.front().Top();
5403 auto startX = dragBoxes.front().Left();
5404
5405 auto endY = dragBoxes.back().Top();
5406 OffsetF offset;
5407 if (NearEqual(startY, endY)) {
5408 offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
5409 } else {
5410 offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
5411 }
5412
5413 return GetParentGlobalOffset() + offset;
5414 }
5415
UpdateRectForSymbolShadow(RectF & rect,float offsetX,float offsetY,float blurRadius) const5416 void TextPattern::UpdateRectForSymbolShadow(RectF& rect, float offsetX, float offsetY, float blurRadius) const
5417 {
5418 float blur = blurRadius * 2.0f;
5419 float leftOffsetX = 0.0f;
5420 float rightOffsetX = 0.0f;
5421 float upOffsetY = 0.0f;
5422 float downOffsetY = 0.0f;
5423 if (LessNotEqual(offsetX - blurRadius, leftOffsetX)) {
5424 leftOffsetX = offsetX - blur;
5425 }
5426 if (GreatNotEqual(offsetX + blur, rightOffsetX)) {
5427 rightOffsetX = offsetX + blur;
5428 }
5429 if (GreatNotEqual(offsetY - blur, upOffsetY)) {
5430 upOffsetY = offsetY - blur;
5431 }
5432 if (GreatNotEqual(offsetY + blur, downOffsetY)) {
5433 downOffsetY = offsetY + blur;
5434 }
5435
5436 rect.SetRect(
5437 leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
5438 }
5439
ProcessBoundRectByTextShadow(RectF & rect)5440 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
5441 {
5442 auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
5443 auto shadowOpt = property->GetSymbolShadow();
5444 if (shadowOpt.has_value()) {
5445 const auto& symbolShadow = shadowOpt.value();
5446 UpdateRectForSymbolShadow(rect, symbolShadow.offset.first, symbolShadow.offset.second, symbolShadow.radius);
5447 return;
5448 }
5449 auto shadows = property->GetTextShadow();
5450 if (!shadows.has_value()) {
5451 return;
5452 }
5453 float leftOffsetX = 0.0f;
5454 float rightOffsetX = 0.0f;
5455 float upOffsetY = 0.0f;
5456 float downOffsetY = 0.0f;
5457 for (const auto& shadow : shadows.value()) {
5458 auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
5459 if (LessNotEqual(shadow.GetOffset().GetX() - shadowBlurRadius, leftOffsetX)) {
5460 leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
5461 }
5462
5463 if (GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
5464 rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
5465 }
5466
5467 if (LessNotEqual(shadow.GetOffset().GetY() - shadowBlurRadius, upOffsetY)) {
5468 upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
5469 }
5470
5471 if (GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
5472 downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
5473 }
5474 }
5475 rect.SetRect(
5476 leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
5477 }
5478
ProcessBoundRectByTextMarquee(RectF & rect)5479 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
5480 {
5481 auto host = GetHost();
5482 CHECK_NULL_VOID(host);
5483 auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
5484 CHECK_NULL_VOID(textLayoutProperty);
5485 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE) {
5486 return;
5487 }
5488 auto geometryNode = host->GetGeometryNode();
5489 CHECK_NULL_VOID(geometryNode);
5490 auto contentSize = geometryNode->GetContentSize();
5491 CHECK_NULL_VOID(pManager_);
5492 if (pManager_->GetTextWidth() < contentSize.Width()) {
5493 return;
5494 }
5495 auto frameSize = geometryNode->GetFrameSize();
5496 auto relativeSelfLeftOffsetX =
5497 std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - pManager_->GetTextWidth());
5498 rect.SetLeft(relativeSelfLeftOffsetX);
5499 rect.SetWidth(frameSize.Width() + pManager_->GetTextWidth() - relativeSelfLeftOffsetX);
5500 }
5501
CreateNodePaintMethod()5502 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
5503 {
5504 CreateModifier();
5505 auto paintMethod = MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
5506 auto host = GetHost();
5507 CHECK_NULL_RETURN(host, paintMethod);
5508 auto context = host->GetRenderContext();
5509 CHECK_NULL_RETURN(context, paintMethod);
5510 auto geometryNode = host->GetGeometryNode();
5511 CHECK_NULL_RETURN(geometryNode, paintMethod);
5512 auto frameSize = geometryNode->GetFrameSize();
5513 if (context->GetClipEdge().value_or(host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
5514 SetResponseRegion(frameSize, frameSize);
5515 return paintMethod;
5516 }
5517 CHECK_NULL_RETURN(pManager_, paintMethod);
5518 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5519 auto thickness = textLayoutProperty->GetLineThicknessScale().value_or(1.0f);
5520 RectF boundsRect = overlayMod_->GetBoundsRect();
5521 auto boundsWidth = contentRect_.GetX() + std::ceil(pManager_->GetLongestLineWithIndent());
5522 auto boundsHeight = contentRect_.GetY() + static_cast<float>(pManager_->GetHeight() + std::fabs(baselineOffset_));
5523 if (GreatNotEqual(thickness, 1.0f)) {
5524 boundsHeight += thickness;
5525 }
5526 boundsRect.SetWidth(boundsWidth);
5527 boundsRect.SetHeight(boundsHeight);
5528 SetResponseRegion(frameSize, boundsRect.GetSize());
5529 ProcessBoundRectByTextShadow(boundsRect);
5530 ProcessBoundRectByTextMarquee(boundsRect);
5531 boundsRect.SetWidth(std::max(frameSize.Width(), boundsRect.Width()));
5532 boundsRect.SetHeight(std::max(frameSize.Height(), boundsRect.Height()));
5533 auto baselineOffset = LessOrEqual(baselineOffset_, 0) ? std::fabs(baselineOffset_) : 0;
5534 pManager_->GetPaintRegion(boundsRect, contentRect_.GetX(), contentRect_.GetY() + baselineOffset);
5535 overlayMod_->SetBoundsRect(boundsRect);
5536 return paintMethod;
5537 }
5538
SetResponseRegion(const SizeF & frameSize,const SizeF & boundsSize)5539 void TextPattern::SetResponseRegion(const SizeF& frameSize, const SizeF& boundsSize)
5540 {
5541 auto host = GetHost();
5542 CHECK_NULL_VOID(host);
5543 auto gestureHub = host->GetOrCreateGestureEventHub();
5544 CHECK_NULL_VOID(gestureHub);
5545 if (isUserSetResponseRegion_) {
5546 return;
5547 }
5548 std::vector<DimensionRect> hotZoneRegions;
5549 DimensionRect hotZoneRegion;
5550 hotZoneRegion.SetSize(DimensionSize(Dimension(std::max(boundsSize.Width(), frameSize.Width())),
5551 Dimension(std::max(frameSize.Height(), boundsSize.Height()))));
5552 hotZoneRegions.emplace_back(hotZoneRegion);
5553 gestureHub->SetResponseRegion(hotZoneRegions);
5554 host->UpdateAccessibilityNodeRect();
5555 }
5556
CreateModifier()5557 void TextPattern::CreateModifier()
5558 {
5559 if (!contentMod_) {
5560 contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_, WeakClaim(this));
5561 }
5562 if (!overlayMod_) {
5563 overlayMod_ = MakeRefPtr<TextOverlayModifier>();
5564 }
5565 if (isCustomFont_) {
5566 contentMod_->SetIsCustomFont(true);
5567 }
5568 }
5569
GetHandleIndex(const Offset & offset) const5570 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
5571 {
5572 return pManager_->GetGlyphIndexByCoordinate(offset);
5573 }
5574
OnHandleAreaChanged()5575 void TextPattern::OnHandleAreaChanged()
5576 {
5577 if (selectOverlay_->SelectOverlayIsOn()) {
5578 auto parentGlobalOffset = GetParentGlobalOffset();
5579 if (parentGlobalOffset != parentGlobalOffset_) {
5580 parentGlobalOffset_ = parentGlobalOffset;
5581 CalculateHandleOffsetAndShowOverlay();
5582 ShowSelectOverlay({ .menuIsShow = false, .animation = true });
5583 }
5584 }
5585 }
5586
RemoveAreaChangeInner()5587 void TextPattern::RemoveAreaChangeInner()
5588 {
5589 auto host = GetHost();
5590 CHECK_NULL_VOID(host);
5591 auto pipeline = host->GetContext();
5592 CHECK_NULL_VOID(pipeline);
5593 auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
5594 CHECK_NULL_VOID(eventHub);
5595 if (eventHub->HasOnAreaChanged()) {
5596 return;
5597 }
5598 pipeline->RemoveOnAreaChangeNode(host->GetId());
5599 }
5600
SetTextDetectEnable(bool enable)5601 void TextPattern::SetTextDetectEnable(bool enable)
5602 {
5603 auto host = GetHost();
5604 // call SetTextDetectEnableMultiThread() by multi thread
5605 FREE_NODE_CHECK(host, SetTextDetectEnable, enable);
5606 CHECK_NULL_VOID(host);
5607 CHECK_NULL_VOID(GetDataDetectorAdapter());
5608 dataDetectorAdapter_->frameNode_ = host;
5609 if (enable == textDetectEnable_) {
5610 return;
5611 }
5612 textDetectEnable_ = enable;
5613 if (textDetectEnable_) {
5614 auto pipeline = host->GetContext();
5615 CHECK_NULL_VOID(pipeline);
5616 auto callback = [weak = WeakClaim(this)]() {
5617 auto pattern = weak.Upgrade();
5618 CHECK_NULL_VOID(pattern);
5619 pattern->dataDetectorAdapter_->GetAIEntityMenu();
5620 };
5621 pipeline->SetConfigChangedCallback(host->GetId(), callback);
5622 } else {
5623 dataDetectorAdapter_->CancelAITask();
5624 }
5625 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
5626 }
5627
CanStartAITask() const5628 bool TextPattern::CanStartAITask() const
5629 {
5630 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5631 if (textLayoutProperty) {
5632 return textDetectEnable_ && enabled_ && !IsSetObscured() &&
5633 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE;
5634 } else {
5635 return textDetectEnable_ && enabled_;
5636 }
5637 }
5638
NeedShowAIDetect()5639 bool TextPattern::NeedShowAIDetect()
5640 {
5641 return CanStartAITask() && !GetDataDetectorAdapter()->aiSpanMap_.empty();
5642 }
5643
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,const SelectMenuParam & menuParam)5644 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
5645 std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
5646 {
5647 auto key = std::make_pair(spanType, responseType);
5648 auto it = selectionMenuMap_.find(key);
5649 if (it != selectionMenuMap_.end()) {
5650 if (menuBuilder == nullptr) {
5651 selectionMenuMap_.erase(it);
5652 return;
5653 }
5654 it->second->buildFunc = menuBuilder;
5655 it->second->onAppear = menuParam.onAppear;
5656 it->second->onDisappear = menuParam.onDisappear;
5657 it->second->onMenuShow = menuParam.onMenuShow;
5658 it->second->onMenuHide = menuParam.onMenuHide;
5659 it->second->isValid = menuParam.isValid;
5660 return;
5661 }
5662
5663 auto selectionMenuParams = std::make_shared<SelectionMenuParams>(
5664 spanType, menuBuilder, menuParam.onAppear, menuParam.onDisappear, responseType);
5665 selectionMenuParams->onMenuShow = menuParam.onMenuShow;
5666 selectionMenuParams->onMenuHide = menuParam.onMenuHide;
5667 selectionMenuParams->isValid = menuParam.isValid;
5668 selectionMenuMap_[key] = selectionMenuParams;
5669 auto host = GetHost();
5670 CHECK_NULL_VOID(host);
5671 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
5672 }
5673
CloseSelectionMenu()5674 void TextPattern::CloseSelectionMenu()
5675 {
5676 textResponseType_ = TextResponseType::NONE;
5677 CloseSelectOverlay(true);
5678 }
5679
GetMenuParams(TextSpanType spanType,TextResponseType responseType)5680 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
5681 {
5682 // JS TextSpanType.DEFAULT = TextSpanType::NONE
5683 // JS TextResponseType.DEFAULT = TextResponseType::NONE
5684 std::vector<std::pair<TextSpanType, TextResponseType>> searchPairs = {
5685 { spanType, responseType },
5686 { spanType, TextResponseType::NONE },
5687 };
5688 if (spanType != TextSpanType::NONE) {
5689 searchPairs.push_back({ TextSpanType::NONE, responseType });
5690 searchPairs.push_back({ TextSpanType::NONE, TextResponseType::NONE });
5691 }
5692 for (const auto& key : searchPairs) {
5693 auto it = selectionMenuMap_.find(key);
5694 if (it != selectionMenuMap_.end()) {
5695 return it->second;
5696 }
5697 }
5698 TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
5699 return nullptr;
5700 }
5701
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)5702 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
5703 {
5704 auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
5705 std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
5706 menuParams = GetMenuParams(currentSpanType, responseType);
5707 if (menuParams == nullptr || !menuParams->isValid) {
5708 return;
5709 }
5710 CopyBindSelectionMenuParams(selectInfo, menuParams);
5711 }
5712
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)5713 void TextPattern::CopyBindSelectionMenuParams(
5714 SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
5715 {
5716 selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
5717 auto weak = AceType::WeakClaim(this);
5718 selectInfo.menuCallback.onAppear = [weak, menuParams]() {
5719 auto pattern = weak.Upgrade();
5720 CHECK_NULL_VOID(pattern);
5721 pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_APPEAR, menuParams);
5722 };
5723 selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
5724 selectInfo.menuCallback.onMenuShow = [weak, menuParams]() {
5725 auto pattern = weak.Upgrade();
5726 CHECK_NULL_VOID(pattern);
5727 pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_SHOW, menuParams);
5728 };
5729 selectInfo.menuCallback.onMenuHide = [weak, menuParams]() {
5730 auto pattern = weak.Upgrade();
5731 CHECK_NULL_VOID(pattern);
5732 pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_HIDE, menuParams);
5733 };
5734 }
5735
OnHandleSelectionMenuCallback(SelectionMenuCalblackId callbackId,std::shared_ptr<SelectionMenuParams> menuParams)5736 void TextPattern::OnHandleSelectionMenuCallback(
5737 SelectionMenuCalblackId callbackId, std::shared_ptr<SelectionMenuParams> menuParams)
5738 {
5739 std::function<void(int32_t, int32_t)> callback;
5740 switch (callbackId) {
5741 case SelectionMenuCalblackId::MENU_SHOW:
5742 callback = menuParams->onMenuShow;
5743 break;
5744 case SelectionMenuCalblackId::MENU_HIDE:
5745 callback = menuParams->onMenuHide;
5746 break;
5747 case SelectionMenuCalblackId::MENU_APPEAR:
5748 callback = menuParams->onAppear;
5749 break;
5750 default:
5751 callback = nullptr;
5752 }
5753 CHECK_NULL_VOID(callback);
5754 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
5755 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
5756 callback(selectStart, selectEnd);
5757 }
5758
FireOnSelectionChange(int32_t start,int32_t end)5759 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
5760 {
5761 auto host = GetHost();
5762 CHECK_NULL_VOID(host);
5763 auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
5764 CHECK_NULL_VOID(eventHub);
5765 eventHub->FireOnSelectionChange(start, end);
5766 }
5767
FireOnMarqueeStateChange(const TextMarqueeState & state)5768 void TextPattern::FireOnMarqueeStateChange(const TextMarqueeState& state)
5769 {
5770 auto host = GetHost();
5771 CHECK_NULL_VOID(host);
5772 auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
5773 CHECK_NULL_VOID(eventHub);
5774 eventHub->FireOnMarqueeStateChange(static_cast<int32_t>(state));
5775
5776 if (TextMarqueeState::START == state) {
5777 CloseSelectOverlay();
5778 ResetSelection();
5779 isMarqueeRunning_ = true;
5780 } else if (TextMarqueeState::FINISH == state) {
5781 isMarqueeRunning_ = false;
5782 }
5783
5784 RecoverCopyOption();
5785 }
5786
OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback && onCreateMenuCallback,const NG::OnMenuItemClickCallback && onMenuItemClick,const NG::OnPrepareMenuCallback && onPrepareMenuCallback)5787 void TextPattern::OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback&& onCreateMenuCallback,
5788 const NG::OnMenuItemClickCallback&& onMenuItemClick, const NG::OnPrepareMenuCallback&& onPrepareMenuCallback)
5789 {
5790 selectOverlay_->OnSelectionMenuOptionsUpdate(
5791 std::move(onCreateMenuCallback), std::move(onMenuItemClick), std::move(onPrepareMenuCallback));
5792 }
5793
StartVibratorByIndexChange(int32_t currentIndex,int32_t preIndex)5794 void TextPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
5795 {
5796 CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
5797 VibratorUtils::StartVibraFeedback("slide");
5798 }
5799
HandleSelectionChange(int32_t start,int32_t end)5800 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
5801 {
5802 auto host = GetHost();
5803 CHECK_NULL_VOID(host);
5804 if (SystemProperties::GetTextTraceEnabled()) {
5805 TAG_LOGI(AceLogTag::ACE_TEXT, "HandleSelectionChange[id:%{public}d][start:%{public}d][end:%{public}d]",
5806 host->GetId(), start, end);
5807 }
5808 if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
5809 return;
5810 }
5811
5812 bool changeSymbolEffect = false;
5813 for (auto& span: spans_) {
5814 if (span->GetSymbolUnicode() == 0) {
5815 continue;
5816 }
5817 bool nextEffectSwitch = start != -1 && end != -1 ? false : true;
5818 if (span->GetSymbolEffectSwitch() != nextEffectSwitch) {
5819 span->SetSymbolEffectSwitch(nextEffectSwitch);
5820 changeSymbolEffect = true;
5821 }
5822 }
5823 textSelector_.Update(start, end);
5824 UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
5825 FireOnSelectionChange(std::min(start, end), std::max(start, end));
5826 if (changeSymbolEffect) {
5827 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
5828 }
5829 }
5830
IsSelectedBindSelectionMenu()5831 bool TextPattern::IsSelectedBindSelectionMenu()
5832 {
5833 auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
5834 return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
5835 }
5836
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)5837 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
5838 {
5839 UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
5840 if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
5841 textSelector_.StartEqualToDest()) {
5842 selectedType_ = TextSpanType::TEXT;
5843 }
5844 }
5845
UpdateSelectionType(const SelectionInfo & selection)5846 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
5847 {
5848 selectedType_ = TextSpanType::NONE;
5849 auto list = selection.GetSelection().resultObjects;
5850 bool imageSelected = false;
5851 bool textSelected = false;
5852 bool builderSelected = false;
5853 for (const auto& obj : list) {
5854 if (obj.type == SelectSpanType::TYPEIMAGE) {
5855 imageSelected = true;
5856 } else if (obj.type == SelectSpanType::TYPESPAN) {
5857 textSelected = true;
5858 } else if (obj.type == SelectSpanType::TYPEBUILDERSPAN) {
5859 builderSelected = true;
5860 }
5861 if ((imageSelected && textSelected) || (builderSelected && textSelected) ||
5862 (imageSelected && builderSelected)) {
5863 selectedType_ = TextSpanType::MIXED;
5864 return;
5865 }
5866 }
5867 if (imageSelected) {
5868 selectedType_ = TextSpanType::IMAGE;
5869 } else if (textSelected) {
5870 selectedType_ = TextSpanType::TEXT;
5871 } else if (builderSelected) {
5872 selectedType_ = TextSpanType::BUILDER;
5873 }
5874
5875 TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
5876 }
5877
GetSelectionSpanItemIndex(const MouseInfo & info)5878 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
5879 {
5880 RectF textContentRect = contentRect_;
5881 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5882 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
5883 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
5884 info.GetLocalLocation().GetY() - textContentRect.GetY() };
5885 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
5886 spans_.empty() || pManager_->GetParagraphs().empty()) {
5887 return -1;
5888 }
5889 int32_t start = 0;
5890 bool isFind = false;
5891 int32_t index = -1;
5892 for (const auto& item : spans_) {
5893 index++;
5894 if (!item) {
5895 continue;
5896 }
5897 auto selectedRects = pManager_->GetRects(start, item->position);
5898 start = item->position;
5899 for (auto&& rect : selectedRects) {
5900 if (rect.IsInRegion(textOffset)) {
5901 isFind = true;
5902 break;
5903 }
5904 }
5905 if (isFind) {
5906 TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
5907 return index;
5908 }
5909 }
5910 return -1;
5911 }
5912
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)5913 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
5914 {
5915 int32_t itemLength = 1;
5916 ResultObject resultObject;
5917 resultObject.isDraggable = true;
5918 if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
5919 return resultObject;
5920 }
5921 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
5922 int32_t startPosition = endPosition - itemLength;
5923 if ((start <= startPosition) && (end >= endPosition)) {
5924 auto builderNode = DynamicCast<FrameNode>(uiNode);
5925 CHECK_NULL_RETURN(builderNode, resultObject);
5926 resultObject.spanPosition.spanIndex = index;
5927 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
5928 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
5929 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
5930 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
5931 resultObject.type = SelectSpanType::TYPEIMAGE;
5932 auto geometryNode = builderNode->GetGeometryNode();
5933 CHECK_NULL_RETURN(geometryNode, resultObject);
5934 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
5935 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
5936 resultObject.valueString = u" ";
5937 }
5938 return resultObject;
5939 }
5940
SetStyledString(const RefPtr<SpanString> & value,bool closeSelectOverlay)5941 void TextPattern::SetStyledString(const RefPtr<SpanString>& value, bool closeSelectOverlay)
5942 {
5943 auto host = GetHost();
5944 FREE_NODE_CHECK(host, SetStyledString, value,
5945 closeSelectOverlay); // call SetStyledStringMultiThread() by multi thread
5946 AllocStyledString();
5947 isSpanStringMode_ = true;
5948 CHECK_NULL_VOID(host);
5949 if (closeSelectOverlay) {
5950 CloseSelectOverlay();
5951 }
5952 auto length = styledString_->GetLength();
5953 styledString_->RemoveCustomSpan();
5954 styledString_->ReplaceSpanString(0, length, value);
5955 spans_ = styledString_->GetSpanItems();
5956 if (SystemProperties::GetTextTraceEnabled()) {
5957 ACE_TEXT_SCOPED_TRACE(
5958 "TextPattern::SetStyledString[id:%d][size:%d]", host->GetId(), static_cast<int32_t>(spans_.size()));
5959 }
5960 ProcessSpanString();
5961 styledString_->AddCustomSpan();
5962 styledString_->SetFramNode(WeakClaim(Referenced::RawPtr(host)));
5963 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
5964 }
5965
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)5966 void TextPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
5967 {
5968 auto host = GetContentHost();
5969 CHECK_NULL_VOID(host);
5970 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
5971 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
5972 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
5973 auto options = imageItem->options;
5974 imageLayoutProperty->UpdateImageSourceInfo(ParagraphUtil::CreateImageSourceInfo(options));
5975 imageNode->MountToParent(host, host->GetChildren().size());
5976 SetImageNodeGesture(imageNode);
5977 if (options.imageAttribute.has_value()) {
5978 auto imgAttr = options.imageAttribute.value();
5979 auto imagePattern = imageNode->GetPattern<ImagePattern>();
5980 CHECK_NULL_VOID(imagePattern);
5981 imagePattern->SetSyncLoad(imgAttr.syncLoad);
5982 if (imgAttr.size.has_value()) {
5983 imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
5984 }
5985 if (imgAttr.verticalAlign.has_value()) {
5986 imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
5987 }
5988 if (imgAttr.objectFit.has_value()) {
5989 imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
5990 }
5991 if (imgAttr.marginProp.has_value()) {
5992 imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
5993 }
5994 if (imgAttr.paddingProp.has_value()) {
5995 imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
5996 }
5997 if (imgAttr.borderRadius.has_value()) {
5998 auto imageRenderCtx = imageNode->GetRenderContext();
5999 imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
6000 imageRenderCtx->SetClipToBounds(true);
6001 }
6002 auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
6003 if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
6004 paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
6005 paintProperty->ResetDrawingColorFilter();
6006 } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
6007 paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
6008 paintProperty->ResetColorFilter();
6009 }
6010 }
6011 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
6012 imageNode->MarkModifyDone();
6013 imageItem->nodeId_ = imageNode->GetId();
6014 imageNode->SetImageItem(imageItem);
6015 childNodes_.emplace_back(imageNode);
6016 }
6017
SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)6018 void TextPattern::SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)
6019 {
6020 auto gesture = imageNode->GetOrCreateGestureEventHub();
6021 CHECK_NULL_VOID(gesture);
6022 gesture->SetHitTestMode(HitTestMode::HTMNONE);
6023 }
6024
ProcessSpanString()6025 void TextPattern::ProcessSpanString()
6026 {
6027 auto host = GetHost();
6028 CHECK_NULL_VOID(host);
6029 textForDisplay_.clear();
6030 childNodes_.clear();
6031 // styled string perf can be optimized via create as requirement
6032 CHECK_NULL_VOID(GetDataDetectorAdapter());
6033 dataDetectorAdapter_->textForAI_.clear();
6034 host->Clean();
6035 hasSpanStringLongPressEvent_ = false;
6036 hasUrlSpan_ = false;
6037
6038 // 适配AI&&挂载image节点
6039 for (const auto& span : spans_) {
6040 auto imageSpan = DynamicCast<ImageSpanItem>(span);
6041 if (imageSpan) {
6042 dataDetectorAdapter_->textForAI_ += u'\n';
6043 MountImageNode(imageSpan);
6044 } else {
6045 dataDetectorAdapter_->textForAI_ += span->content;
6046 }
6047 if (span->onClick || span->urlOnRelease) {
6048 auto gestureEventHub = host->GetOrCreateGestureEventHub();
6049 InitClickEvent(gestureEventHub);
6050 }
6051 if (span->onLongPress) {
6052 auto gestureEventHub = host->GetOrCreateGestureEventHub();
6053 InitLongPressEvent(gestureEventHub);
6054 hasSpanStringLongPressEvent_ = true;
6055 }
6056 if (span->urlOnRelease) {
6057 hasUrlSpan_ = true;
6058 InitUrlMouseEvent();
6059 InitUrlTouchEvent();
6060 }
6061 if (span->onTouch) {
6062 InitSpanStringTouchEvent();
6063 }
6064 textForDisplay_ += span->content;
6065 }
6066 if (dataDetectorAdapter_->textForAI_ != textForDisplay_) {
6067 dataDetectorAdapter_->aiDetectInitialized_ = false;
6068 }
6069 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
6070 dataDetectorAdapter_->StartAITask();
6071 }
6072
6073 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
6074 CHECK_NULL_VOID(layoutProperty);
6075 layoutProperty->UpdateContent(textForDisplay_);
6076 }
6077
OnSensitiveStyleChange(bool isSensitive)6078 void TextPattern::OnSensitiveStyleChange(bool isSensitive)
6079 {
6080 auto host = GetHost();
6081 CHECK_NULL_VOID(host);
6082 isSensitive_ = isSensitive;
6083 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
6084 }
6085
IsSensitiveEnable()6086 bool TextPattern::IsSensitiveEnable()
6087 {
6088 auto host = GetHost();
6089 CHECK_NULL_RETURN(host, false);
6090 return isSensitive_ && host->IsPrivacySensitive();
6091 }
6092
ConvertGlobalToLocalOffset(const Offset & globalOffset)6093 Offset TextPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
6094 {
6095 auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
6096 selectOverlay_->RevertLocalPointWithTransform(localPoint);
6097 return Offset(localPoint.GetX(), localPoint.GetY());
6098 }
6099
SetExternalSpanItem(const std::list<RefPtr<SpanItem>> & spans)6100 void TextPattern::SetExternalSpanItem(const std::list<RefPtr<SpanItem>>& spans)
6101 {
6102 auto host = GetHost();
6103 FREE_NODE_CHECK(host, SetExternalSpanItem, spans); // call SetExternalSpanItemMultiThread() by multi thread
6104 isSpanStringMode_ = !spans.empty();
6105 if (isSpanStringMode_) {
6106 AllocStyledString();
6107 }
6108 spans_ = spans;
6109 ProcessSpanString();
6110 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
6111 CHECK_NULL_VOID(layoutProperty);
6112 layoutProperty->UpdateContent(textForDisplay_);
6113 }
6114
GetTextContentRect(bool isActualText) const6115 RectF TextPattern::GetTextContentRect(bool isActualText) const
6116 {
6117 auto textRect = contentRect_;
6118 auto host = GetHost();
6119 CHECK_NULL_RETURN(host, textRect);
6120 auto renderContext = host->GetRenderContext();
6121 CHECK_NULL_RETURN(renderContext, textRect);
6122 CHECK_NULL_RETURN(pManager_, textRect);
6123 if (!renderContext->GetClipEdge().value_or(false) &&
6124 LessNotEqual(textRect.Width(), pManager_->GetLongestLine())) {
6125 textRect.SetWidth(pManager_->GetLongestLine());
6126 }
6127 if (isActualText && !renderContext->GetClipEdge().value_or(false) &&
6128 LessNotEqual(textRect.Height(), pManager_->GetHeight())) {
6129 textRect.SetHeight(pManager_->GetHeight());
6130 }
6131 return textRect;
6132 }
6133
GetLineCount() const6134 size_t TextPattern::GetLineCount() const
6135 {
6136 CHECK_NULL_RETURN(pManager_, 0);
6137 return pManager_->GetLineCount();
6138 }
6139
DidExceedMaxLines() const6140 bool TextPattern::DidExceedMaxLines() const
6141 {
6142 CHECK_NULL_RETURN(pManager_, false);
6143 return pManager_->DidExceedMaxLines();
6144 }
6145
IsSetObscured() const6146 bool TextPattern::IsSetObscured() const
6147 {
6148 auto host = GetHost();
6149 CHECK_NULL_RETURN(host, false);
6150 auto renderContext = host->GetRenderContext();
6151 CHECK_NULL_RETURN(renderContext, false);
6152 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
6153 bool ifHaveObscured = spans_.empty() && std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
6154 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
6155 return ifHaveObscured;
6156 }
6157
GetLineMetrics(int32_t lineNumber)6158 TextLineMetrics TextPattern::GetLineMetrics(int32_t lineNumber)
6159 {
6160 CHECK_NULL_RETURN(pManager_, TextLineMetrics());
6161 if (lineNumber < 0 || GetLineCount() == 0 || lineNumber > static_cast<int32_t>(GetLineCount()) - 1) {
6162 TAG_LOGI(AceLogTag::ACE_TEXT, "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d",
6163 lineNumber);
6164 return TextLineMetrics();
6165 }
6166 auto lineMetrics = pManager_->GetLineMetrics(lineNumber);
6167 RectF textContentRect = contentRect_;
6168 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
6169 lineMetrics.x += textContentRect.GetX();
6170 lineMetrics.y += textContentRect.GetY();
6171 lineMetrics.baseline += textContentRect.GetY();
6172 return lineMetrics;
6173 }
6174
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)6175 std::vector<ParagraphManager::TextBox> TextPattern::GetRectsForRange(
6176 int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
6177 {
6178 if (start < 0 || end < 0 || start > end) {
6179 return {};
6180 }
6181 std::vector<ParagraphManager::TextBox> textBoxes = pManager_->GetRectsForRange(start, end, heightStyle, widthStyle);
6182 RectF textContentRect = contentRect_;
6183 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
6184 std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
6185 for (auto& textBox : textBoxes) {
6186 ParagraphManager::TextBox adjustedTextBox = textBox;
6187 adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textContentRect.Left());
6188 adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textContentRect.Top());
6189 adjustedTextBoxes.push_back(adjustedTextBox);
6190 }
6191 return adjustedTextBoxes;
6192 }
6193
ConvertLocalOffsetToParagraphOffset(const Offset & offset)6194 Offset TextPattern::ConvertLocalOffsetToParagraphOffset(const Offset& offset)
6195 {
6196 RectF textContentRect = contentRect_;
6197 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
6198 Offset paragraphOffset = { offset.GetX() - textContentRect.GetX(), offset.GetY() - textContentRect.GetY() };
6199 return paragraphOffset;
6200 }
6201
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)6202 PositionWithAffinity TextPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
6203 {
6204 Offset offset(x, y);
6205 return pManager_->GetGlyphPositionAtCoordinate(ConvertLocalOffsetToParagraphOffset(offset));
6206 }
6207
ProcessMarqueeVisibleAreaCallback()6208 void TextPattern::ProcessMarqueeVisibleAreaCallback()
6209 {
6210 OnTextOverflowChanged();
6211 if (!IsMarqueeOverflow()) {
6212 return;
6213 }
6214 auto host = GetHost();
6215 CHECK_NULL_VOID(host);
6216 auto pipeline = GetContext();
6217 CHECK_NULL_VOID(pipeline);
6218 auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
6219 auto pattern = weak.Upgrade();
6220 CHECK_NULL_VOID(pattern);
6221 CHECK_NULL_VOID(pattern->contentMod_);
6222 if (!pattern->IsMarqueeOverflow()) {
6223 return;
6224 }
6225 if (visible && Positive(ratio)) {
6226 pattern->contentMod_->ResumeAnimation();
6227 }
6228 if (!visible && NonPositive(ratio)) {
6229 pattern->contentMod_->PauseAnimation();
6230 }
6231 };
6232 std::vector<double> ratioList = { 0.0 };
6233 pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
6234 }
6235
OnTextOverflowChanged()6236 void TextPattern::OnTextOverflowChanged()
6237 {
6238 auto host = GetHost();
6239 CHECK_NULL_VOID(host);
6240 if (host->GetTag() == V2::SYMBOL_ETS_TAG) {
6241 return;
6242 }
6243 auto pipeline = GetContext();
6244 CHECK_NULL_VOID(pipeline);
6245 auto eventHub = host->GetOrCreateEventHub<TextEventHub>();
6246 CHECK_NULL_VOID(eventHub);
6247 auto hasInnerCallabck = eventHub->HasVisibleAreaCallback(false);
6248 if (!hasInnerCallabck) {
6249 return;
6250 }
6251 auto hasUserCallback = eventHub->HasVisibleAreaCallback(true);
6252 if (!hasUserCallback) {
6253 pipeline->RemoveVisibleAreaChangeNode(host->GetId());
6254 }
6255 eventHub->CleanVisibleAreaCallback(false);
6256 }
6257
OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)6258 void TextPattern::OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)
6259 {
6260 if (selectOverlay_->SelectOverlayIsOn()) {
6261 selectOverlay_->OnAncestorNodeChanged(flag);
6262 }
6263 if (leftMousePressed_ && mouseStatus_ == MouseStatus::MOVE && scrollableParent_.Upgrade()) {
6264 auto host = GetHost();
6265 CHECK_NULL_VOID(host);
6266 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
6267 NG::PointF localPoint(lastLeftMouseMoveLocation_.GetX(), lastLeftMouseMoveLocation_.GetY());
6268 NG::NGGestureRecognizer::Transform(localPoint, WeakClaim(Referenced::RawPtr(host)), true);
6269 Offset textOffset = { localPoint.GetX() - textPaintOffset.GetX(), localPoint.GetY() - textPaintOffset.GetY() };
6270 CHECK_NULL_VOID(pManager_);
6271 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
6272 HandleSelectionChange(textSelector_.baseOffset, end);
6273 isAutoScrollByMouse_ = true;
6274 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6275 }
6276 }
6277
IsMarqueeOverflow() const6278 bool TextPattern::IsMarqueeOverflow() const
6279 {
6280 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
6281 CHECK_NULL_RETURN(textLayoutProperty, false);
6282 return textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE;
6283 }
6284
UnRegisterResource(const std::string & key)6285 void TextPattern::UnRegisterResource(const std::string& key)
6286 {
6287 if (key == "symbolColor") {
6288 for (auto index : symbolFontColorResObjIndexArr) {
6289 auto storeKey = key + "_" + std::to_string(index);
6290 RemoveResObj(storeKey);
6291 }
6292 symbolFontColorResObjIndexArr.clear();
6293 return;
6294 }
6295 Pattern::UnRegisterResource(key);
6296 }
6297
UpdateFontColor(const Color & value)6298 void TextPattern::UpdateFontColor(const Color& value)
6299 {
6300 auto host = GetHost();
6301 CHECK_NULL_VOID(host);
6302 const auto& children = host->GetChildren();
6303 if (children.empty() && spans_.empty() && !NeedShowAIDetect() && !textEffect_) {
6304 if (textStyle_.has_value()) {
6305 textStyle_->SetTextColor(value);
6306 }
6307 if (contentMod_) {
6308 contentMod_->TextColorModifier(value);
6309 } else if (pManager_) {
6310 for (auto&& info : pManager_->GetParagraphs()) {
6311 auto paragraph = info.paragraph;
6312 CHECK_NULL_VOID(paragraph);
6313 auto length = paragraph->GetParagraphText().length();
6314 paragraph->UpdateColor(0, length, value);
6315 }
6316 }
6317 } else {
6318 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
6319 }
6320 }
6321
MarkDirtyNodeRender()6322 void TextPattern::MarkDirtyNodeRender()
6323 {
6324 auto host = GetHost();
6325 CHECK_NULL_VOID(host);
6326 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6327 }
6328
MarkDirtyNodeMeasure()6329 void TextPattern::MarkDirtyNodeMeasure()
6330 {
6331 auto host = GetHost();
6332 CHECK_NULL_VOID(host);
6333 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
6334 }
6335
BeforeCreatePaintWrapper()6336 void TextPattern::BeforeCreatePaintWrapper()
6337 {
6338 // mark content dirty
6339 if (contentMod_) {
6340 contentMod_->ContentChange();
6341 }
6342 }
6343
StartGestureSelection(int32_t start,int32_t end,const Offset & startOffset)6344 void TextPattern::StartGestureSelection(int32_t start, int32_t end, const Offset& startOffset)
6345 {
6346 scrollableParent_ = selectOverlay_->FindScrollableParent();
6347 SetupMagnifier();
6348 TextGestureSelector::StartGestureSelection(start, end, startOffset);
6349 }
6350
GetTouchIndex(const OffsetF & offset)6351 int32_t TextPattern::GetTouchIndex(const OffsetF& offset)
6352 {
6353 OffsetF deltaOffset;
6354 if (scrollableParent_.Upgrade()) {
6355 auto parentGlobalOffset = GetParentGlobalOffset();
6356 deltaOffset = parentGlobalOffset - parentGlobalOffset_;
6357 }
6358 auto paragraphOffset =
6359 offset - deltaOffset - GetTextContentRect().GetOffset() + OffsetF(0.0f, std::min(GetBaselineOffset(), 0.0f));
6360 return GetHandleIndex({ paragraphOffset.GetX(), paragraphOffset.GetY() });
6361 }
6362
OnTextGestureSelectionUpdate(int32_t start,int32_t end,const TouchEventInfo & info)6363 void TextPattern::OnTextGestureSelectionUpdate(int32_t start, int32_t end, const TouchEventInfo& info)
6364 {
6365 if (!HasContent()) {
6366 return;
6367 }
6368 selectOverlay_->TriggerScrollableParentToScroll(
6369 scrollableParent_.Upgrade(), info.GetTouches().front().GetGlobalLocation(), false);
6370 auto localOffset = info.GetTouches().front().GetLocalLocation();
6371 if (GetOrCreateMagnifier()) {
6372 magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
6373 }
6374 if (start != textSelector_.GetStart()) {
6375 StartVibratorByIndexChange(start, textSelector_.GetStart());
6376 } else if (end != textSelector_.GetEnd()) {
6377 StartVibratorByIndexChange(end, textSelector_.GetEnd());
6378 }
6379 auto host = GetHost();
6380 CHECK_NULL_VOID(host);
6381 HandleSelectionChange(start, end);
6382 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
6383 }
6384
OnTextGestureSelectionEnd(const TouchLocationInfo & locationInfo)6385 void TextPattern::OnTextGestureSelectionEnd(const TouchLocationInfo& locationInfo)
6386 {
6387 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), Offset(), true);
6388 if (magnifierController_) {
6389 magnifierController_->RemoveMagnifierFrameNode();
6390 }
6391 if (HasContent()) {
6392 CalculateHandleOffsetAndShowOverlay();
6393 oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
6394 ShowSelectOverlay({ .animation = true });
6395 }
6396 }
6397
ChangeHandleHeight(const GestureEvent & event,bool isFirst,bool isOverlayMode)6398 void TextPattern::ChangeHandleHeight(const GestureEvent& event, bool isFirst, bool isOverlayMode)
6399 {
6400 auto touchOffset = event.GetGlobalLocation();
6401 auto& currentHandle = isFirst ? textSelector_.firstHandle : textSelector_.secondHandle;
6402 bool isChangeFirstHandle = isFirst ? (!textSelector_.StartGreaterDest()) : textSelector_.StartGreaterDest();
6403 if (isChangeFirstHandle) {
6404 ChangeFirstHandleHeight(touchOffset, currentHandle);
6405 } else {
6406 if (!selectOverlay_->ChangeSecondHandleHeight(event, isOverlayMode)) {
6407 ChangeSecondHandleHeight(touchOffset, currentHandle);
6408 }
6409 }
6410 }
6411
ChangeFirstHandleHeight(const Offset & touchOffset,RectF & handleRect)6412 void TextPattern::ChangeFirstHandleHeight(const Offset& touchOffset, RectF& handleRect)
6413 {
6414 auto height = handleRect.Height();
6415 CalculateDefaultHandleHeight(height);
6416 bool isTouchHandleCircle = LessNotEqual(touchOffset.GetY(), handleRect.Top());
6417 if (!isTouchHandleCircle) {
6418 handleRect.SetTop(static_cast<float>(touchOffset.GetY()) - height / 2.0f);
6419 }
6420 handleRect.SetHeight(height);
6421 }
6422
ChangeSecondHandleHeight(const Offset & touchOffset,RectF & handleRect)6423 void TextPattern::ChangeSecondHandleHeight(const Offset& touchOffset, RectF& handleRect)
6424 {
6425 auto height = handleRect.Height();
6426 CalculateDefaultHandleHeight(height);
6427 bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
6428 auto handleOffsetY = isTouchHandleCircle
6429 ? handleRect.Bottom() - height
6430 : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
6431 handleRect.SetTop(handleOffsetY);
6432 handleRect.SetHeight(height);
6433 }
6434
CalculateDefaultHandleHeight(float & height)6435 void TextPattern::CalculateDefaultHandleHeight(float& height)
6436 {
6437 CHECK_NULL_VOID(textStyle_.has_value());
6438 #ifdef ENABLE_ROSEN_BACKEND
6439 MeasureContext content;
6440 content.textContent = "a";
6441 content.fontSize = textStyle_.value().GetFontSize();
6442 auto fontweight = StringUtils::FontWeightToString(textStyle_.value().GetFontWeight());
6443 content.fontWeight = fontweight;
6444 height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
6445 #endif
6446 }
6447
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)6448 void TextPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
6449 {
6450 json->Put("contentRect", contentRect_.ToString().c_str());
6451 if (SystemProperties::GetDebugEnabled() && pManager_) {
6452 std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
6453 children->Put("DidExceedMaxLines", std::to_string(pManager_->DidExceedMaxLines()).c_str());
6454 children->Put("GetTextWidth", std::to_string(pManager_->GetTextWidth()).c_str());
6455 children->Put("GetHeight", std::to_string(pManager_->GetHeight()).c_str());
6456 children->Put("GetMaxWidth", std::to_string(pManager_->GetMaxWidth()).c_str());
6457 children->Put("GetMaxIntrinsicWidth", std::to_string(pManager_->GetMaxIntrinsicWidth()).c_str());
6458 children->Put("GetLineCount", std::to_string(pManager_->GetLineCount()).c_str());
6459 children->Put("GetLongestLine", std::to_string(pManager_->GetLongestLine()).c_str());
6460 children->Put("GetLongestLineWithIndent", std::to_string(pManager_->GetLongestLineWithIndent()).c_str());
6461 json->Put("from TextEngine paragraphs_ info", children);
6462 }
6463 json->Put("BindSelectionMenu", std::to_string(selectionMenuMap_.empty()).c_str());
6464 auto host = GetHost();
6465 CHECK_NULL_VOID(host);
6466 auto pipeline = host->GetContext();
6467 CHECK_NULL_VOID(pipeline);
6468 auto fontScale = pipeline->GetFontScale();
6469 auto fontWeightScale = pipeline->GetFontWeightScale();
6470 json->Put("fontScale", std::to_string(fontScale).c_str());
6471 json->Put("fontWeightScale", std::to_string(fontWeightScale).c_str());
6472 auto renderContext = host->GetRenderContext();
6473 CHECK_NULL_VOID(renderContext);
6474 if (renderContext->HasForegroundColor()) {
6475 json->Put("ForegroundColor", renderContext->GetForegroundColorValue().ColorToString().c_str());
6476 }
6477 if (renderContext->GetForegroundColorStrategy().has_value()) {
6478 auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
6479 json->Put("ForegroundColorStrategy", strategy);
6480 }
6481 }
6482
SetTextStyleDumpInfo(std::unique_ptr<JsonValue> & json)6483 void TextPattern::SetTextStyleDumpInfo(std::unique_ptr<JsonValue>& json)
6484 {
6485 if (textStyle_.has_value()) {
6486 json->Put("MaxFontSize", textStyle_->GetAdaptMaxFontSize().ToString().c_str());
6487 json->Put("MinFontSize", textStyle_->GetAdaptMinFontSize().ToString().c_str());
6488 json->Put("FontWeight", StringUtils::ToString(textStyle_->GetFontWeight()).c_str());
6489 json->Put("FontStyle", StringUtils::ToString(textStyle_->GetFontStyle()).c_str());
6490 json->Put("LineHeight", textStyle_->GetLineHeight().ToString().c_str());
6491 json->Put("LineSpacing", textStyle_->GetLineSpacing().ToString().c_str());
6492 json->Put("isOnlyBetweenLines", (textStyle_->GetIsOnlyBetweenLines()) ? "true" : "false");
6493 json->Put("BaselineOffset", textStyle_->GetBaselineOffset().ToString().c_str());
6494 json->Put("TextIndent", textStyle_->GetTextIndent().ToString().c_str());
6495 json->Put("LetterSpacing", textStyle_->GetLetterSpacing().ToString().c_str());
6496 json->Put("TextOverflow", StringUtils::ToString(textStyle_->GetTextOverflow()).c_str());
6497 json->Put("TextAlign", StringUtils::ToString(textStyle_->GetTextAlign()).c_str());
6498 json->Put("WordBreak", StringUtils::ToString(textStyle_->GetWordBreak()).c_str());
6499 json->Put("TextCase", StringUtils::ToString(textStyle_->GetTextCase()).c_str());
6500 json->Put("EllipsisMode", StringUtils::ToString(textStyle_->GetEllipsisMode()).c_str());
6501 json->Put("LineBreakStrategy", GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy()).c_str());
6502 }
6503 }
6504
DumpInfo(std::unique_ptr<JsonValue> & json)6505 void TextPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
6506 {
6507 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
6508 CHECK_NULL_VOID(textLayoutProp);
6509 auto nowTime = GetSystemTimestamp();
6510 json->Put("time", std::to_string(nowTime).c_str());
6511 if (!IsSetObscured() && !IsSensitiveEnable()) {
6512 json->Put("Content", UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" ")).c_str());
6513 }
6514 json->Put("ConteFontColornt",
6515 (textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString().c_str());
6516 json->Put(
6517 "FontSize", (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
6518 .ToString()
6519 .c_str());
6520 SetTextStyleDumpInfo(json);
6521 json->Put("HeightAdaptivePolicy",
6522 V2::ConvertWrapTextHeightAdaptivePolicyToString(
6523 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))
6524 .c_str());
6525
6526 json->Put("Selection", textSelector_.ToString().c_str());
6527
6528 if (pManager_ && !pManager_->GetParagraphs().empty()) {
6529 auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
6530 json->Put("Paragraphs num", std::to_string(num).c_str());
6531 json->Put("PaintInfo", paintInfo_.c_str());
6532 }
6533 if (SystemProperties::GetDebugEnabled()) {
6534 DumpAdvanceInfo(json);
6535 }
6536 }
6537
HasContent()6538 bool TextPattern::HasContent()
6539 {
6540 if (GetTextForDisplay().empty()) {
6541 for (const auto& span : spans_) {
6542 if (span->spanItemType != SpanItemType::NORMAL) {
6543 return true;
6544 }
6545 }
6546 return false;
6547 }
6548 return true;
6549 }
6550
SetupMagnifier()6551 void TextPattern::SetupMagnifier()
6552 {
6553 GetOrCreateMagnifier();
6554 CHECK_NULL_VOID(magnifierController_);
6555 auto host = GetHost();
6556 CHECK_NULL_VOID(host);
6557 auto renderContext = host->GetRenderContext();
6558 CHECK_NULL_VOID(renderContext);
6559 auto geometryNode = host->GetGeometryNode();
6560 CHECK_NULL_VOID(geometryNode);
6561 if (renderContext->GetClipEdge().value_or(false)) {
6562 return;
6563 }
6564 RectF viewPort;
6565 if (selectOverlay_->GetClipHandleViewPort(viewPort)) {
6566 viewPort.SetHeight(std::min(pManager_->GetHeight(), viewPort.Height()));
6567 viewPort.SetWidth(std::min(pManager_->GetLongestLine(), viewPort.Width()));
6568 auto globalFrameRect = geometryNode->GetFrameRect();
6569 globalFrameRect.SetOffset(parentGlobalOffset_);
6570 auto maxRight = std::max(viewPort.Right(), globalFrameRect.Right());
6571 auto maxBottom = std::max(viewPort.Bottom(), globalFrameRect.Bottom());
6572 viewPort = geometryNode->GetFrameRect();
6573 viewPort.SetWidth(maxRight - globalFrameRect.Left());
6574 viewPort.SetHeight(maxBottom - globalFrameRect.Top());
6575 magnifierController_->SetHostViewPort(viewPort);
6576 }
6577 }
6578
DoTextSelectionTouchCancel()6579 void TextPattern::DoTextSelectionTouchCancel()
6580 {
6581 CHECK_NULL_VOID(magnifierController_);
6582 magnifierController_->RemoveMagnifierFrameNode();
6583 ResetSelection();
6584 }
6585
BeforeSyncGeometryProperties(const DirtySwapConfig & config)6586 void TextPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
6587 {
6588 if (afterLayoutCallback_.has_value()) {
6589 (*afterLayoutCallback_)();
6590 }
6591 }
6592
GetCaretColor() const6593 std::string TextPattern::GetCaretColor() const
6594 {
6595 auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
6596 CHECK_NULL_RETURN(context, "");
6597 auto theme = context->GetTheme<TextTheme>();
6598 CHECK_NULL_RETURN(theme, "");
6599 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
6600 CHECK_NULL_RETURN(textLayoutProperty, "");
6601 return textLayoutProperty->GetCursorColorValue(theme->GetCaretColor()).ColorToString();
6602 }
6603
GetSelectedBackgroundColor() const6604 std::string TextPattern::GetSelectedBackgroundColor() const
6605 {
6606 auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
6607 CHECK_NULL_RETURN(context, "");
6608 auto theme = context->GetTheme<TextTheme>();
6609 CHECK_NULL_RETURN(theme, "");
6610 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
6611 CHECK_NULL_RETURN(textLayoutProperty, "");
6612 return textLayoutProperty->GetSelectedBackgroundColorValue(theme->GetSelectedColor()).ColorToString();
6613 }
6614
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)6615 void TextPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
6616 {
6617 CHECK_NULL_VOID(selectOverlay_);
6618 selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
6619 }
6620
IsLocationInFrameRegion(const Offset & localOffset) const6621 bool TextPattern::IsLocationInFrameRegion(const Offset& localOffset) const
6622 {
6623 auto host = GetHost();
6624 CHECK_NULL_RETURN(host, false);
6625 auto geometryNode = host->GetGeometryNode();
6626 CHECK_NULL_RETURN(geometryNode, false);
6627 auto frameSize = geometryNode->GetFrameSize();
6628 auto frameRect = RectF(OffsetF(0.0f, 0.0f), frameSize);
6629 return frameRect.IsInRegion(PointF(localOffset.GetX(), localOffset.GetY()));
6630 }
6631
RegisterFormVisibleChangeCallback()6632 void TextPattern::RegisterFormVisibleChangeCallback()
6633 {
6634 if (hasRegisterFormVisibleCallback_ || !IsMarqueeOverflow()) {
6635 return;
6636 }
6637 auto host = GetHost();
6638 CHECK_NULL_VOID(host);
6639 auto pipeline = host->GetContext();
6640 CHECK_NULL_VOID(pipeline);
6641 auto isFormRender = pipeline->IsFormRender();
6642 auto formMgr = pipeline->GetFormVisibleManager();
6643 if (!isFormRender || !formMgr) {
6644 return;
6645 }
6646 auto formCallback = [weak = WeakClaim(this)](bool visible) {
6647 auto textPattern = weak.Upgrade();
6648 CHECK_NULL_VOID(textPattern);
6649 textPattern->HandleFormVisibleChange(visible);
6650 };
6651 formMgr->AddFormVisibleChangeNode(host, formCallback);
6652 hasRegisterFormVisibleCallback_ = true;
6653 }
6654
RegisterVisibleAreaChangeCallback()6655 void TextPattern::RegisterVisibleAreaChangeCallback()
6656 {
6657 auto host = GetHost();
6658 CHECK_NULL_VOID(host);
6659 if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
6660 return;
6661 }
6662 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
6663 CHECK_NULL_VOID(layoutProperty);
6664 bool isLoopAnimation = layoutProperty->GetIsLoopAnimation();
6665 auto pipeline = GetContext();
6666 CHECK_NULL_VOID(pipeline);
6667 if (isLoopAnimation && !isRegisteredAreaCallback_) {
6668 isRegisteredAreaCallback_ = true;
6669 auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
6670 auto pattern = weak.Upgrade();
6671 CHECK_NULL_VOID(pattern);
6672 pattern->OnVisibleChange(visible);
6673 };
6674 std::vector<double> ratioList = {0.0};
6675 pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
6676 } else if (!isLoopAnimation && isRegisteredAreaCallback_) {
6677 isRegisteredAreaCallback_ = false;
6678 pipeline->RemoveVisibleAreaChangeNode(host->GetId());
6679 host->CleanVisibleAreaInnerCallback();
6680 } else {
6681 return;
6682 }
6683 }
6684
RemoveFormVisibleChangeCallback(int32_t id)6685 void TextPattern::RemoveFormVisibleChangeCallback(int32_t id)
6686 {
6687 if (!hasRegisterFormVisibleCallback_) {
6688 return;
6689 }
6690 auto pipeline = pipeline_.Upgrade();
6691 CHECK_NULL_VOID(pipeline);
6692 auto formMgr = pipeline->GetFormVisibleManager();
6693 CHECK_NULL_VOID(formMgr);
6694 formMgr->RemoveFormVisibleChangeNode(id);
6695 }
6696
HandleFormVisibleChange(bool visible)6697 void TextPattern::HandleFormVisibleChange(bool visible)
6698 {
6699 if (!IsMarqueeOverflow() || !contentMod_) {
6700 return;
6701 }
6702 if (visible) {
6703 contentMod_->ResumeAnimation();
6704 } else {
6705 contentMod_->PauseAnimation();
6706 }
6707 }
6708
6709 #define DEFINE_PROP_HANDLER(KEY_TYPE, VALUE_TYPE, UPDATE_METHOD) \
6710 { \
6711 #KEY_TYPE, [](TextLayoutProperty* prop, RefPtr<PropertyValueBase> value) { \
6712 if (auto realValue = std::get_if<VALUE_TYPE>(&(value->GetValue()))) { \
6713 prop->UPDATE_METHOD(*realValue); \
6714 } \
6715 \
6716 } \
6717 } \
6718
UpdatePropertyImpl(const std::string & key,RefPtr<PropertyValueBase> value)6719 void TextPattern::UpdatePropertyImpl(const std::string& key, RefPtr<PropertyValueBase> value)
6720 {
6721 auto frameNode = GetHost();
6722 CHECK_NULL_VOID(frameNode);
6723 auto property = frameNode->GetLayoutPropertyPtr<TextLayoutProperty>();
6724 CHECK_NULL_VOID(property);
6725 CHECK_NULL_VOID(value);
6726 using Handler = std::function<void(TextLayoutProperty*, RefPtr<PropertyValueBase>)>;
6727 const std::unordered_map<std::string, Handler> handlers = {
6728 DEFINE_PROP_HANDLER(FontSize, CalcDimension, UpdateFontSize),
6729 DEFINE_PROP_HANDLER(TextIndent, CalcDimension, UpdateTextIndent),
6730 DEFINE_PROP_HANDLER(MinFontScale, float, UpdateMinFontScale),
6731 DEFINE_PROP_HANDLER(MaxFontScale, float, UpdateMaxFontScale),
6732 DEFINE_PROP_HANDLER(LineHeight, CalcDimension, UpdateLineHeight),
6733 DEFINE_PROP_HANDLER(LineSpacing, CalcDimension, UpdateLineSpacing),
6734 DEFINE_PROP_HANDLER(LetterSpacing, CalcDimension, UpdateLetterSpacing),
6735 DEFINE_PROP_HANDLER(AdaptMaxFontSize, CalcDimension, UpdateAdaptMaxFontSize),
6736 DEFINE_PROP_HANDLER(AdaptMinFontSize, CalcDimension, UpdateAdaptMinFontSize),
6737 DEFINE_PROP_HANDLER(BaselineOffset, CalcDimension, UpdateBaselineOffset),
6738 DEFINE_PROP_HANDLER(TextCaretColor, Color, UpdateCursorColor),
6739 DEFINE_PROP_HANDLER(TextDecorationColor, Color, UpdateTextDecorationColor),
6740 DEFINE_PROP_HANDLER(Content, std::u16string, UpdateContent),
6741 DEFINE_PROP_HANDLER(FontFamily, std::vector<std::string>, UpdateFontFamily),
6742
6743 {"SelectedBackgroundColor", [wp = WeakClaim(RawPtr(frameNode))](
6744 TextLayoutProperty* prop, RefPtr<PropertyValueBase> value) {
6745 if (auto realValue = std::get_if<Color>(&(value->GetValue()))) {
6746 if (realValue->GetAlpha() == 255) {
6747 *realValue = realValue->ChangeOpacity(0.2);
6748 }
6749 prop->UpdateSelectedBackgroundColor(*realValue);
6750 }
6751 }
6752 },
6753
6754 { "TextColor",
6755 [node = WeakClaim(RawPtr((frameNode))), weak = WeakClaim(this)](
6756 TextLayoutProperty* prop, RefPtr<PropertyValueBase> value) {
6757 if (auto realValue = std::get_if<Color>(&(value->GetValue()))) {
6758 prop->UpdateTextColorByRender(*realValue);
6759 auto frameNode = node.Upgrade();
6760 CHECK_NULL_VOID(frameNode);
6761 auto pattern = weak.Upgrade();
6762 CHECK_NULL_VOID(pattern);
6763 ACE_UPDATE_NODE_RENDER_CONTEXT(ForegroundColor, *realValue, frameNode);
6764 ACE_RESET_NODE_RENDER_CONTEXT(RenderContext, ForegroundColorStrategy, frameNode);
6765 ACE_UPDATE_NODE_RENDER_CONTEXT(ForegroundColorFlag, true, frameNode);
6766 pattern->UpdateFontColor(*realValue);
6767 }
6768 }
6769 },
6770 DEFINE_PROP_HANDLER(LetterSpacing, CalcDimension, UpdateLetterSpacing),
6771 };
6772 auto it = handlers.find(key);
6773 if (it != handlers.end()) {
6774 it->second(property, value);
6775 }
6776 if (frameNode->GetRerenderable()) {
6777 frameNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
6778 }
6779 }
6780 } // namespace OHOS::Ace::NG
6781