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 <iterator>
20 #include <stack>
21 #include <string>
22
23 #include "adapter/ohos/capability/clipboard/clipboard_impl.h"
24 #include "base/geometry/ng/offset_t.h"
25 #include "base/geometry/ng/point_t.h"
26 #include "base/geometry/ng/rect_t.h"
27 #include "base/geometry/offset.h"
28 #include "base/log/dump_log.h"
29 #include "base/log/log_wrapper.h"
30 #include "base/utils/utf_helper.h"
31 #include "base/utils/string_utils.h"
32 #include "base/utils/utils.h"
33 #include "base/window/drag_window.h"
34 #include "core/common/ace_engine_ext.h"
35 #include "core/common/ai/data_detector_mgr.h"
36 #include "core/common/container.h"
37 #include "core/common/container_scope.h"
38 #include "core/common/font_manager.h"
39 #include "core/common/recorder/node_data_cache.h"
40 #include "core/common/udmf/udmf_client.h"
41 #include "core/common/vibrator/vibrator_utils.h"
42 #include "core/components/common/properties/text_style_parser.h"
43 #include "core/components_ng/gestures/recognizers/gesture_recognizer.h"
44 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
45 #include "core/text/text_emoji_processor.h"
46 #ifdef ENABLE_ROSEN_BACKEND
47 #include "core/components/custom_paint/rosen_render_custom_paint.h"
48 #endif
49
50 namespace OHOS::Ace::NG {
51 namespace {
52 constexpr double DIMENSION_VALUE = 16.0;
53 constexpr char COPY[] = "copy";
54 constexpr char SELECT_TEXT[] = "selectText";
55 constexpr const char SYMBOL_COLOR[] = "BLACK";
56 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
57 const std::u16string SYMBOL_TRANS = u"\uF0001";
58 const std::u16string WIDE_NEWLINE = u"\n";
59 constexpr float RICH_DEFAULT_SHADOW_COLOR = 0x33000000;
60 constexpr float RICH_DEFAULT_ELEVATION = 120.0f;
61 constexpr Dimension CLICK_THRESHOLD = 5.0_vp;
62 const OffsetF DEFAULT_NEGATIVE_CARET_OFFSET {-1.0f, -1.0f};
63
IsJumpLink(const std::string & content)64 bool IsJumpLink(const std::string& content)
65 {
66 // start with http:// or https://
67 std::regex pattern(R"(https?://[^\s]+)");
68 return std::regex_match(content, pattern);
69 }
70 }; // namespace
71
~TextPattern()72 TextPattern::~TextPattern()
73 {
74 // node destruct, need to stop text race animation
75 CHECK_NULL_VOID(contentMod_);
76 contentMod_->StopTextRace();
77 }
78
OnWindowHide()79 void TextPattern::OnWindowHide()
80 {
81 if (magnifierController_) {
82 magnifierController_->RemoveMagnifierFrameNode();
83 }
84 CHECK_NULL_VOID(contentMod_);
85 contentMod_->PauseAnimation();
86 auto host = GetHost();
87 CHECK_NULL_VOID(host);
88 TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowHide [%{public}d]", host->GetId());
89 PauseSymbolAnimation();
90 }
91
OnWindowShow()92 void TextPattern::OnWindowShow()
93 {
94 CHECK_NULL_VOID(contentMod_);
95 contentMod_->ResumeAnimation();
96 auto host = GetHost();
97 CHECK_NULL_VOID(host);
98 TAG_LOGD(AceLogTag::ACE_TEXT, "OnWindowShow [%{public}d]", host->GetId());
99 ResumeSymbolAnimation();
100 }
101
OnAttachToFrameNode()102 void TextPattern::OnAttachToFrameNode()
103 {
104 auto host = GetHost();
105 CHECK_NULL_VOID(host);
106 auto pipeline = host->GetContext();
107 CHECK_NULL_VOID(pipeline);
108 pipeline_ = pipeline;
109 auto fontManager = pipeline->GetFontManager();
110 if (fontManager) {
111 fontManager->AddFontNodeNG(host);
112 }
113 if (host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
114 if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
115 host->GetRenderContext()->UpdateClipEdge(true);
116 host->GetRenderContext()->SetClipToFrame(true);
117 }
118 }
119 InitSurfaceChangedCallback();
120 InitSurfacePositionChangedCallback();
121 pipeline->AddWindowStateChangedCallback(host->GetId());
122 pipeline->AddWindowSizeChangeCallback(host->GetId());
123 if (host->GetTag() == V2::SYMBOL_ETS_TAG) {
124 ProcessVisibleAreaCallback();
125 }
126 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
127 CHECK_NULL_VOID(textLayoutProperty);
128 auto theme = pipeline->GetTheme<TextTheme>();
129 CHECK_NULL_VOID(theme);
130 textLayoutProperty->UpdateTextAlign(theme->GetTextStyle().GetTextAlign());
131 textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
132 }
133
OnDetachFromFrameNode(FrameNode * node)134 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
135 {
136 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
137 CloseSelectOverlay();
138 auto pipeline = pipeline_.Upgrade();
139 CHECK_NULL_VOID(pipeline);
140 if (HasSurfaceChangedCallback()) {
141 pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
142 }
143 if (HasSurfacePositionChangedCallback()) {
144 pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
145 }
146 auto frameNode = WeakClaim(node);
147 pipeline->RemoveFontNodeNG(frameNode);
148 auto fontManager = pipeline->GetFontManager();
149 if (fontManager) {
150 fontManager->UnRegisterCallbackNG(frameNode);
151 fontManager->RemoveVariationNodeNG(frameNode);
152 }
153 pipeline->RemoveOnAreaChangeNode(node->GetId());
154 pipeline->RemoveWindowStateChangedCallback(node->GetId());
155 pipeline->RemoveVisibleAreaChangeNode(node->GetId());
156 pipeline->RemoveWindowSizeChangeCallback(node->GetId());
157 }
158
CloseSelectOverlay()159 void TextPattern::CloseSelectOverlay()
160 {
161 CloseSelectOverlay(false);
162 }
163
CloseSelectOverlay(bool animation)164 void TextPattern::CloseSelectOverlay(bool animation)
165 {
166 // Deprecated use selectOverlay_ instead.
167 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
168 selectOverlayProxy_->Close(animation);
169 RemoveAreaChangeInner();
170 }
171 selectOverlay_->CloseOverlay(animation, CloseReason::CLOSE_REASON_NORMAL);
172 }
173
ResetSelection()174 void TextPattern::ResetSelection()
175 {
176 if (textSelector_.IsValid() && !shiftFlag_) {
177 HandleSelectionChange(-1, -1);
178 auto host = GetHost();
179 CHECK_NULL_VOID(host);
180 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
181 }
182 }
183
InitSelection(const Offset & pos)184 void TextPattern::InitSelection(const Offset& pos)
185 {
186 CHECK_NULL_VOID(pManager_);
187 auto selectionOffset = pos;
188 if (GreatNotEqual(selectionOffset.GetY(), pManager_->GetHeight())) {
189 selectionOffset.SetX(contentRect_.Width());
190 selectionOffset.SetY(pManager_->GetHeight());
191 }
192 int32_t extend = pManager_->GetGlyphIndexByCoordinate(selectionOffset, true);
193 if (pManager_->GetParagraphs().size() > 1) {
194 // paragraph may contain only newlines, look forward for non-newlines characters.
195 auto selectRects = pManager_->GetRects(extend, extend + 1);
196 if (selectRects.size() == 1 && NearZero(selectRects.back().Width())) {
197 auto selectStr = GetSelectedText(extend, extend + 1);
198 while (selectStr == u"\n" && extend > 0) {
199 --extend;
200 selectStr = GetSelectedText(extend, extend + 1);
201 }
202 }
203 }
204 int32_t start = 0;
205 int32_t end = 0;
206 if (!pManager_->GetWordBoundary(extend, start, end)) {
207 start = extend;
208 end = std::min(static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_,
209 extend + GetGraphemeClusterLength(textForDisplay_, extend));
210 }
211 HandleSelectionChange(start, end);
212 }
213
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)214 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
215 {
216 auto host = GetHost();
217 CHECK_NULL_VOID(host);
218 auto rect = host->GetGeometryNode()->GetFrameRect();
219 CHECK_NULL_VOID(pManager_);
220 auto computeSuccess = pManager_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
221 if (!computeSuccess) {
222 caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Height()), 0.0f);
223 }
224 }
225
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)226 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
227 {
228 parentGlobalOffset_ = GetParentGlobalOffset();
229 auto textContentGlobalOffset = selectOverlay_->GetHandleGlobalOffset() + contentRect_.GetOffset();
230 auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
231
232 // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
233 CaretMetricsF firstHandleMetrics;
234 CaretMetricsF secondHandleMetrics;
235 CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
236 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
237 OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
238 OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
239
240 textSelector_.selectionBaseOffset = firstHandleOffset;
241 textSelector_.selectionDestinationOffset = secondHandleOffset;
242
243 RectF firstHandle;
244 firstHandle.SetOffset(firstHandleOffset);
245 firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
246 firstHandle.SetOffset(OffsetF(firstHandle.GetX() - firstHandle.Width() / 2.0f, firstHandle.GetY()));
247 textSelector_.firstHandle = firstHandle;
248
249 RectF secondHandle;
250 secondHandle.SetOffset(secondHandleOffset);
251 secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
252 secondHandle.SetHeight(secondHandleMetrics.height);
253 secondHandle.SetOffset(OffsetF(secondHandle.GetX() - secondHandle.Width() / 2.0f, secondHandle.GetY()));
254 textSelector_.secondHandle = secondHandle;
255 }
256
GetSpansInfoInStyledString(int32_t start,int32_t end)257 std::list<ResultObject> TextPattern::GetSpansInfoInStyledString(int32_t start, int32_t end)
258 {
259 std::list<ResultObject> resultObjects;
260 int32_t imageIndex = 0;
261 for (const auto& item : spans_) {
262 auto obj = item->GetSpanResultObject(start, end);
263 if (obj.type == SelectSpanType::TYPEIMAGE) {
264 obj.spanPosition.spanIndex = imageIndex;
265 ++imageIndex;
266 }
267 if (obj.isInit) {
268 resultObjects.emplace_back(obj);
269 }
270 }
271 return resultObjects;
272 }
273
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)274 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
275 {
276 int32_t index = 0;
277 std::int32_t realEnd = 0;
278 std::int32_t realStart = 0;
279 SelectionInfo selection;
280 std::list<ResultObject> resultObjects;
281 auto length = GetTextContentLength();
282 if (method == GetSpansMethod::GETSPANS) {
283 realStart = (start == -1) ? 0 : start;
284 realEnd = (end == -1) ? length : end;
285 if (realStart > realEnd) {
286 std::swap(realStart, realEnd);
287 }
288 realStart = std::max(0, realStart);
289 realEnd = std::min(length, realEnd);
290 } else if (method == GetSpansMethod::ONSELECT) {
291 realEnd = std::min(length, end);
292 realStart = std::min(length, start);
293 }
294 selection.SetSelectionEnd(realEnd);
295 selection.SetSelectionStart(realStart);
296 // Verify that realStart, realEnd, and spans_ are valid
297 if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
298 (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
299 selection.SetResultObjectList(resultObjects);
300 return selection;
301 }
302 if (isSpanStringMode_) {
303 auto result = GetSpansInfoInStyledString(realStart, realEnd);
304 selection.SetResultObjectList(result);
305 return selection;
306 }
307 const auto& children = GetAllChildren();
308 for (const auto& uinode : children) {
309 if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
310 ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
311 if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
312 resultObjects.emplace_back(resultObject);
313 }
314 } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
315 ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
316 if (!resultObject.valueString.empty()) {
317 resultObjects.emplace_back(resultObject);
318 }
319 } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
320 ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
321 if (!resultObject.valueString.empty()) {
322 resultObjects.emplace_back(resultObject);
323 }
324 } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG ||
325 uinode->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
326 ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
327 if (!resultObject.valueString.empty()) {
328 resultObjects.emplace_back(resultObject);
329 }
330 }
331 index++;
332 }
333 selection.SetResultObjectList(resultObjects);
334 return selection;
335 }
336
GetTextContentLength()337 int32_t TextPattern::GetTextContentLength()
338 {
339 if (!spans_.empty()) {
340 return static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
341 }
342 return 0;
343 }
344
StartVibratorByLongPress()345 void TextPattern::StartVibratorByLongPress()
346 {
347 CHECK_NULL_VOID(isEnableHapticFeedback_);
348 VibratorUtils::StartVibraFeedback("longPress.light");
349 }
350
HandleLongPress(GestureEvent & info)351 void TextPattern::HandleLongPress(GestureEvent& info)
352 {
353 HandleSpanLongPressEvent(info);
354 if (!IsSelectableAndCopy() || isMousePressed_ || selectOverlay_->GetIsHandleDragging()) {
355 return;
356 }
357 auto host = GetHost();
358 CHECK_NULL_VOID(host);
359 auto hub = host->GetEventHub<EventHub>();
360 CHECK_NULL_VOID(hub);
361 auto gestureHub = hub->GetOrCreateGestureEventHub();
362 CHECK_NULL_VOID(gestureHub);
363 auto localOffset = info.GetLocalLocation();
364 if (selectOverlay_->HasRenderTransform()) {
365 localOffset = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
366 }
367
368 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
369 if ((textLayoutProperty && textLayoutProperty->GetMaxLines() != 0) && textForDisplay_.length() != 0) {
370 StartVibratorByLongPress();
371 }
372
373 if (IsDraggable(localOffset)) {
374 // prevent long press event from being triggered when dragging
375 gestureHub->SetIsTextDraggable(true);
376 return;
377 }
378 gestureHub->SetIsTextDraggable(false);
379 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
380 Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() };
381 InitSelection(textOffset);
382 textResponseType_ = TextResponseType::LONG_PRESS;
383 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
384 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
385 oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
386 parentGlobalOffset_ = GetParentGlobalOffset();
387 CalculateHandleOffsetAndShowOverlay();
388 CloseSelectOverlay(true);
389 if (GetOrCreateMagnifier() && HasContent()) {
390 magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
391 }
392 StartGestureSelection(textSelector_.GetStart(), textSelector_.GetEnd(), localOffset);
393 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
394 }
395
ShowShadow(const PointF & textOffset,const Color & color)396 bool TextPattern::ShowShadow(const PointF& textOffset, const Color& color)
397 {
398 CHECK_NULL_RETURN(overlayMod_, false);
399 CHECK_NULL_RETURN(hasUrlSpan_, false);
400 CHECK_NULL_RETURN(!spans_.empty() && pManager_, false);
401 int32_t start = 0;
402 for (const auto& item : spans_) {
403 if (!item) {
404 continue;
405 }
406 auto selectedRects = GetSelectedRects(start, item->position);
407 for (auto&& rect : selectedRects) {
408 if (!rect.IsInRegion(textOffset)) {
409 continue;
410 }
411 if (!item->urlOnRelease) {
412 overlayMod_->ClearSelectedForegroundColorAndRects();
413 MarkDirtySelf();
414 return false;
415 }
416 auto inter = GetStartAndEnd(start, item);
417 auto rects = GetSelectedRects(inter.first, inter.second);
418 overlayMod_->SetSelectedForegroundColorAndRects(rects, color.GetValue());
419 MarkDirtySelf();
420 return true;
421 }
422 start = item->position;
423 }
424 overlayMod_->ClearSelectedForegroundColorAndRects();
425 MarkDirtySelf();
426 return false;
427 }
428
GetStartAndEnd(int32_t start,const RefPtr<SpanItem> & spanItem)429 std::pair<int32_t, int32_t> TextPattern::GetStartAndEnd(int32_t start, const RefPtr<SpanItem>& spanItem)
430 {
431 auto spanBases = styledString_->GetSpans(0, styledString_->GetLength(), SpanType::Url);
432 for (const auto& spanBase : spanBases) {
433 if (start >= spanBase->GetStartIndex() && start < spanBase->GetEndIndex()) {
434 return {spanBase->GetStartIndex(), spanBase->GetEndIndex()};
435 }
436 }
437 return {0, 0};
438 }
439
HandleSpanLongPressEvent(GestureEvent & info)440 void TextPattern::HandleSpanLongPressEvent(GestureEvent& info)
441 {
442 RectF textContentRect = contentRect_;
443 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
444 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
445
446 auto localLocation = info.GetLocalLocation();
447 if (selectOverlay_->HasRenderTransform()) {
448 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
449 }
450
451 auto host = GetHost();
452 CHECK_NULL_VOID(host);
453 auto renderContext = host->GetRenderContext();
454 CHECK_NULL_VOID(renderContext);
455 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
456 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
457 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
458 textContentRect = overlayMod_->GetBoundsRect();
459 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
460 }
461 auto longPressFunc = [](RefPtr<SpanItem> item, GestureEvent& info, const RectF& rect,
462 const PointF& textOffset) -> bool {
463 if (rect.IsInRegion(textOffset)) {
464 if (item && item->onLongPress) {
465 item->onLongPress(info);
466 }
467 return true;
468 }
469 return false;
470 };
471
472 if (textContentRect.IsInRegion(
473 PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
474 !spans_.empty() && pManager_) {
475 int32_t start = 0;
476 for (const auto& item : spans_) {
477 if (!item) {
478 continue;
479 }
480 auto selectedRects = GetSelectedRects(start, item->position);
481 for (auto&& rect : selectedRects) {
482 CHECK_NULL_VOID(!longPressFunc(item, info, rect, textOffset));
483 }
484 start = item->position;
485 }
486 }
487 }
488
489 // Deprecated: Use the TextSelectOverlay::OnHandleMove() instead.
490 // It is currently used by RichEditorPattern.
OnHandleMove(const RectF & handleRect,bool isFirstHandle)491 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
492 {
493 auto host = GetHost();
494 CHECK_NULL_VOID(host);
495 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
496 auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
497
498 auto localOffset = handleRect.GetOffset();
499
500 auto renderContext = host->GetRenderContext();
501 CHECK_NULL_VOID(renderContext);
502 auto clip = false;
503 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
504 clip = true;
505 }
506 if (renderContext->GetClipEdge().value_or(clip)) {
507 if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
508 localOffset.SetX(textContentGlobalOffset.GetX());
509 } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
510 localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
511 }
512
513 if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
514 localOffset.SetY(textContentGlobalOffset.GetY());
515 } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
516 localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
517 }
518 }
519
520 localOffset -= textPaintOffset;
521
522 CHECK_NULL_VOID(pManager_);
523 // the handle position is calculated based on the middle of the handle height.
524 UpdateSelectorOnHandleMove(localOffset, handleRect.Height(), isFirstHandle);
525 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
526
527 CHECK_NULL_VOID(selectOverlayProxy_);
528 auto start = textSelector_.GetTextStart();
529 auto end = textSelector_.GetTextEnd();
530 selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
531 }
532
UpdateSelectorOnHandleMove(const OffsetF & localOffset,float handleHeight,bool isFirstHandle)533 void TextPattern::UpdateSelectorOnHandleMove(const OffsetF& localOffset, float handleHeight, bool isFirstHandle)
534 {
535 if (isFirstHandle) {
536 auto start = GetHandleIndex(Offset(
537 localOffset.GetX(), localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() ? handleHeight : 0)));
538 HandleSelectionChange(start, textSelector_.destinationOffset);
539 } else {
540 auto end = GetHandleIndex(Offset(localOffset.GetX(),
541 localOffset.GetY() +
542 (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0) ? 0 : handleHeight)));
543 HandleSelectionChange(textSelector_.baseOffset, end);
544 }
545 }
546
IsSelectAll()547 bool TextPattern::IsSelectAll()
548 {
549 return textSelector_.GetTextStart() == 0 &&
550 textSelector_.GetTextEnd() == static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
551 }
552
GetSelectedText(int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const553 std::u16string TextPattern::GetSelectedText(int32_t start, int32_t end, bool includeStartHalf,
554 bool includeEndHalf, bool getSubstrDirectly) const
555 {
556 if (spans_.empty()) {
557 auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(textForDisplay_.length()));
558 auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(textForDisplay_.length())), 0,
559 static_cast<int32_t>(textForDisplay_.length()));
560 if (max - min < 0) {
561 return std::u16string();
562 }
563 if (getSubstrDirectly) {
564 return textForDisplay_.substr(min, max - min);
565 } else {
566 return TextEmojiProcessor::SubU16string(min, max - min, textForDisplay_, includeStartHalf, includeEndHalf);
567 }
568 }
569 std::u16string value;
570 int32_t tag = 0;
571 for (const auto& span : spans_) {
572 if (span->GetSymbolUnicode() != 0) {
573 tag = span->position == -1 ? tag + 1 : span->position;
574 continue;
575 }
576 if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
577 auto wideString = span->GetSpanContent();
578 auto max = std::min(span->position, end);
579 auto min = std::max(start, tag);
580 if (getSubstrDirectly) {
581 value += wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
582 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())));
583 } else {
584 value += TextEmojiProcessor::SubU16string(
585 std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
586 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length())),
587 wideString, includeStartHalf, includeEndHalf);
588 }
589 } else if (span->position - 1 >= start && span->position != -1) {
590 // image span or custom span (span->placeholderIndex != -1)
591 value += u" ";
592 }
593 tag = span->position == -1 ? tag + 1 : span->position;
594 if (span->position >= end) {
595 break;
596 }
597 }
598 return value;
599 }
600
HandleOnCopy()601 void TextPattern::HandleOnCopy()
602 {
603 CHECK_NULL_VOID(clipboard_);
604 if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
605 HandleSelectionChange(-1, -1);
606 return;
607 }
608 auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), false, false, true);
609 if (IsSelectableAndCopy() || dataDetectorAdapter_->hasClickedMenuOption_) {
610 if (isSpanStringMode_ && !externalParagraph_) {
611 HandleOnCopySpanString();
612 } else if (!value.empty()) {
613 HandleOnCopyWithoutSpanString(UtfUtils::Str16DebugToStr8(value));
614 }
615 }
616 HiddenMenu();
617 CHECK_NULL_VOID(!value.empty());
618 auto host = GetHost();
619 CHECK_NULL_VOID(host);
620 auto eventHub = host->GetEventHub<TextEventHub>();
621 CHECK_NULL_VOID(eventHub);
622 eventHub->FireOnCopy(value);
623 }
624
HandleOnCopySpanString()625 void TextPattern::HandleOnCopySpanString()
626 {
627 auto subSpanString = styledString_->GetSubSpanString(textSelector_.GetTextStart(),
628 textSelector_.GetTextEnd() - textSelector_.GetTextStart());
629 #if defined(PREVIEW)
630 clipboard_->SetData(subSpanString->GetString(), copyOption_);
631 return;
632 #endif
633 RefPtr<PasteDataMix> pasteData = clipboard_->CreatePasteDataMix();
634 std::vector<uint8_t> tlvData;
635 subSpanString->EncodeTlv(tlvData);
636 clipboard_->AddSpanStringRecord(pasteData, tlvData);
637 clipboard_->AddTextRecord(pasteData, subSpanString->GetString());
638 clipboard_->SetData(pasteData, copyOption_);
639 }
640
HandleOnCopyWithoutSpanString(const std::string & pasteData)641 void TextPattern::HandleOnCopyWithoutSpanString(const std::string& pasteData)
642 {
643 #if defined(PREVIEW)
644 clipboard_->SetData(pasteData, copyOption_);
645 return;
646 #endif
647 RefPtr<PasteDataMix> pasteDataMix = clipboard_->CreatePasteDataMix();
648 auto multiTypeRecordImpl = AceType::MakeRefPtr<MultiTypeRecordImpl>();
649 if (spans_.empty()) {
650 EncodeTlvNoChild(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
651 } else {
652 EncodeTlvSpanItems(pasteData, multiTypeRecordImpl->GetSpanStringBuffer());
653 }
654 multiTypeRecordImpl->SetPlainText(pasteData);
655 clipboard_->AddMultiTypeRecord(pasteDataMix, multiTypeRecordImpl);
656 clipboard_->SetData(pasteDataMix, copyOption_);
657 }
658
659 #define WRITE_TLV_INHERIT(group, name, tag, type, inheritName) \
660 if ((group)->Has##name()) { \
661 TLVUtil::WriteUint8(buff, (tag)); \
662 TLVUtil::Write##type(buff, (group)->prop##name.value()); \
663 } else if (textStyle_.has_value()) { \
664 auto temp##name = textStyle_->Get##inheritName(); \
665 TLVUtil::WriteUint8(buff, (tag)); \
666 TLVUtil::Write##type(buff, temp##name); \
667 }
668
669 #define WRITE_TEXT_STYLE_TLV(group, name, tag, type) \
670 do { \
671 if ((group)->Has##name()) { \
672 TLVUtil::WriteUint8(buff, (tag)); \
673 TLVUtil::Write##type(buff, (group)->prop##name.value()); \
674 } \
675 } while (false)
676
EncodeTlvNoChild(const std::string & pasteData,std::vector<uint8_t> & buff)677 void TextPattern::EncodeTlvNoChild(const std::string& pasteData, std::vector<uint8_t>& buff)
678 {
679 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
680 TLVUtil::WriteInt32(buff, 1);
681
682 TLVUtil::WriteInt32(buff, static_cast<int32_t>(NG::SpanItemType::NORMAL));
683 TLVUtil::WriteUint8(buff, TLV_SPANITEM_TAG);
684 TLVUtil::WriteInt32(buff, 0);
685 TLVUtil::WriteInt32(buff, pasteData.length());
686 TLVUtil::WriteString(buff, pasteData);
687 EncodeTlvFontStyleNoChild(buff);
688 EncodeTlvTextLineStyleNoChild(buff);
689 TLVUtil::WriteUint8(buff, TLV_SPANITEM_END_TAG);
690
691 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
692 TLVUtil::WriteString(buff, pasteData);
693 TLVUtil::WriteUint8(buff, TLV_END);
694 }
695
EncodeTlvFontStyleNoChild(std::vector<uint8_t> & buff)696 void TextPattern::EncodeTlvFontStyleNoChild(std::vector<uint8_t>& buff)
697 {
698 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
699 CHECK_NULL_VOID(textLayoutProperty);
700 auto& fontStyle = textLayoutProperty->GetFontStyle();
701 CHECK_NULL_VOID(fontStyle);
702 WRITE_TLV_INHERIT(fontStyle, FontSize, TLV_SPAN_FONT_STYLE_FONTSIZE, Dimension, FontSize);
703 WRITE_TLV_INHERIT(fontStyle, TextColor, TLV_SPAN_FONT_STYLE_TEXTCOLOR, Color, TextColor);
704 WRITE_TLV_INHERIT(fontStyle, TextShadow, TLV_SPAN_FONT_STYLE_TEXTSHADOW, TextShadows, TextShadows);
705 WRITE_TLV_INHERIT(fontStyle, ItalicFontStyle, TLV_SPAN_FONT_STYLE_ITALICFONTSTYLE, FontStyle, FontStyle);
706 WRITE_TLV_INHERIT(fontStyle, FontWeight, TLV_SPAN_FONT_STYLE_FONTWEIGHT, FontWeight, FontWeight);
707 WRITE_TLV_INHERIT(fontStyle, FontFamily, TLV_SPAN_FONT_STYLE_FONTFAMILY, FontFamily, FontFamilies);
708 WRITE_TLV_INHERIT(fontStyle, FontFeature, TLV_SPAN_FONT_STYLE_FONTFEATURE, FontFeature, FontFeatures);
709 WRITE_TLV_INHERIT(fontStyle, TextDecoration, TLV_SPAN_FONT_STYLE_TEXTDECORATION, TextDecoration, TextDecoration);
710 WRITE_TLV_INHERIT(
711 fontStyle, TextDecorationColor, TLV_SPAN_FONT_STYLE_TEXTDECORATIONCOLOR, Color, TextDecorationColor);
712 WRITE_TLV_INHERIT(fontStyle, TextDecorationStyle, TLV_SPAN_FONT_STYLE_TEXTDECORATIONSTYLE, TextDecorationStyle,
713 TextDecorationStyle);
714 WRITE_TLV_INHERIT(fontStyle, TextCase, TLV_SPAN_FONT_STYLE_TEXTCASE, TextCase, TextCase);
715 WRITE_TLV_INHERIT(fontStyle, AdaptMinFontSize, TLV_SPAN_FONT_STYLE_ADPATMINFONTSIZE, Dimension, AdaptMinFontSize);
716 WRITE_TLV_INHERIT(fontStyle, AdaptMaxFontSize, TLV_SPAN_FONT_STYLE_ADPATMAXFONTSIZE, Dimension, AdaptMaxFontSize);
717 WRITE_TLV_INHERIT(fontStyle, LetterSpacing, TLV_SPAN_FONT_STYLE_LETTERSPACING, Dimension, LetterSpacing);
718 }
719
EncodeTlvTextLineStyleNoChild(std::vector<uint8_t> & buff)720 void TextPattern::EncodeTlvTextLineStyleNoChild(std::vector<uint8_t>& buff)
721 {
722 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
723 CHECK_NULL_VOID(textLayoutProperty);
724 auto& textLineStyle = textLayoutProperty->GetTextLineStyle();
725 CHECK_NULL_VOID(textLineStyle);
726 WRITE_TLV_INHERIT(textLineStyle, LineHeight, TLV_SPAN_TEXT_LINE_STYLE_LINEHEIGHT, Dimension, LineHeight);
727 WRITE_TLV_INHERIT(textLineStyle, LineSpacing, TLV_SPAN_TEXT_LINE_STYLE_LINESPACING, Dimension, LineSpacing);
728 WRITE_TLV_INHERIT(textLineStyle, TextBaseline, TLV_SPAN_TEXT_LINE_STYLE_TEXTBASELINE, TextBaseline, TextBaseline);
729 WRITE_TLV_INHERIT(textLineStyle, TextOverflow, TLV_SPAN_TEXT_LINE_STYLE_TEXTOVERFLOW, TextOverflow, TextOverflow);
730 WRITE_TLV_INHERIT(textLineStyle, TextAlign, TLV_SPAN_TEXT_LINE_STYLE_TEXTALIGN, TextAlign, TextAlign);
731 WRITE_TEXT_STYLE_TLV(textLineStyle, MaxLength, TLV_SPAN_TEXT_LINE_STYLE_MAXLENGTH, Int32);
732 WRITE_TLV_INHERIT(textLineStyle, MaxLines, TLV_SPAN_TEXT_LINE_STYLE_MAXLINES, Int32, MaxLines);
733 WRITE_TEXT_STYLE_TLV(
734 textLineStyle, HeightAdaptivePolicy, TLV_SPAN_TEXT_LINE_STYLE_HEIGHTADAPTIVEPOLICY, TextHeightAdaptivePolicy);
735 WRITE_TLV_INHERIT(textLineStyle, TextIndent, TLV_SPAN_TEXT_LINE_STYLE_TEXTINDENT, Dimension, TextIndent);
736 WRITE_TEXT_STYLE_TLV(textLineStyle, LeadingMargin, TLV_SPAN_TEXT_LINE_STYLE_LEADINGMARGIN, LeadingMargin);
737 WRITE_TLV_INHERIT(textLineStyle, WordBreak, TLV_SPAN_TEXT_LINE_STYLE_WORDBREAK, WordBreak, WordBreak);
738 WRITE_TLV_INHERIT(textLineStyle, LineBreakStrategy, TLV_SPAN_TEXT_LINE_STYLE_LINEBREAKSTRATEGY, LineBreakStrategy,
739 LineBreakStrategy);
740 WRITE_TLV_INHERIT(textLineStyle, EllipsisMode, TLV_SPAN_TEXT_LINE_STYLE_ELLIPSISMODE, EllipsisMode, EllipsisMode);
741 }
742
EncodeTlvSpanItems(const std::string & pasteData,std::vector<uint8_t> & buff)743 void TextPattern::EncodeTlvSpanItems(const std::string& pasteData, std::vector<uint8_t>& buff)
744 {
745 auto start = textSelector_.GetTextStart();
746 auto end = textSelector_.GetTextEnd();
747 std::list<RefPtr<NG::SpanItem>> selectSpanItems;
748 int32_t ignoreLength = 0;
749 for (const auto& spanItem : spans_) {
750 int32_t oldStart = spanItem->position - static_cast<int32_t>(spanItem->length);
751 int32_t oldEnd = spanItem->position;
752 if (oldEnd <= start || end <= oldStart) {
753 continue;
754 }
755 if (spanItem->spanItemType == NG::SpanItemType::SYMBOL) {
756 ignoreLength += static_cast<int32_t>(spanItem->length);
757 continue;
758 }
759 auto spanStart = oldStart <= start ? 0 : oldStart - start;
760 auto spanEnd = oldEnd < end ? oldEnd - start : end - start;
761 auto newSpanItem = spanItem->GetSameStyleSpanItem(true);
762 newSpanItem->interval = { spanStart - ignoreLength, spanEnd - ignoreLength };
763 newSpanItem->content = spanItem->content
764 .substr(std::max(start - oldStart, 0), std::min(end, oldEnd) - std::max(start, oldStart));
765 selectSpanItems.emplace_back(newSpanItem);
766 }
767
768 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_SPANS);
769 TLVUtil::WriteInt32(buff, selectSpanItems.size());
770 for (auto it = selectSpanItems.begin(); it != selectSpanItems.end(); ++it) {
771 auto spanItem = (*it);
772 if (spanItem->spanItemType == NG::SpanItemType::CustomSpan) {
773 TLVUtil::WriteInt32(buff, static_cast<int32_t>(NG::SpanItemType::NORMAL));
774 auto placeHolderSpan = AceType::MakeRefPtr<NG::SpanItem>();
775 placeHolderSpan->content = u" ";
776 placeHolderSpan->interval = spanItem->interval;
777 placeHolderSpan->EncodeTlv(buff);
778 continue;
779 }
780 TLVUtil::WriteInt32(buff, static_cast<int32_t>(spanItem->spanItemType));
781 spanItem->EncodeTlv(buff);
782 }
783 TLVUtil::WriteUint8(buff, TLV_SPAN_STRING_CONTENT);
784 TLVUtil::WriteString(buff, pasteData);
785 TLVUtil::WriteUint8(buff, TLV_END);
786 }
787
HiddenMenu()788 void TextPattern::HiddenMenu()
789 {
790 if (IsUsingMouse()) {
791 CloseSelectOverlay();
792 } else {
793 selectOverlay_->HideMenu();
794 }
795 }
796
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)797 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
798 {
799 auto host = GetHost();
800 CHECK_NULL_VOID(host);
801 auto eventHub = host->GetEventHub<EventHub>();
802 CHECK_NULL_VOID(eventHub);
803 auto context = host->GetContext();
804 if (context) {
805 context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
806 auto textPattern = weak.Upgrade();
807 CHECK_NULL_VOID(textPattern);
808 auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
809 CHECK_NULL_VOID(textLayoutProperty);
810 if (textLayoutProperty->GetCalcLayoutConstraint() &&
811 textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize.has_value()) {
812 auto selfIdealSizeWidth = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Width();
813 auto selfIdealSizeHeight = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Height();
814 auto constraint = textLayoutProperty->GetLayoutConstraint();
815 if ((selfIdealSizeWidth.has_value() && NearZero(selfIdealSizeWidth->GetDimension().ConvertToPxWithSize(
816 constraint->percentReference.Width()))) ||
817 (selfIdealSizeHeight.has_value() &&
818 NearZero(selfIdealSizeHeight->GetDimension().ConvertToPxWithSize(
819 constraint->percentReference.Height())))) {
820 return;
821 }
822 }
823
824 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
825 if (mode == TextSelectableMode::UNSELECTABLE ||
826 textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
827 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
828 return;
829 }
830 if (!textPattern->IsSetObscured() && eventHub->IsEnabled()) {
831 textPattern->ActSetSelection(selectionStart, selectionEnd);
832 }
833 });
834 }
835 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
836 }
837
GetRenderContext()838 RefPtr<RenderContext> TextPattern::GetRenderContext()
839 {
840 auto frameNode = GetHost();
841 CHECK_NULL_RETURN(frameNode, nullptr);
842 return frameNode->GetRenderContext();
843 }
844
ShowSelectOverlay(const OverlayRequest & request)845 void TextPattern::ShowSelectOverlay(const OverlayRequest& request)
846 {
847 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
848 CHECK_NULL_VOID(textLayoutProperty);
849 if (textLayoutProperty->GetMaxLines() == 0) {
850 CloseSelectOverlay();
851 ResetSelection();
852 return;
853 }
854 selectOverlay_->ProcessOverlay(request);
855 }
856
HandleOnSelectAll()857 void TextPattern::HandleOnSelectAll()
858 {
859 auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
860 HandleSelectionChange(0, textSize);
861 CalculateHandleOffsetAndShowOverlay();
862 CloseSelectOverlay(true);
863 if (IsUsingMouse()) {
864 if (IsSelected()) {
865 selectOverlay_->SetSelectionHoldCallback();
866 }
867 } else {
868 ShowSelectOverlay({ .animation = true });
869 }
870 auto host = GetHost();
871 CHECK_NULL_VOID(host);
872 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
873 ResetOriginCaretPosition();
874 }
875
IsShowTranslate()876 bool TextPattern::IsShowTranslate()
877 {
878 auto host = GetHost();
879 CHECK_NULL_RETURN(host, false);
880 auto context = host->GetContext();
881 CHECK_NULL_RETURN(context, false);
882 auto textTheme = context->GetTheme<TextTheme>();
883 CHECK_NULL_RETURN(textTheme, false);
884 return textTheme->IsShowTranslate();
885 }
886
IsShowSearch()887 bool TextPattern::IsShowSearch()
888 {
889 auto container = Container::Current();
890 if (container && container->IsScenceBoardWindow()) {
891 return false;
892 }
893 auto host = GetHost();
894 CHECK_NULL_RETURN(host, false);
895 auto context = host->GetContext();
896 CHECK_NULL_RETURN(context, false);
897 auto textTheme = context->GetTheme<TextTheme>();
898 CHECK_NULL_RETURN(textTheme, false);
899 return textTheme->IsShowSearch();
900 }
901
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)902 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
903 {
904 constexpr int32_t longPressDelay = 600;
905 if (longPressEvent_) {
906 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
907 return;
908 }
909 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
910 auto pattern = weak.Upgrade();
911 CHECK_NULL_VOID(pattern);
912 pattern->sourceType_ = info.GetSourceDevice();
913 pattern->HandleLongPress(info);
914 };
915 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
916
917 // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
918 // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
919 // be slightly longer to ensure that order.
920 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
921
922 auto onTextSelectorChange = [weak = WeakClaim(this)]() {
923 auto pattern = weak.Upgrade();
924 CHECK_NULL_VOID(pattern);
925 auto frameNode = pattern->GetHost();
926 CHECK_NULL_VOID(frameNode);
927 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
928 };
929 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
930 }
931
OnHandleTouchUp()932 void TextPattern::OnHandleTouchUp()
933 {
934 CloseSelectOverlay();
935 ResetSelection();
936 }
937
HandleClickEvent(GestureEvent & info)938 void TextPattern::HandleClickEvent(GestureEvent& info)
939 {
940 if ((selectOverlay_->IsClickAtHandle(info) && !multipleClickRecognizer_->IsRunning()) ||
941 selectOverlay_->GetIsHandleDragging()) {
942 return;
943 }
944 if (dataDetectorAdapter_->hasClickedAISpan_) {
945 dataDetectorAdapter_->hasClickedAISpan_ = false;
946 }
947 multipleClickRecognizer_->Start(info);
948 if (multipleClickRecognizer_->IsDoubleClick()) {
949 HandleDoubleClickEvent(info);
950 } else {
951 HandleSingleClickEvent(info);
952 }
953 }
954
HandleUrlClick()955 bool TextPattern::HandleUrlClick()
956 {
957 if (LessNotEqual(clickedSpanPosition_, 0)) {
958 return false;
959 }
960 auto iter = spans_.begin();
961 std::advance(iter, clickedSpanPosition_);
962 RefPtr<SpanItem> span;
963 if (iter == spans_.end()) {
964 span = spans_.back();
965 } else {
966 span = *iter;
967 }
968 if (span && span->urlOnRelease) {
969 span->urlOnRelease();
970 return true;
971 }
972 return false;
973 }
974
HandleSingleClickEvent(GestureEvent & info)975 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
976 {
977 RectF textContentRect = contentRect_;
978 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
979 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
980 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
981 info.GetLocalLocation().GetY() - textContentRect.GetY() };
982
983 if (IsUsingMouse() && isMousePressed_ && leftMousePressed_ && moveOverClickThreshold_) {
984 TAG_LOGI(ACE_TEXT, "not trigger click after mouse move");
985 moveOverClickThreshold_ = false;
986 return;
987 }
988
989 if (IsSelectableAndCopy() || NeedShowAIDetect()) {
990 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
991 }
992 if (HandleUrlClick()) {
993 return;
994 }
995 if (selectOverlay_->SelectOverlayIsOn() && !selectOverlay_->IsUsingMouse() &&
996 GlobalOffsetInSelectedArea(info.GetGlobalLocation())) {
997 if (!IsLocationInFrameRegion(info.GetLocalLocation())) {
998 return;
999 }
1000 selectOverlay_->ToggleMenu();
1001 selectOverlay_->SwitchToOverlayMode();
1002 return;
1003 }
1004 if (!isMousePressed_) {
1005 HandleClickAISpanEvent(textOffset);
1006 }
1007 if (dataDetectorAdapter_->hasClickedAISpan_) {
1008 selectOverlay_->HideMenu();
1009 return;
1010 }
1011 HandleClickOnTextAndSpan(info);
1012 }
1013
HandleClickOnTextAndSpan(GestureEvent & info)1014 void TextPattern::HandleClickOnTextAndSpan(GestureEvent& info)
1015 {
1016 if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE && !isMousePressed_) {
1017 CloseSelectOverlay(true);
1018 ResetSelection();
1019 }
1020 if (clickedSpanPosition_ == -1) {
1021 ActTextOnClick(info);
1022 return;
1023 }
1024 auto iter = spans_.begin();
1025 std::advance(iter, clickedSpanPosition_);
1026 RefPtr<SpanItem> span;
1027 if (iter == spans_.end()) {
1028 span = spans_.back();
1029 } else {
1030 span = *iter;
1031 }
1032 if (span && span->onClick) {
1033 GestureEvent spanClickinfo = info;
1034 EventTarget target = info.GetTarget();
1035 target.area.SetWidth(Dimension(0.0f));
1036 target.area.SetHeight(Dimension(0.0f));
1037 spanClickinfo.SetTarget(target);
1038 if (!TryLinkJump(span)) {
1039 span->onClick(spanClickinfo);
1040 // todo: RecordSpanClickEvent
1041 }
1042 } else {
1043 ActTextOnClick(info);
1044 }
1045 }
1046
1047 // return: whether execute link jump callback
TryLinkJump(const RefPtr<SpanItem> & span)1048 bool TextPattern::TryLinkJump(const RefPtr<SpanItem>& span)
1049 {
1050 auto host = GetHost();
1051 CHECK_NULL_RETURN(host, false);
1052 auto pipelineContext = host->GetContext();
1053 CHECK_NULL_RETURN(pipelineContext, false);
1054
1055 bool isCloudConfOpen = pipelineContext->GetIsLinkJumpOpen();
1056 if (isCloudConfOpen) {
1057 std::string spanContent = UtfUtils::Str16DebugToStr8(span->GetSpanContent()); // change for u16string
1058 auto isJumpLink = IsJumpLink(spanContent);
1059 TAG_LOGI(AceLogTag::ACE_TEXT, "TextPattern::TryLinkJump, spanContentLen: %{public}zu, isJumpLink: %{public}d",
1060 spanContent.size(), isJumpLink);
1061 if (isJumpLink) {
1062 pipelineContext->ExecuteLinkJumpCallback(spanContent);
1063 // todo: RecordSpanClickEvent
1064 return true;
1065 }
1066 }
1067 return false;
1068 }
1069
ActTextOnClick(GestureEvent & info)1070 void TextPattern::ActTextOnClick(GestureEvent& info)
1071 {
1072 if (onClick_) {
1073 auto onClick = onClick_;
1074 onClick(info);
1075 }
1076 }
1077
GlobalOffsetInSelectedArea(const Offset & globalOffset)1078 bool TextPattern::GlobalOffsetInSelectedArea(const Offset& globalOffset)
1079 {
1080 auto host = GetHost();
1081 CHECK_NULL_RETURN(host, false);
1082 auto offset = host->GetPaintRectOffset(false, true);
1083 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
1084 if (selectOverlay_->HasRenderTransform()) {
1085 localOffset = ConvertGlobalToLocalOffset(globalOffset);
1086 }
1087 return LocalOffsetInSelectedArea(localOffset);
1088 }
1089
LocalOffsetInSelectedArea(const Offset & localOffset)1090 bool TextPattern::LocalOffsetInSelectedArea(const Offset& localOffset)
1091 {
1092 if (IsSelectableAndCopy() && GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
1093 // Determine if the pan location is in the selected area
1094 auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1095 TextBase::CalculateSelectedRect(selectedRects, contentRect_.Width());
1096 auto panOffset = OffsetF(localOffset.GetX(), localOffset.GetY()) - contentRect_.GetOffset() +
1097 OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1098 for (const auto& selectedRect : selectedRects) {
1099 if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1100 return true;
1101 }
1102 }
1103 }
1104 return false;
1105 }
1106
HandleClickAISpanEvent(const PointF & textOffset)1107 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
1108 {
1109 dataDetectorAdapter_->hasClickedAISpan_ = false;
1110 if (!NeedShowAIDetect() || mouseStatus_ == MouseStatus::MOVE || IsDragging()) {
1111 return;
1112 }
1113
1114 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
1115 auto& aiSpan = kv.second;
1116 ClickAISpan(textOffset, aiSpan);
1117 if (dataDetectorAdapter_->hasClickedAISpan_) {
1118 return;
1119 }
1120 }
1121 }
1122
CheckClickedOnSpanOrText(RectF textContentRect,const Offset & localLocation)1123 bool TextPattern::CheckClickedOnSpanOrText(RectF textContentRect, const Offset& localLocation)
1124 {
1125 clickedSpanPosition_ = -1;
1126 auto host = GetHost();
1127 CHECK_NULL_RETURN(host, false);
1128 auto renderContext = host->GetRenderContext();
1129 CHECK_NULL_RETURN(host, false);
1130 PointF textOffset = GetTextOffset(localLocation, textContentRect);
1131 auto clip = false;
1132 if (Container::LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE)) {
1133 clip = true;
1134 }
1135 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value_or(clip) && overlayMod_) {
1136 textContentRect = overlayMod_->GetBoundsRect();
1137 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1138 }
1139 if (textContentRect.IsInRegion(
1140 PointF(static_cast<float>(localLocation.GetX()), static_cast<float>(localLocation.GetY()))) &&
1141 !spans_.empty() && pManager_) {
1142 if (CalculateClickedSpanPosition(textOffset)) {
1143 return true;
1144 }
1145 }
1146 if (onClick_) {
1147 return true;
1148 }
1149 return false;
1150 }
1151
GetTextOffset(const Offset & localLocation,const RectF & contentRect)1152 PointF TextPattern::GetTextOffset(const Offset &localLocation, const RectF &contentRect)
1153 {
1154 PointF textOffset = {static_cast<float>(localLocation.GetX()) - contentRect.GetX(),
1155 static_cast<float>(localLocation.GetY()) - contentRect.GetY()};
1156 return textOffset;
1157 }
1158
CalculateClickedSpanPosition(const PointF & textOffset)1159 bool TextPattern::CalculateClickedSpanPosition(const PointF& textOffset)
1160 {
1161 int32_t start = 0;
1162 for (const auto& item : spans_) {
1163 clickedSpanPosition_++;
1164 if (!item) {
1165 continue;
1166 }
1167 auto end = isSpanStringMode_ && item->position == -1 ? item->interval.second : item->position;
1168 auto selectedRects = GetSelectedRects(start, end);
1169 start = end;
1170 for (auto&& rect : selectedRects) {
1171 if (!rect.IsInRegion(textOffset)) {
1172 continue;
1173 }
1174 return CheckAndClick(item);
1175 }
1176 }
1177 clickedSpanPosition_ = -1;
1178 return false;
1179 }
1180
CheckAndClick(const RefPtr<SpanItem> & item)1181 bool TextPattern::CheckAndClick(const RefPtr<SpanItem>& item)
1182 {
1183 if (item->onClick || item->urlOnRelease) {
1184 return true;
1185 }
1186 clickedSpanPosition_ = -1;
1187 return false;
1188 }
1189
GetSelectedRects(int32_t start,int32_t end)1190 std::vector<RectF> TextPattern::GetSelectedRects(int32_t start, int32_t end)
1191 {
1192 return pManager_->GetRects(start, end);
1193 }
1194
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)1195 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
1196 {
1197 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1198 CHECK_NULL_RETURN(textLayoutProperty, false);
1199 int32_t start = aiSpan.start;
1200 int32_t end = aiSpan.end;
1201 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::ELLIPSIS) {
1202 auto range = pManager_->GetEllipsisTextRange();
1203 int32_t ellipsisStart = static_cast<int32_t>(range.first);
1204 int32_t ellipsisEnd = static_cast<int32_t>(range.second);
1205 if (ellipsisStart != -1 && ellipsisEnd > 0 && ellipsisStart < ellipsisEnd) {
1206 if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.end) {
1207 // ellipsisTextRange contains [aispan.start, aispan.end)
1208 return false;
1209 } else if (ellipsisStart <= aiSpan.start && ellipsisEnd >= aiSpan.start) {
1210 // ellipsisTextRange covers [aispan.start, ellipsisEnd)
1211 start = ellipsisEnd;
1212 } else if (ellipsisStart <= aiSpan.end && ellipsisEnd >= aiSpan.end) {
1213 // ellipsisTextRange covers [ellipsisStart, aiSpan.end);
1214 end = ellipsisStart;
1215 }
1216 }
1217 }
1218
1219 auto aiRects = pManager_->GetRects(start, end);
1220 for (auto&& rect : aiRects) {
1221 if (rect.IsInRegion(textOffset)) {
1222 dataDetectorAdapter_->hasClickedAISpan_ = true;
1223 if (leftMousePressed_) {
1224 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
1225 return true;
1226 }
1227 return ShowAIEntityMenu(aiSpan);
1228 }
1229 }
1230 return false;
1231 }
1232
InitUrlMouseEvent()1233 void TextPattern::InitUrlMouseEvent()
1234 {
1235 CHECK_NULL_VOID(!urlMouseEventInitialized_);
1236 auto host = GetHost();
1237 CHECK_NULL_VOID(host);
1238 auto eventHub = host->GetEventHub<EventHub>();
1239 CHECK_NULL_VOID(eventHub);
1240 auto inputHub = eventHub->GetOrCreateInputEventHub();
1241 CHECK_NULL_VOID(inputHub);
1242 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1243 auto pattern = weak.Upgrade();
1244 if (pattern) {
1245 pattern->HandleUrlMouseEvent(info);
1246 }
1247 };
1248 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1249 inputHub->AddOnMouseEvent(mouseEvent);
1250 auto mouseHoverTask = [weak = WeakClaim(this)](bool isHover) {
1251 auto pattern = weak.Upgrade();
1252 CHECK_NULL_VOID(pattern);
1253 pattern->URLOnHover(isHover);
1254 };
1255 auto mouseHoverEvent = MakeRefPtr<InputEvent>(std::move(mouseHoverTask));
1256 inputHub->AddOnHoverEvent(mouseHoverEvent);
1257 urlMouseEventInitialized_ = true;
1258 }
1259
URLOnHover(bool isHover)1260 void TextPattern::URLOnHover(bool isHover)
1261 {
1262 CHECK_NULL_VOID(!isHover);
1263 auto host = GetHost();
1264 CHECK_NULL_VOID(host);
1265 auto nodeId = host->GetId();
1266 auto pipelineContext = host->GetContext();
1267 CHECK_NULL_VOID(pipelineContext);
1268 pipelineContext->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1269 pipelineContext->FreeMouseStyleHoldNode(nodeId);
1270 CHECK_NULL_VOID(overlayMod_);
1271 overlayMod_->ClearSelectedForegroundColorAndRects();
1272 MarkDirtySelf();
1273 }
1274
HandleUrlMouseEvent(const MouseInfo & info)1275 void TextPattern::HandleUrlMouseEvent(const MouseInfo& info)
1276 {
1277 if (isMousePressed_) {
1278 return;
1279 }
1280 RectF textContentRect = contentRect_;
1281 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1282 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1283 auto localLocation = info.GetLocalLocation();
1284 if (selectOverlay_->HasRenderTransform()) {
1285 localLocation = ConvertGlobalToLocalOffset(info.GetGlobalLocation());
1286 }
1287 auto host = GetHost();
1288 CHECK_NULL_VOID(host);
1289 auto hostId = host->GetId();
1290 auto renderContext = host->GetRenderContext();
1291 CHECK_NULL_VOID(renderContext);
1292 PointF textOffset = { static_cast<float>(localLocation.GetX()) - textContentRect.GetX(),
1293 static_cast<float>(localLocation.GetY()) - textContentRect.GetY() };
1294 auto show = ShowShadow(textOffset, GetUrlHoverColor());
1295 auto pipelineContext = host->GetContext();
1296 CHECK_NULL_VOID(pipelineContext);
1297 if (show) {
1298 pipelineContext->SetMouseStyleHoldNode(hostId);
1299 pipelineContext->ChangeMouseStyle(hostId, MouseFormat::HAND_POINTING);
1300 } else {
1301 pipelineContext->ChangeMouseStyle(hostId, MouseFormat::DEFAULT);
1302 pipelineContext->FreeMouseStyleHoldNode(hostId);
1303 }
1304 }
1305
HandleUrlTouchEvent(const TouchEventInfo & info)1306 void TextPattern::HandleUrlTouchEvent(const TouchEventInfo& info)
1307 {
1308 CHECK_NULL_VOID(overlayMod_);
1309 CHECK_NULL_VOID(!IsDragging());
1310 if (selectOverlay_->IsTouchAtHandle(info)) {
1311 return;
1312 }
1313 auto touchType = info.GetTouches().front().GetTouchType();
1314 if (touchType != TouchType::DOWN && touchType != TouchType::UP) {
1315 return;
1316 }
1317 if (touchType == TouchType::DOWN) {
1318 RectF textContentRect = contentRect_;
1319 auto touchOffset = info.GetTouches().front().GetLocalLocation();
1320 PointF textOffset = { static_cast<float>(touchOffset.GetX()) - textContentRect.GetX(),
1321 static_cast<float>(touchOffset.GetY()) - textContentRect.GetY() };
1322 ShowShadow(textOffset, GetUrlPressColor());
1323 } else {
1324 overlayMod_->ClearSelectedForegroundColorAndRects();
1325 MarkDirtySelf();
1326 }
1327 }
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1328 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1329 const ShowSelectOverlayFunc& showSelectOverlayFunc)
1330
1331 {
1332 dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc](
1333 const std::string& action) {
1334 auto pattern = weak.Upgrade();
1335 CHECK_NULL_VOID(pattern);
1336 pattern->CloseSelectOverlay();
1337 pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
1338 if (action == COPY) {
1339 pattern->HandleOnCopy();
1340 pattern->ResetSelection();
1341 } else if (action == SELECT_TEXT) {
1342 if (calculateHandleFunc == nullptr) {
1343 pattern->CalculateHandleOffsetAndShowOverlay();
1344 } else {
1345 calculateHandleFunc();
1346 }
1347 if (showSelectOverlayFunc == nullptr) {
1348 pattern->ShowSelectOverlay({ .animation = true });
1349 } else {
1350 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
1351 }
1352 auto frameNode = pattern->GetHost();
1353 CHECK_NULL_VOID(frameNode);
1354 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1355 }
1356 };
1357 }
1358
CalcAIMenuPosition(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc)1359 RectF TextPattern::CalcAIMenuPosition(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc)
1360 {
1361 // save information
1362 auto baseOffset = textSelector_.baseOffset;
1363 auto destinationOffset = textSelector_.destinationOffset;
1364 // calculate result
1365 textSelector_.Update(aiSpan.start, aiSpan.end);
1366 parentGlobalOffset_ = GetParentGlobalOffset();
1367 if (calculateHandleFunc == nullptr) {
1368 CalculateHandleOffsetAndShowOverlay();
1369 } else {
1370 calculateHandleFunc();
1371 }
1372 RectF aiRect;
1373 if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
1374 auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
1375 auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
1376 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
1377 auto left = textContentGlobalOffset.GetX();
1378 auto right = textContentGlobalOffset.GetX() + contentRect_.Width();
1379 aiRect = RectT(left, top, right - left, bottom - top);
1380 } else {
1381 aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
1382 }
1383 // restore textSelector_
1384 textSelector_.Update(baseOffset, destinationOffset);
1385 if (calculateHandleFunc == nullptr) {
1386 CalculateHandleOffsetAndShowOverlay();
1387 } else {
1388 calculateHandleFunc();
1389 }
1390 return aiRect;
1391 }
1392
ShowAIEntityMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)1393 bool TextPattern::ShowAIEntityMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
1394 const ShowSelectOverlayFunc& showSelectOverlayFunc)
1395 {
1396 auto host = GetHost();
1397 CHECK_NULL_RETURN(host, false);
1398 auto context = host->GetContext();
1399 CHECK_NULL_RETURN(context, false);
1400 auto safeAreaManager = context->GetSafeAreaManager();
1401 CHECK_NULL_RETURN(safeAreaManager, false);
1402 SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
1403 RectF aiRect = CalcAIMenuPosition(aiSpan, calculateHandleFunc);
1404
1405 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1406 CHECK_NULL_RETURN(textLayoutProperty, false);
1407 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
1408 if (!NearEqual(safeAreaManager->GetKeyboardInset().Length(), 0)
1409 && mode == TextSelectableMode::SELECTABLE_FOCUSABLE) {
1410 aiRect.SetTop(aiRect.GetY() - safeAreaManager->GetKeyboardOffset());
1411 }
1412
1413 bool isShowCopy = true;
1414 bool isShowSelectText = true;
1415 if (copyOption_ == CopyOptions::None) {
1416 isShowCopy = false;
1417 isShowSelectText = false;
1418 } else if (mode == TextSelectableMode::UNSELECTABLE) {
1419 isShowSelectText = false;
1420 }
1421 return dataDetectorAdapter_->ShowAIEntityMenu(aiSpan, aiRect, host, isShowCopy, isShowSelectText);
1422 }
1423
HandleDoubleClickEvent(GestureEvent & info)1424 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
1425 {
1426 CheckOnClickEvent(info);
1427 auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
1428 if (!IsSelectableAndCopy() || (textSize == 0)) {
1429 return;
1430 }
1431 auto host = GetHost();
1432 CHECK_NULL_VOID(host);
1433 auto hub = host->GetEventHub<EventHub>();
1434 CHECK_NULL_VOID(hub);
1435 auto gestureHub = hub->GetOrCreateGestureEventHub();
1436 CHECK_NULL_VOID(gestureHub);
1437 isDoubleClick_ = true;
1438 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1439 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1440 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1441 InitSelection(textOffset);
1442 textResponseType_ = TextResponseType::NONE;
1443 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
1444 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
1445 parentGlobalOffset_ = GetParentGlobalOffset();
1446 CalculateHandleOffsetAndShowOverlay();
1447 if (!isMousePressed_) {
1448 ShowSelectOverlay({ .animation = true });
1449 }
1450 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1451 }
1452
CheckOnClickEvent(GestureEvent & info)1453 void TextPattern::CheckOnClickEvent(GestureEvent& info)
1454 {
1455 RectF textContentRect = contentRect_;
1456 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
1457 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
1458 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
1459 info.GetLocalLocation().GetY() - textContentRect.GetY() };
1460 if (IsSelectableAndCopy() || NeedShowAIDetect()) {
1461 CheckClickedOnSpanOrText(textContentRect, info.GetLocalLocation());
1462 }
1463 HandleClickOnTextAndSpan(info);
1464 }
1465
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)1466 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
1467 {
1468 gestureHub->SetNodeClickDistance(distanceThreshold_);
1469 CHECK_NULL_VOID(!clickEventInitialized_);
1470 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
1471 auto pattern = weak.Upgrade();
1472 CHECK_NULL_VOID(pattern);
1473 pattern->sourceType_ = info.GetSourceDevice();
1474 pattern->HandleClickEvent(info);
1475 };
1476 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
1477 clickListener->SetSysJudge([weak = WeakClaim(this)](const RefPtr<GestureInfo>& gestureInfo,
1478 const std::shared_ptr<BaseGestureEvent>& info) -> GestureJudgeResult {
1479 auto textPattern = weak.Upgrade();
1480 CHECK_NULL_RETURN(textPattern, GestureJudgeResult::CONTINUE);
1481 if (info->GetFingerList().empty()) {
1482 return GestureJudgeResult::CONTINUE;
1483 }
1484 auto localLocation = info->GetFingerList().begin()->localLocation_;
1485 auto contentRect = textPattern->GetTextContentRect();
1486 auto baselineOffset = textPattern->GetBaselineOffset();
1487
1488 RectF textContentRect = contentRect;
1489 textContentRect.SetTop(contentRect.GetY() - std::min(baselineOffset, 0.0f));
1490 textContentRect.SetHeight(contentRect.Height() - std::max(baselineOffset, 0.0f));
1491 if (textPattern->GetCopyOptions() == CopyOptions::None && !textPattern->NeedShowAIDetect() &&
1492 !textPattern->CheckClickedOnSpanOrText(textContentRect, localLocation)) {
1493 return GestureJudgeResult::REJECT;
1494 }
1495 return GestureJudgeResult::CONTINUE;
1496 });
1497 gestureHub->AddClickEvent(clickListener);
1498 clickEventInitialized_ = true;
1499 }
1500
InitAISpanHoverEvent()1501 void TextPattern::InitAISpanHoverEvent()
1502 {
1503 CHECK_NULL_VOID(!aiSpanHoverEventInitialized_);
1504 auto host = GetHost();
1505 CHECK_NULL_VOID(host);
1506 auto eventHub = host->GetEventHub<EventHub>();
1507 CHECK_NULL_VOID(eventHub);
1508 auto inputHub = eventHub->GetOrCreateInputEventHub();
1509 CHECK_NULL_VOID(inputHub);
1510
1511 auto aiSpanHoverTask = [weak = WeakClaim(this)](MouseInfo& info) {
1512 auto pattern = weak.Upgrade();
1513 CHECK_NULL_VOID(pattern);
1514 pattern->HandleAISpanHoverEvent(info);
1515 };
1516 auto aiSpanHoverEvent = MakeRefPtr<InputEvent>(std::move(aiSpanHoverTask));
1517 inputHub->AddOnMouseEvent(aiSpanHoverEvent);
1518 aiSpanHoverEventInitialized_ = true;
1519 }
1520
HandleAISpanHoverEvent(const MouseInfo & info)1521 void TextPattern::HandleAISpanHoverEvent(const MouseInfo& info)
1522 {
1523 if (info.GetAction() != MouseAction::MOVE || !NeedShowAIDetect() || !isHover_) {
1524 return;
1525 }
1526 if (dataDetectorAdapter_->aiSpanRects_.empty()) {
1527 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
1528 auto& aiSpan = kv.second;
1529 const auto& aiRects = pManager_->GetRects(aiSpan.start, aiSpan.end);
1530 dataDetectorAdapter_->aiSpanRects_.insert(
1531 dataDetectorAdapter_->aiSpanRects_.end(), aiRects.begin(), aiRects.end());
1532 }
1533 }
1534
1535 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1536 PointF textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
1537 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
1538 auto host = GetHost();
1539 CHECK_NULL_VOID(host);
1540 auto pipeline = GetContext();
1541 CHECK_NULL_VOID(pipeline);
1542 auto nodeId = host->GetId();
1543 pipeline->SetMouseStyleHoldNode(nodeId);
1544 for (auto&& rect : dataDetectorAdapter_->aiSpanRects_) {
1545 if (!rect.IsInRegion(textOffset)) {
1546 continue;
1547 }
1548 if (currentMouseStyle_ != MouseFormat::HAND_POINTING) {
1549 bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::HAND_POINTING);
1550 CHECK_NULL_VOID(changeSuccess);
1551 currentMouseStyle_ = MouseFormat::HAND_POINTING;
1552 }
1553 return;
1554 }
1555 if (currentMouseStyle_ != MouseFormat::DEFAULT) {
1556 bool changeSuccess = pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1557 CHECK_NULL_VOID(changeSuccess);
1558 currentMouseStyle_ = MouseFormat::DEFAULT;
1559 }
1560 }
1561
OnHover(bool isHover)1562 void TextPattern::OnHover(bool isHover)
1563 {
1564 isHover_ = isHover;
1565 TAG_LOGI(AceLogTag::ACE_TEXT, "isHover=%{public}d", isHover);
1566 auto host = GetHost();
1567 CHECK_NULL_VOID(host);
1568 auto pipeline = GetContext();
1569 CHECK_NULL_VOID(pipeline);
1570 auto nodeId = host->GetId();
1571 if (isHover) {
1572 pipeline->SetMouseStyleHoldNode(nodeId);
1573 pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1574 currentMouseStyle_ = MouseFormat::DEFAULT;
1575 } else {
1576 pipeline->ChangeMouseStyle(nodeId, MouseFormat::DEFAULT);
1577 currentMouseStyle_ = MouseFormat::DEFAULT;
1578 pipeline->FreeMouseStyleHoldNode(nodeId);
1579 }
1580 }
1581
InitMouseEvent()1582 void TextPattern::InitMouseEvent()
1583 {
1584 CHECK_NULL_VOID(!mouseEventInitialized_);
1585 auto host = GetHost();
1586 CHECK_NULL_VOID(host);
1587 auto eventHub = host->GetEventHub<EventHub>();
1588 CHECK_NULL_VOID(eventHub);
1589 auto inputHub = eventHub->GetOrCreateInputEventHub();
1590 CHECK_NULL_VOID(inputHub);
1591
1592 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
1593 auto pattern = weak.Upgrade();
1594 CHECK_NULL_VOID(pattern);
1595 pattern->HandleMouseEvent(info);
1596 };
1597 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
1598 inputHub->AddOnMouseEvent(mouseEvent);
1599
1600 auto hoverTask = [weak = WeakClaim(this)](bool isHover) {
1601 TAG_LOGI(AceLogTag::ACE_TEXT, "on hover event isHover=%{public}d", isHover);
1602 auto pattern = weak.Upgrade();
1603 if (pattern) {
1604 pattern->OnHover(isHover);
1605 }
1606 };
1607 auto hoverEvent = MakeRefPtr<InputEvent>(std::move(hoverTask));
1608 inputHub->AddOnHoverEvent(hoverEvent);
1609 mouseEventInitialized_ = true;
1610 }
1611
InitFocusEvent()1612 void TextPattern::InitFocusEvent()
1613 {
1614 CHECK_NULL_VOID(!focusInitialized_);
1615 auto host = GetHost();
1616 auto focusHub = host->GetFocusHub();
1617 CHECK_NULL_VOID(focusHub);
1618 auto focusTask = [weak = WeakClaim(this)]() {
1619 auto pattern = weak.Upgrade();
1620 CHECK_NULL_VOID(pattern);
1621 auto contentModifier = pattern->GetContentModifier();
1622 CHECK_NULL_VOID(contentModifier);
1623 contentModifier->SetIsFocused(true);
1624 pattern->AddIsFocusActiveUpdateEvent();
1625 };
1626 focusHub->SetOnFocusInternal(focusTask);
1627
1628 auto blurTask = [weak = WeakClaim(this)]() {
1629 auto pattern = weak.Upgrade();
1630 CHECK_NULL_VOID(pattern);
1631 auto contentModifier = pattern->GetContentModifier();
1632 CHECK_NULL_VOID(contentModifier);
1633 contentModifier->SetIsFocused(false);
1634 pattern->RemoveIsFocusActiveUpdateEvent();
1635 pattern->ResetOriginCaretPosition();
1636 };
1637 focusHub->SetOnBlurInternal(blurTask);
1638
1639 focusInitialized_ = true;
1640 }
1641
AddIsFocusActiveUpdateEvent()1642 void TextPattern::AddIsFocusActiveUpdateEvent()
1643 {
1644 if (!isFocusActiveUpdateEvent_) {
1645 isFocusActiveUpdateEvent_ = [weak = WeakClaim(this)](bool isFocusAcitve) {
1646 auto pattern = weak.Upgrade();
1647 CHECK_NULL_VOID(pattern);
1648 pattern->OnIsFocusActiveUpdate(isFocusAcitve);
1649 };
1650 }
1651
1652 auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
1653 CHECK_NULL_VOID(pipline);
1654 pipline->AddIsFocusActiveUpdateEvent(GetHost(), isFocusActiveUpdateEvent_);
1655 }
1656
RemoveIsFocusActiveUpdateEvent()1657 void TextPattern::RemoveIsFocusActiveUpdateEvent()
1658 {
1659 auto pipline = PipelineContext::GetCurrentContextSafelyWithCheck();
1660 CHECK_NULL_VOID(pipline);
1661 pipline->RemoveIsFocusActiveUpdateEvent(GetHost());
1662 }
1663
OnIsFocusActiveUpdate(bool isFocusAcitve)1664 void TextPattern::OnIsFocusActiveUpdate(bool isFocusAcitve)
1665 {
1666 auto host = GetHost();
1667 CHECK_NULL_VOID(host);
1668 auto pattern = host->GetPattern<TextPattern>();
1669 CHECK_NULL_VOID(pattern);
1670 auto contentModifier = pattern->GetContentModifier();
1671 CHECK_NULL_VOID(contentModifier);
1672 contentModifier->SetIsFocused(isFocusAcitve);
1673 }
1674
InitHoverEvent()1675 void TextPattern::InitHoverEvent()
1676 {
1677 CHECK_NULL_VOID(!hoverInitialized_);
1678 auto host = GetHost();
1679 CHECK_NULL_VOID(host);
1680 auto eventHub = host->GetEventHub<EventHub>();
1681 CHECK_NULL_VOID(eventHub);
1682 auto inputHub = eventHub->GetOrCreateInputEventHub();
1683 CHECK_NULL_VOID(inputHub);
1684
1685 auto mouseTask = [weak = WeakClaim(this)](bool isHover) {
1686 auto pattern = weak.Upgrade();
1687 CHECK_NULL_VOID(pattern);
1688 auto contentModifier = pattern->GetContentModifier();
1689 CHECK_NULL_VOID(contentModifier);
1690 contentModifier->SetIsHovered(isHover);
1691 };
1692 auto mouseEvent_ = MakeRefPtr<InputEvent>(std::move(mouseTask));
1693 inputHub->AddOnHoverEvent(mouseEvent_);
1694
1695 hoverInitialized_ = true;
1696 }
1697
RecoverCopyOption()1698 void TextPattern::RecoverCopyOption()
1699 {
1700 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1701 CHECK_NULL_VOID(textLayoutProperty);
1702 auto host = GetHost();
1703 CHECK_NULL_VOID(host);
1704
1705 copyOption_ = textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE
1706 ? CopyOptions::None
1707 : textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
1708
1709 const auto& children = host->GetChildren();
1710 if (children.empty()) {
1711 if (IsSetObscured() && !isSpanStringMode_) {
1712 copyOption_ = CopyOptions::None;
1713 }
1714 }
1715 auto gestureEventHub = host->GetOrCreateGestureEventHub();
1716 CHECK_NULL_VOID(gestureEventHub);
1717 auto eventHub = host->GetEventHub<EventHub>();
1718 CHECK_NULL_VOID(eventHub);
1719 if (copyOption_ == CopyOptions::None && !textDetectEnable_ && !textLayoutProperty->GetTextOverflow() &&
1720 !onClick_ && !longPressEvent_) { // performance prune
1721 if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
1722 gestureEventHub->SetTextDraggable(false);
1723 eventHub->SetDefaultOnDragStart(nullptr);
1724 if (!eventHub->HasOnDragStart() && IsTextNode()) {
1725 gestureEventHub->RemoveDragEvent();
1726 }
1727 }
1728 return;
1729 }
1730 if (copyOption_ == CopyOptions::None) {
1731 CloseSelectOverlay();
1732 ResetSelection();
1733 }
1734 if (children.empty() && CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
1735 dataDetectorAdapter_->textForAI_ = textForDisplay_;
1736 dataDetectorAdapter_->StartAITask();
1737 }
1738 ProcessMarqueeVisibleAreaCallback();
1739 InitCopyOption(gestureEventHub, eventHub);
1740 bool enabledCache = eventHub->IsEnabled();
1741 selectOverlay_->SetMenuTranslateIsSupport(IsShowTranslate());
1742 selectOverlay_->SetIsSupportMenuSearch(IsShowSearch());
1743 selectOverlay_->UpdateHandleColor();
1744 if (textDetectEnable_ && enabledCache != enabled_) {
1745 enabled_ = enabledCache;
1746 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
1747 }
1748 }
1749
InitCopyOption(const RefPtr<GestureEventHub> & gestureEventHub,const RefPtr<EventHub> & eventHub)1750 void TextPattern::InitCopyOption(const RefPtr<GestureEventHub>& gestureEventHub, const RefPtr<EventHub>& eventHub)
1751 {
1752 CHECK_NULL_VOID(gestureEventHub);
1753 CHECK_NULL_VOID(eventHub);
1754 auto host = GetHost();
1755 CHECK_NULL_VOID(host);
1756 if (IsSelectableAndCopy()) {
1757 auto context = host->GetContext();
1758 CHECK_NULL_VOID(context);
1759 if (!clipboard_ && context) {
1760 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
1761 }
1762 InitLongPressEvent(gestureEventHub);
1763 if (host->IsDraggable() && !shiftFlag_) {
1764 InitDragEvent();
1765 }
1766 InitKeyEvent();
1767 InitMouseEvent();
1768 InitTouchEvent();
1769 SetAccessibilityAction();
1770 } else {
1771 if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
1772 gestureEventHub->SetTextDraggable(false);
1773 eventHub->SetDefaultOnDragStart(nullptr);
1774 if (!eventHub->HasOnDragStart() && IsTextNode()) {
1775 gestureEventHub->RemoveDragEvent();
1776 }
1777 }
1778 if (longPressEvent_ && !hasSpanStringLongPressEvent_) {
1779 gestureEventHub->SetLongPressEvent(nullptr);
1780 longPressEvent_ = nullptr;
1781 }
1782 }
1783 if (onClick_ || IsSelectableAndCopy() || CanStartAITask()) {
1784 InitClickEvent(gestureEventHub);
1785 if (CanStartAITask()) {
1786 auto context = host->GetContext();
1787 CHECK_NULL_VOID(context);
1788 if (!clipboard_ && context) {
1789 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
1790 }
1791 InitMouseEvent();
1792 InitAISpanHoverEvent();
1793 }
1794 }
1795 }
1796
HandleMouseEvent(const MouseInfo & info)1797 void TextPattern::HandleMouseEvent(const MouseInfo& info)
1798 {
1799 auto localLocation = info.GetLocalLocation();
1800 if (isAutoScrollByMouse_ && GetHost()) {
1801 NG::PointF localPoint(info.GetGlobalLocation().GetX(), info.GetGlobalLocation().GetY());
1802 NG::NGGestureRecognizer::Transform(localPoint, GetHost(), true);
1803 localLocation.SetX(localPoint.GetX());
1804 localLocation.SetY(localPoint.GetY());
1805 }
1806 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1807 Offset textOffset = { localLocation.GetX() - textPaintOffset.GetX(),
1808 localLocation.GetY() - textPaintOffset.GetY() };
1809 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
1810 lastLeftMouseMoveLocation_ = info.GetGlobalLocation();
1811 HandleMouseLeftButton(info, textOffset);
1812 if (IsSelected()) {
1813 selectOverlay_->SetSelectionHoldCallback();
1814 }
1815 sourceType_ = info.GetSourceDevice();
1816 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
1817 HandleMouseRightButton(info, textOffset);
1818 sourceType_ = info.GetSourceDevice();
1819 }
1820 if (!IsSelected()) {
1821 ResetOriginCaretPosition();
1822 }
1823 }
1824
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)1825 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
1826 {
1827 if (info.GetAction() == MouseAction::PRESS) {
1828 HandleMouseLeftPressAction(info, textOffset);
1829 } else if (info.GetAction() == MouseAction::MOVE) {
1830 HandleMouseLeftMoveAction(info, textOffset);
1831 } else if (info.GetAction() == MouseAction::RELEASE) {
1832 HandleMouseLeftReleaseAction(info, textOffset);
1833 }
1834
1835 auto host = GetHost();
1836 CHECK_NULL_VOID(host);
1837 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1838 }
1839
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)1840 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
1841 {
1842 isMousePressed_ = true;
1843 CheckPressedSpanPosition(textOffset);
1844 leftMousePressed_ = true;
1845 ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlPressColor());
1846 if (BetweenSelectedPosition(info.GetGlobalLocation())) {
1847 blockPress_ = true;
1848 return;
1849 }
1850 mouseStatus_ = MouseStatus::PRESSED;
1851 CHECK_NULL_VOID(pManager_);
1852 if (shiftFlag_) {
1853 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
1854 HandleSelectionChange(textSelector_.lastValidStart, end);
1855 } else {
1856 auto start = pManager_->GetGlyphIndexByCoordinate(textOffset);
1857 textSelector_.Update(start, start);
1858 }
1859 // auto scroll.
1860 scrollableParent_ = selectOverlay_->FindScrollableParent();
1861 auto host = GetHost();
1862 if (scrollableParent_.Upgrade() && host) {
1863 host->RegisterNodeChangeListener();
1864 }
1865 }
1866
CheckPressedSpanPosition(const Offset & textOffset)1867 void TextPattern::CheckPressedSpanPosition(const Offset& textOffset)
1868 {
1869 leftMousePressedOffset_ = textOffset;
1870 }
1871
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)1872 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
1873 {
1874 if (blockPress_) {
1875 blockPress_ = false;
1876 }
1877 auto oldMouseStatus = mouseStatus_;
1878 mouseStatus_ = MouseStatus::RELEASED;
1879 ShowShadow({ textOffset.GetX(), textOffset.GetY() }, GetUrlHoverColor());
1880 if (isDoubleClick_) {
1881 isDoubleClick_ = false;
1882 isMousePressed_ = false;
1883 leftMousePressed_ = false;
1884 return;
1885 }
1886 if (oldMouseStatus != MouseStatus::MOVE && oldMouseStatus == MouseStatus::PRESSED && !IsDragging()) {
1887 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1888 if (dataDetectorAdapter_->hasClickedAISpan_) {
1889 selectOverlay_->DisableMenu();
1890 isMousePressed_ = false;
1891 leftMousePressed_ = false;
1892 return;
1893 }
1894 }
1895
1896 CHECK_NULL_VOID(pManager_);
1897 auto start = textSelector_.baseOffset;
1898 auto end = textSelector_.destinationOffset;
1899 if (!IsSelected()) {
1900 start = -1;
1901 end = -1;
1902 }
1903 if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE || shiftFlag_) {
1904 HandleSelectionChange(start, end);
1905 }
1906
1907 if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
1908 selectOverlay_->SetMouseMenuOffset(OffsetF(
1909 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
1910 textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
1911 ShowSelectOverlay({ .animation = true });
1912 }
1913 isMousePressed_ = false;
1914 leftMousePressed_ = false;
1915 moveOverClickThreshold_ = false;
1916 // stop auto scroll.
1917 auto host = GetHost();
1918 if (host && scrollableParent_.Upgrade() && !selectOverlay_->SelectOverlayIsOn()) {
1919 host->UnregisterNodeChangeListener();
1920 }
1921 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), true);
1922 isAutoScrollByMouse_ = false;
1923 }
1924
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)1925 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
1926 {
1927 if (!IsSelectableAndCopy()) {
1928 isMousePressed_ = false;
1929 leftMousePressed_ = false;
1930 return;
1931 }
1932 if (isMousePressed_) {
1933 mouseStatus_ = MouseStatus::MOVE;
1934 CHECK_NULL_VOID(pManager_);
1935 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
1936 HandleSelectionChange(textSelector_.baseOffset, end);
1937 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), info.GetGlobalLocation(), false);
1938 auto distance = (textOffset - leftMousePressedOffset_).GetDistance();
1939 if (distance >= CLICK_THRESHOLD.ConvertToPx()) {
1940 moveOverClickThreshold_ = true;
1941 return;
1942 }
1943 }
1944 }
1945
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)1946 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
1947 {
1948 if (info.GetAction() == MouseAction::RELEASE) {
1949 selectOverlay_->SetMouseMenuOffset(OffsetF(
1950 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY())));
1951 if (!BetweenSelectedPosition(info.GetGlobalLocation())) {
1952 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1953 if (dataDetectorAdapter_->hasClickedAISpan_) {
1954 isMousePressed_ = false;
1955 return;
1956 }
1957 }
1958 if (!IsSelectableAndCopy()) {
1959 return;
1960 }
1961
1962 CalculateHandleOffsetAndShowOverlay(true);
1963 if (selectOverlay_->SelectOverlayIsOn()) {
1964 CloseSelectOverlay(true);
1965 }
1966 textResponseType_ = TextResponseType::RIGHT_CLICK;
1967 if (!IsSelected()) {
1968 auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
1969 if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
1970 selectedType_ = TextSpanType::IMAGE;
1971 } else {
1972 selectedType_ = TextSpanType::TEXT;
1973 }
1974 }
1975 ShowSelectOverlay({ .animation = true });
1976 isMousePressed_ = false;
1977 auto host = GetHost();
1978 CHECK_NULL_VOID(host);
1979 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1980 } else if (info.GetAction() == MouseAction::PRESS) {
1981 isMousePressed_ = true;
1982 CloseSelectOverlay(true);
1983 }
1984 }
1985
InitTouchEvent()1986 void TextPattern::InitTouchEvent()
1987 {
1988 CHECK_NULL_VOID(!touchEventInitialized_);
1989 auto host = GetHost();
1990 CHECK_NULL_VOID(host);
1991 auto gesture = host->GetOrCreateGestureEventHub();
1992 CHECK_NULL_VOID(gesture);
1993
1994 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1995 auto pattern = weak.Upgrade();
1996 CHECK_NULL_VOID(pattern);
1997 pattern->sourceType_ = info.GetSourceDevice();
1998 pattern->HandleTouchEvent(info);
1999 };
2000 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2001 gesture->AddTouchEvent(touchListener);
2002 touchEventInitialized_ = true;
2003 }
2004
InitUrlTouchEvent()2005 void TextPattern::InitUrlTouchEvent()
2006 {
2007 CHECK_NULL_VOID(!urlTouchEventInitialized_);
2008 auto host = GetHost();
2009 CHECK_NULL_VOID(host);
2010 auto gesture = host->GetOrCreateGestureEventHub();
2011 CHECK_NULL_VOID(gesture);
2012
2013 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
2014 auto pattern = weak.Upgrade();
2015 CHECK_NULL_VOID(pattern);
2016 pattern->HandleUrlTouchEvent(info);
2017 };
2018 auto touchListener = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
2019 gesture->AddTouchEvent(touchListener);
2020 urlTouchEventInitialized_ = true;
2021 }
2022
MarkDirtySelf()2023 void TextPattern::MarkDirtySelf()
2024 {
2025 auto host = GetHost();
2026 CHECK_NULL_VOID(host);
2027 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2028 }
2029
HandleTouchEvent(const TouchEventInfo & info)2030 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
2031 {
2032 DoGestureSelection(info);
2033 ResetOriginCaretPosition();
2034 }
2035
InitKeyEvent()2036 void TextPattern::InitKeyEvent()
2037 {
2038 CHECK_NULL_VOID(!keyEventInitialized_);
2039 auto host = GetHost();
2040 CHECK_NULL_VOID(host);
2041 auto focusHub = host->GetOrCreateFocusHub();
2042 CHECK_NULL_VOID(focusHub);
2043
2044 auto keyTask = [weak = WeakClaim(this)](const KeyEvent& event) -> bool {
2045 auto pattern = weak.Upgrade();
2046 CHECK_NULL_RETURN(pattern, false);
2047 return pattern->HandleKeyEvent(event);
2048 };
2049 focusHub->SetOnKeyEventInternal(std::move(keyTask));
2050 keyEventInitialized_ = true;
2051 }
2052
UpdateShiftFlag(const KeyEvent & keyEvent)2053 void TextPattern::UpdateShiftFlag(const KeyEvent& keyEvent)
2054 {
2055 bool flag = false;
2056 if (keyEvent.action == KeyAction::DOWN) {
2057 if (keyEvent.HasKey(KeyCode::KEY_SHIFT_LEFT) || keyEvent.HasKey(KeyCode::KEY_SHIFT_RIGHT)) {
2058 flag = true;
2059 }
2060 }
2061 if (flag != shiftFlag_) {
2062 shiftFlag_ = flag;
2063 if (!shiftFlag_) {
2064 // open drag
2065 InitDragEvent();
2066 } else {
2067 // close drag
2068 ClearDragEvent();
2069 }
2070 }
2071 }
2072
HandleKeyEvent(const KeyEvent & keyEvent)2073 bool TextPattern::HandleKeyEvent(const KeyEvent& keyEvent)
2074 {
2075 UpdateShiftFlag(keyEvent);
2076 if (keyEvent.action != KeyAction::DOWN) {
2077 return false;
2078 }
2079
2080 if (keyEvent.IsCtrlWith(KeyCode::KEY_C)) {
2081 HandleOnCopy();
2082 return true;
2083 }
2084
2085 if (keyEvent.IsCtrlWith(KeyCode::KEY_A)) {
2086 auto textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
2087 HandleSelectionChange(0, textSize);
2088 CloseSelectOverlay();
2089 auto host = GetHost();
2090 CHECK_NULL_RETURN(host, false);
2091 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2092 return true;
2093 }
2094
2095 if (keyEvent.IsShiftWith(keyEvent.code)) {
2096 HandleOnSelect(keyEvent.code);
2097 return true;
2098 }
2099 return false;
2100 }
2101
HandleOnSelect(KeyCode code)2102 void TextPattern::HandleOnSelect(KeyCode code)
2103 {
2104 auto end = textSelector_.GetEnd();
2105 switch (code) {
2106 case KeyCode::KEY_DPAD_LEFT: {
2107 HandleSelection(true, end - 1);
2108 break;
2109 }
2110 case KeyCode::KEY_DPAD_RIGHT: {
2111 HandleSelection(false, end + 1);
2112 break;
2113 }
2114 case KeyCode::KEY_DPAD_UP: {
2115 HandleSelectionUp();
2116 break;
2117 }
2118 case KeyCode::KEY_DPAD_DOWN: {
2119 HandleSelectionDown();
2120 break;
2121 }
2122 default:
2123 break;
2124 }
2125 if (!(shiftFlag_ && (code == KeyCode::KEY_DPAD_UP ||
2126 code == KeyCode::KEY_DPAD_DOWN))) {
2127 ResetOriginCaretPosition();
2128 }
2129 }
2130
HandleSelectionUp()2131 void TextPattern::HandleSelectionUp()
2132 {
2133 auto end = textSelector_.GetEnd();
2134 auto line = pManager_->GetLineCount();
2135 if (line == 1) {
2136 HandleSelection(true, 0);
2137 return;
2138 }
2139 CaretMetricsF secondHandleMetrics;
2140 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2141 auto secondOffsetX = secondHandleMetrics.offset.GetX();
2142 auto secondOffsetY = secondHandleMetrics.offset.GetY();
2143 RecordOriginCaretPosition({ secondOffsetX, secondOffsetY });
2144 OffsetF originCaretPosition;
2145 auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2146 originCaretPosition.GetX() : secondOffsetX;
2147 double height = GetTextHeight(end, false);
2148 Offset offset = { caretXPosition, secondOffsetY - height * 0.5 };
2149 auto caculateIndex = GetHandleIndex(offset);
2150 if (end == caculateIndex) {
2151 caculateIndex = 0;
2152 }
2153 HandleSelection(true, caculateIndex);
2154 }
2155
HandleSelectionDown()2156 void TextPattern::HandleSelectionDown()
2157 {
2158 auto end = textSelector_.GetEnd();
2159 auto line = pManager_->GetLineCount();
2160 auto lastIndex = GetActualTextLength();
2161 if (line == 1) {
2162 HandleSelection(true, lastIndex);
2163 return;
2164 }
2165 CaretMetricsF secondHandleMetrics;
2166 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
2167 auto secondOffsetX = secondHandleMetrics.offset.GetX();
2168 RecordOriginCaretPosition({ secondOffsetX, secondHandleMetrics.offset.GetY() });
2169 OffsetF originCaretPosition;
2170 auto caretXPosition = GetOriginCaretPosition(originCaretPosition) ? // recorded offset x
2171 originCaretPosition.GetX() : secondOffsetX;
2172 double height = GetTextHeight(end, true);
2173 auto caculateIndex = GetHandleIndex({ caretXPosition, height });
2174 if (NearZero(height) || caculateIndex == end || caculateIndex > lastIndex) {
2175 caculateIndex = lastIndex;
2176 }
2177 HandleSelection(true, caculateIndex);
2178 }
2179
HandleSelection(bool isEmojiStart,int32_t end)2180 void TextPattern::HandleSelection(bool isEmojiStart, int32_t end)
2181 {
2182 auto start = textSelector_.GetStart();
2183 auto lastIndex = GetActualTextLength();
2184 if (start < 0 || start > lastIndex || end < 0 || end > lastIndex) {
2185 return;
2186 }
2187 int32_t emojiStartIndex;
2188 int32_t emojiEndIndex;
2189 bool isIndexInEmoji = TextEmojiProcessor::IsIndexInEmoji(end, GetSelectedText(0, lastIndex),
2190 emojiStartIndex, emojiEndIndex);
2191 if (isIndexInEmoji) {
2192 end = isEmojiStart ? emojiStartIndex : emojiEndIndex;
2193 }
2194 HandleSelectionChange(start, end);
2195 CalculateHandleOffsetAndShowOverlay();
2196 CloseSelectOverlay(true);
2197 auto host = GetHost();
2198 CHECK_NULL_VOID(host);
2199 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2200 }
2201
GetTextHeight(int32_t index,bool isNextLine)2202 double TextPattern::GetTextHeight(int32_t index, bool isNextLine)
2203 {
2204 double lineHeight = 0.0;
2205 auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
2206 for (auto lineNumber = 0; lineNumber < lineCount; lineNumber++) {
2207 auto lineMetrics = GetLineMetrics(lineNumber);
2208 auto startIndex = static_cast<int32_t>(lineMetrics.startIndex);
2209 auto endIndex = static_cast<int32_t>(lineMetrics.endIndex);
2210 lineHeight += lineMetrics.height;
2211 if (isNextLine) {
2212 if (index <= endIndex && endIndex != GetActualTextLength()) {
2213 return lineHeight;
2214 }
2215 } else {
2216 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2217 CHECK_NULL_RETURN(textLayoutProperty, 0);
2218 auto maxLines = textLayoutProperty->GetMaxLinesValue(Infinity<uint32_t>());
2219 if ((index <= endIndex && startIndex != 0) ||
2220 ((lineNumber + 1) == static_cast<int32_t>(maxLines) && lineNumber != 0)) {
2221 return GetLineMetrics(lineNumber - 1).height;
2222 }
2223 }
2224 }
2225 return 0.0;
2226 }
2227
GetActualTextLength()2228 int32_t TextPattern::GetActualTextLength()
2229 {
2230 auto lineCount = static_cast<int32_t>(pManager_->GetLineCount());
2231 return GetLineMetrics(lineCount - 1).endIndex;
2232 }
2233
SetTextSelectableMode(TextSelectableMode value)2234 void TextPattern::SetTextSelectableMode(TextSelectableMode value)
2235 {
2236 auto host = GetHost();
2237 CHECK_NULL_VOID(host);
2238 auto focusHub = host->GetOrCreateFocusHub();
2239 CHECK_NULL_VOID(focusHub);
2240 if (value == TextSelectableMode::SELECTABLE_FOCUSABLE) {
2241 focusHub->SetFocusable(true);
2242 focusHub->SetIsFocusOnTouch(true);
2243 } else {
2244 focusHub->SetFocusable(false);
2245 focusHub->SetIsFocusOnTouch(false);
2246 }
2247 }
2248
IsSelectableAndCopy()2249 bool TextPattern::IsSelectableAndCopy()
2250 {
2251 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2252 CHECK_NULL_RETURN(textLayoutProperty, false);
2253 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
2254 return mode != TextSelectableMode::UNSELECTABLE && copyOption_ != CopyOptions::None;
2255 }
2256
IsDraggable(const Offset & offset)2257 bool TextPattern::IsDraggable(const Offset& offset)
2258 {
2259 auto host = GetHost();
2260 CHECK_NULL_RETURN(host, false);
2261 auto eventHub = host->GetEventHub<EventHub>();
2262 bool draggable = eventHub->HasOnDragStart();
2263 return draggable && LocalOffsetInSelectedArea(offset);
2264 }
2265
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)2266 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
2267 {
2268 DragDropInfo itemInfo;
2269 auto host = GetHost();
2270 CHECK_NULL_RETURN(host, itemInfo);
2271 if (overlayMod_) {
2272 overlayMod_->ClearSelectedForegroundColorAndRects();
2273 }
2274 auto hub = host->GetEventHub<EventHub>();
2275 auto gestureHub = hub->GetOrCreateGestureEventHub();
2276 auto selectStart = textSelector_.GetTextStart();
2277 auto selectEnd = textSelector_.GetTextEnd();
2278 recoverStart_ = selectStart;
2279 recoverEnd_ = selectEnd;
2280 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
2281 dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
2282 ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
2283 dragBoxes_ = GetTextBoxes();
2284 status_ = Status::DRAGGING;
2285 if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
2286 return itemInfo;
2287 }
2288 auto data = event->GetData();
2289 if (!data) {
2290 AddUdmfData(event);
2291 }
2292 CloseOperate();
2293 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
2294 return itemInfo;
2295 }
2296
AddUdmfTxtPreProcessor(const ResultObject src,ResultObject & result,bool isAppend)2297 void TextPattern::AddUdmfTxtPreProcessor(const ResultObject src, ResultObject& result, bool isAppend)
2298 {
2299 auto valueString = GetSelectedSpanText(src.valueString,
2300 src.offsetInSpan[RichEditorSpanRange::RANGESTART], src.offsetInSpan[RichEditorSpanRange::RANGEEND],
2301 false, true, false);
2302 if (isAppend) {
2303 result.valueString = result.valueString + valueString;
2304 } else {
2305 result.valueString = valueString;
2306 }
2307 }
2308
AddUdmfData(const RefPtr<Ace::DragEvent> & event)2309 void TextPattern::AddUdmfData(const RefPtr<Ace::DragEvent>& event)
2310 {
2311 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
2312 if (isSpanStringMode_) {
2313 std::vector<uint8_t> arr;
2314 auto dragSpanString = styledString_->GetSubSpanString(recoverStart_, recoverEnd_ - recoverStart_,
2315 false, true, false);
2316 dragSpanString->EncodeTlv(arr);
2317 UdmfClient::GetInstance()->AddSpanStringRecord(unifiedData, arr);
2318 } else {
2319 ProcessNormalUdmfData(unifiedData);
2320 }
2321 event->SetData(unifiedData);
2322 }
2323
ProcessNormalUdmfData(const RefPtr<UnifiedData> & unifiedData)2324 void TextPattern::ProcessNormalUdmfData(const RefPtr<UnifiedData>& unifiedData)
2325 {
2326 std::list<ResultObject> finalResult;
2327 auto type = SelectSpanType::TYPESPAN;
2328 for (const auto& resultObj : dragResultObjects_) {
2329 if (finalResult.empty() || resultObj.type != SelectSpanType::TYPESPAN || type != SelectSpanType::TYPESPAN) {
2330 type = resultObj.type;
2331 finalResult.emplace_back(resultObj);
2332 if (resultObj.type == SelectSpanType::TYPESPAN) {
2333 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), false);
2334 }
2335 } else {
2336 AddUdmfTxtPreProcessor(resultObj, finalResult.back(), true);
2337 }
2338 }
2339 auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
2340 auto pattern = weak.Upgrade();
2341 CHECK_NULL_VOID(pattern);
2342 std::string u8ValueString = UtfUtils::Str16DebugToStr8(result.valueString);
2343 if (result.type == SelectSpanType::TYPESPAN) {
2344 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, u8ValueString);
2345 return;
2346 }
2347 if (result.type == SelectSpanType::TYPEIMAGE) {
2348 if (result.valuePixelMap) {
2349 pattern->AddPixelMapToUdmfData(result.valuePixelMap, unifiedData);
2350 } else if (u8ValueString.size() > 1) {
2351 UdmfClient::GetInstance()->AddImageRecord(unifiedData, u8ValueString);
2352 } else {
2353 // builder span, fill pixelmap data
2354 auto builderNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(result.spanPosition.spanIndex));
2355 CHECK_NULL_VOID(builderNode);
2356 pattern->AddPixelMapToUdmfData(builderNode->GetDragPixelMap(), unifiedData);
2357 }
2358 }
2359 };
2360 for (const auto& resultObj : finalResult) {
2361 resultProcessor(resultObj);
2362 }
2363 }
2364
AddPixelMapToUdmfData(const RefPtr<PixelMap> & pixelMap,const RefPtr<UnifiedData> & unifiedData)2365 void TextPattern::AddPixelMapToUdmfData(const RefPtr<PixelMap>& pixelMap, const RefPtr<UnifiedData>& unifiedData)
2366 {
2367 CHECK_NULL_VOID(pixelMap && unifiedData);
2368 const uint8_t* pixels = pixelMap->GetPixels();
2369 CHECK_NULL_VOID(pixels);
2370 int32_t length = pixelMap->GetByteCount();
2371 std::vector<uint8_t> data(pixels, pixels + length);
2372 PixelMapRecordDetails details = { pixelMap->GetWidth(), pixelMap->GetHeight(),
2373 pixelMap->GetPixelFormat(), pixelMap->GetAlphaType() };
2374 UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
2375 }
2376
CloseOperate()2377 void TextPattern::CloseOperate()
2378 {
2379 UpdateSpanItemDragStatus(dragResultObjects_, true);
2380 recoverDragResultObjects_ = dragResultObjects_;
2381 AceEngineExt::GetInstance().DragStartExt();
2382 CloseKeyboard(true);
2383 CloseSelectOverlay();
2384 ResetSelection();
2385 }
2386
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)2387 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
2388 {
2389 auto weakPtr = WeakClaim(this);
2390 DragDropInfo itemInfo;
2391 auto pattern = weakPtr.Upgrade();
2392 auto host = pattern->GetHost();
2393 auto hub = host->GetEventHub<EventHub>();
2394 auto gestureHub = hub->GetOrCreateGestureEventHub();
2395 CHECK_NULL_RETURN(gestureHub, itemInfo);
2396 if (!gestureHub->GetIsTextDraggable()) {
2397 return itemInfo;
2398 }
2399 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
2400 dragBoxes_ = GetTextBoxes();
2401 pattern->status_ = Status::DRAGGING;
2402 pattern->contentMod_->ChangeDragStatus();
2403 pattern->showSelect_ = false;
2404 auto start = textSelector_.GetTextStart();
2405 pattern->recoverStart_ = start;
2406 auto end = textSelector_.GetTextEnd();
2407 pattern->recoverEnd_ = end;
2408 auto beforeStr = GetSelectedText(0, start, false, true);
2409 auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), false, true);
2410 auto afterStr = GetSelectedText(end, textForDisplay_.length(), false, true);
2411 pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
2412 auto selectedUtf8Str = UtfUtils::Str16DebugToStr8(selectedStr);
2413 itemInfo.extraInfo = selectedUtf8Str;
2414 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
2415 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedUtf8Str);
2416 event->SetData(unifiedData);
2417 host->MarkDirtyWithOnProChange(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1
2418 ? PROPERTY_UPDATE_MEASURE_SELF
2419 : PROPERTY_UPDATE_MEASURE);
2420
2421 CloseSelectOverlay();
2422 ResetSelection();
2423 return itemInfo;
2424 }
2425
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)2426 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
2427 {
2428 if (resultObjects.empty()) {
2429 return;
2430 }
2431 auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
2432 auto pattern = weakPtr.Upgrade();
2433 CHECK_NULL_VOID(pattern);
2434 if (pattern->spans_.empty()) {
2435 return;
2436 }
2437 auto it = pattern->spans_.begin();
2438 if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
2439 std::advance(it, !pattern->spans_.empty() ? static_cast<int32_t>(pattern->spans_.size()) - 1 : 0);
2440 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
2441 } else {
2442 std::advance(it, resultObj.spanPosition.spanIndex);
2443 }
2444 auto spanItem = *it;
2445 CHECK_NULL_VOID(spanItem);
2446 if (resultObj.type == SelectSpanType::TYPESPAN) {
2447 if (pattern->isSpanStringMode_) {
2448 spanItem = resultObj.span.Upgrade();
2449 CHECK_NULL_VOID(spanItem);
2450 }
2451 if (isDragging) {
2452 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
2453 resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
2454 pattern->dragSpanItems_.emplace_back(spanItem);
2455 } else {
2456 spanItem->EndDrag();
2457 }
2458 return;
2459 }
2460
2461 if (resultObj.type == SelectSpanType::TYPEIMAGE) {
2462 if (isDragging) {
2463 pattern->dragSpanItems_.emplace_back(spanItem);
2464 }
2465 auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
2466 CHECK_NULL_VOID(imageNode);
2467 auto renderContext = imageNode->GetRenderContext();
2468 CHECK_NULL_VOID(renderContext);
2469 renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
2470 imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
2471 }
2472 };
2473 for (const auto& resultObj : resultObjects) {
2474 dragStatusUpdateAction(resultObj);
2475 }
2476 }
2477
OnDragEnd(const RefPtr<Ace::DragEvent> & event)2478 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
2479 {
2480 ResetDragRecordSize(-1);
2481 auto wk = WeakClaim(this);
2482 auto pattern = wk.Upgrade();
2483 CHECK_NULL_VOID(pattern);
2484 auto host = GetHost();
2485 CHECK_NULL_VOID(host);
2486 isMousePressed_ = false;
2487 if (status_ == Status::DRAGGING) {
2488 status_ = Status::NONE;
2489 }
2490 dragSpanItems_.clear();
2491 if (dragResultObjects_.empty()) {
2492 return;
2493 }
2494 UpdateSpanItemDragStatus(dragResultObjects_, false);
2495 dragResultObjects_.clear();
2496 if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
2497 HandleSelectionChange(recoverStart_, recoverEnd_);
2498 isShowMenu_ = false;
2499 if (GetCurrentDragTool() == SourceTool::FINGER) {
2500 CalculateHandleOffsetAndShowOverlay();
2501 ShowSelectOverlay({ .menuIsShow = false });
2502 }
2503 }
2504 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
2505 }
2506
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)2507 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
2508 {
2509 auto wk = WeakClaim(this);
2510 auto pattern = wk.Upgrade();
2511 CHECK_NULL_VOID(pattern);
2512 auto host = pattern->GetHost();
2513 CHECK_NULL_VOID(host);
2514 isMousePressed_ = false;
2515 if (pattern->status_ == Status::DRAGGING) {
2516 pattern->status_ = Status::NONE;
2517 pattern->MarkContentChange();
2518 pattern->contentMod_->ChangeDragStatus();
2519 if (event && event->GetResult() != DragRet::DRAG_SUCCESS && IsSelectableAndCopy()) {
2520 HandleSelectionChange(recoverStart_, recoverEnd_);
2521 isShowMenu_ = false;
2522 if (GetCurrentDragTool() == SourceTool::FINGER) {
2523 CalculateHandleOffsetAndShowOverlay();
2524 ShowSelectOverlay({ .menuIsShow = false });
2525 }
2526 }
2527 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
2528 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
2529 }
2530 }
2531
OnDragMove(const RefPtr<Ace::DragEvent> & event)2532 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
2533 {
2534 auto weakPtr = WeakClaim(this);
2535 auto pattern = weakPtr.Upgrade();
2536 if (pattern->status_ == Status::DRAGGING) {
2537 CloseSelectOverlay();
2538 pattern->showSelect_ = false;
2539 }
2540 }
2541
InitDragEvent()2542 void TextPattern::InitDragEvent()
2543 {
2544 auto host = GetHost();
2545 CHECK_NULL_VOID(host);
2546 auto eventHub = host->GetEventHub<EventHub>();
2547 CHECK_NULL_VOID(eventHub);
2548 auto gestureHub = host->GetOrCreateGestureEventHub();
2549 CHECK_NULL_VOID(gestureHub);
2550 gestureHub->InitDragDropEvent();
2551 gestureHub->SetTextDraggable(true);
2552 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
2553 auto onDragStart = [weakPtr = WeakClaim(this)](
2554 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
2555 NG::DragDropInfo itemInfo;
2556 auto pattern = weakPtr.Upgrade();
2557 CHECK_NULL_RETURN(pattern, itemInfo);
2558 auto eventHub = pattern->GetEventHub<EventHub>();
2559 CHECK_NULL_RETURN(eventHub, itemInfo);
2560 pattern->SetCurrentDragTool(event->GetSourceTool());
2561 if (pattern->spans_.empty() && !pattern->isSpanStringMode_) {
2562 return pattern->OnDragStartNoChild(event, extraParams);
2563 }
2564 return pattern->OnDragStart(event, extraParams);
2565 };
2566 eventHub->SetDefaultOnDragStart(std::move(onDragStart));
2567 auto onDragMove = [weakPtr = WeakClaim(this)](
2568 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
2569 auto pattern = weakPtr.Upgrade();
2570 CHECK_NULL_VOID(pattern);
2571 pattern->OnDragMove(event);
2572 };
2573 eventHub->SetOnDragMove(std::move(onDragMove));
2574 auto onDragEnd = [weakPtr = WeakClaim(this)](const RefPtr<OHOS::Ace::DragEvent>& event) {
2575 auto pattern = weakPtr.Upgrade();
2576 CHECK_NULL_VOID(pattern);
2577 // 拖拽框架强引用导致退出页面后还能够运行到这里
2578 if (pattern->isDetachFromMainTree_) {
2579 return;
2580 }
2581 ContainerScope scope(pattern->GetHostInstanceId());
2582 pattern->showSelect_ = true;
2583 if (pattern->spans_.empty()) {
2584 pattern->OnDragEndNoChild(event);
2585 } else {
2586 pattern->OnDragEnd(event);
2587 }
2588 };
2589 eventHub->SetOnDragEnd(std::move(onDragEnd));
2590 }
2591
ClearDragEvent()2592 void TextPattern::ClearDragEvent()
2593 {
2594 auto host = GetHost();
2595 CHECK_NULL_VOID(host);
2596 auto eventHub = host->GetEventHub<EventHub>();
2597 CHECK_NULL_VOID(eventHub);
2598 auto gestureHub = host->GetOrCreateGestureEventHub();
2599 CHECK_NULL_VOID(gestureHub);
2600 gestureHub->SetTextDraggable(false);
2601 gestureHub->SetIsTextDraggable(false);
2602 gestureHub->SetThumbnailCallback(nullptr);
2603 eventHub->SetDefaultOnDragStart(nullptr);
2604 eventHub->SetOnDragMove(nullptr);
2605 eventHub->SetOnDragEnd(nullptr);
2606 }
2607
GetThumbnailCallback()2608 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
2609 {
2610 return [wk = WeakClaim(this)](const Offset& point) {
2611 auto pattern = wk.Upgrade();
2612 CHECK_NULL_VOID(pattern);
2613 if (pattern->BetweenSelectedPosition(point)) {
2614 const auto& children = pattern->GetChildNodes();
2615 std::list<RefPtr<FrameNode>> imageChildren;
2616 for (const auto& child : children) {
2617 auto node = DynamicCast<FrameNode>(child);
2618 if (!node) {
2619 continue;
2620 }
2621 auto image = node->GetPattern<ImagePattern>();
2622 if (image) {
2623 imageChildren.emplace_back(node);
2624 }
2625 }
2626 auto info = pattern->CreateTextDragInfo();
2627 pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren, info);
2628 auto textDragPattern = pattern->dragNode_->GetPattern<TextDragPattern>();
2629 if (textDragPattern) {
2630 auto option = pattern->GetHost()->GetDragPreviewOption();
2631 option.options.shadowPath = textDragPattern->GetBackgroundPath()->ConvertToSVGString();
2632 option.options.shadow = Shadow(RICH_DEFAULT_ELEVATION, {0.0, 0.0}, Color(RICH_DEFAULT_SHADOW_COLOR),
2633 ShadowStyle::OuterFloatingSM);
2634 pattern->GetHost()->SetDragPreviewOptions(option);
2635 }
2636 FrameNode::ProcessOffscreenNode(pattern->dragNode_);
2637 }
2638 };
2639 }
2640
CreateTextDragInfo()2641 TextDragInfo TextPattern::CreateTextDragInfo()
2642 {
2643 TextDragInfo info;
2644 auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
2645 CHECK_NULL_RETURN(context, info);
2646 auto theme = context->GetTheme<TextTheme>();
2647 CHECK_NULL_RETURN(theme, info);
2648 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2649 CHECK_NULL_RETURN(textLayoutProperty, info);
2650 info.handleColor = theme->GetCaretColor();
2651 info.selectedBackgroundColor = theme->GetSelectedColor();
2652 auto selectOverlayInfo = selectOverlay_->GetSelectOverlayInfos();
2653 if (selectOverlayInfo.has_value()) {
2654 if (selectOverlayInfo->firstHandle.isShow) {
2655 info.firstHandle = selectOverlayInfo->firstHandle.paintRect;
2656 }
2657 if (selectOverlayInfo->secondHandle.isShow) {
2658 info.secondHandle = selectOverlayInfo->secondHandle.paintRect;
2659 }
2660 }
2661 return info;
2662 }
2663
GetAllChildren() const2664 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
2665 {
2666 return childNodes_;
2667 }
2668
GetSelectedSpanText(std::u16string value,int32_t start,int32_t end,bool includeStartHalf,bool includeEndHalf,bool getSubstrDirectly) const2669 std::u16string TextPattern::GetSelectedSpanText(std::u16string value, int32_t start, int32_t end, bool includeStartHalf,
2670 bool includeEndHalf, bool getSubstrDirectly) const
2671 {
2672 if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
2673 return u"";
2674 }
2675 auto min = std::min(start, end);
2676 auto max = std::max(start, end);
2677 if (getSubstrDirectly) {
2678 min = std::clamp(min, 0, static_cast<int32_t>(value.length()));
2679 return value.substr(min, max - min);
2680 } else {
2681 return TextEmojiProcessor::SubU16string(min, max - min, value, includeStartHalf, includeEndHalf);
2682 }
2683 }
2684
GetTextStyleObject(const RefPtr<SpanNode> & node)2685 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
2686 {
2687 TextStyleResult textStyle;
2688 textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
2689 textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
2690 textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
2691 std::string fontFamilyValue;
2692 const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
2693 auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
2694 for (const auto& str : fontFamily) {
2695 fontFamilyValue += str;
2696 fontFamilyValue += ",";
2697 }
2698 fontFamilyValue =
2699 fontFamilyValue.substr(0, !fontFamilyValue.empty() ? static_cast<int32_t>(fontFamilyValue.size()) - 1 : 0);
2700 textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
2701 textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationValue(TextDecoration::NONE));
2702 textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
2703 textStyle.decorationStyle = static_cast<int32_t>(node->GetTextDecorationStyleValue(TextDecorationStyle::SOLID));
2704 textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
2705 auto lm = node->GetLeadingMarginValue({});
2706 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
2707 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToFp();
2708 textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToFp();
2709 textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToFp();
2710 textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToFp();
2711 } else {
2712 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
2713 textStyle.lineHeight = node->GetLineHeightValue(Dimension()).ConvertToVp();
2714 textStyle.letterSpacing = node->GetLetterSpacingValue(Dimension()).ConvertToVp();
2715 textStyle.lineSpacing = node->GetLineSpacingValue(Dimension()).ConvertToVp();
2716 }
2717 textStyle.halfLeading = node->GetHalfLeadingValue(false);
2718 textStyle.fontFeature = node->GetFontFeatureValue(ParseFontFeatureSettings("\"pnum\" 1"));
2719 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = lm.size.Width().ToString();
2720 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = lm.size.Height().ToString();
2721 textStyle.wordBreak = static_cast<int32_t>(node->GetWordBreakValue(WordBreak::BREAK_WORD));
2722 textStyle.lineBreakStrategy = static_cast<int32_t>(node->GetLineBreakStrategyValue(LineBreakStrategy::GREEDY));
2723 textStyle.textShadows = node->GetTextShadowValue({});
2724 textStyle.textBackgroundStyle = node->GetTextBackgroundStyle();
2725 textStyle.paragraphSpacing = node->GetParagraphSpacing();
2726 return textStyle;
2727 }
2728
GetChildByIndex(int32_t index) const2729 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
2730 {
2731 const auto& children = childNodes_;
2732 int32_t size = static_cast<int32_t>(children.size());
2733 if (index < 0 || index >= size) {
2734 return nullptr;
2735 }
2736 auto pos = children.begin();
2737 std::advance(pos, index);
2738 return *pos;
2739 }
2740
GetSpanItemByIndex(int32_t index) const2741 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
2742 {
2743 int32_t size = static_cast<int32_t>(spans_.size());
2744 if (index < 0 || index >= size) {
2745 return nullptr;
2746 }
2747 auto pos = spans_.begin();
2748 std::advance(pos, index);
2749 return *pos;
2750 }
2751
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2752 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2753 {
2754 bool selectFlag = false;
2755 ResultObject resultObject;
2756 if (!DynamicCast<SpanNode>(uinode)) {
2757 return resultObject;
2758 }
2759 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
2760 int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
2761 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
2762 int32_t startPosition = endPosition - itemLength;
2763
2764 if (startPosition >= start && endPosition <= end) {
2765 selectFlag = true;
2766 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2767 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2768 } else if (startPosition < start && endPosition <= end && endPosition > start) {
2769 selectFlag = true;
2770 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2771 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2772 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
2773 selectFlag = true;
2774 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2775 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2776 } else if (startPosition <= start && endPosition >= end) {
2777 selectFlag = true;
2778 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2779 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2780 }
2781 if (selectFlag) {
2782 resultObject.spanPosition.spanIndex = index;
2783 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2784 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2785 resultObject.type = SelectSpanType::TYPESPAN;
2786 SetResultObjectText(resultObject, spanItem);
2787 auto spanNode = DynamicCast<SpanNode>(uinode);
2788 resultObject.textStyle = GetTextStyleObject(spanNode);
2789 }
2790 return resultObject;
2791 }
2792
SetResultObjectText(ResultObject & resultObject,const RefPtr<SpanItem> & spanItem)2793 void TextPattern::SetResultObjectText(ResultObject& resultObject, const RefPtr<SpanItem>& spanItem)
2794 {
2795 CHECK_NULL_VOID(spanItem);
2796 resultObject.valueString = spanItem->content;
2797 }
2798
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2799 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2800 {
2801 bool selectFlag = false;
2802 ResultObject resultObject;
2803 resultObject.isDraggable = false;
2804 if (!DynamicCast<SpanNode>(uinode)) {
2805 return resultObject;
2806 }
2807 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
2808 int32_t itemLength = static_cast<int32_t>(spanItem->content.length());
2809 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
2810 int32_t startPosition = endPosition - itemLength;
2811
2812 if (startPosition >= start && endPosition <= end) {
2813 selectFlag = true;
2814 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2815 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2816 } else if (startPosition < start && endPosition <= end && endPosition > start) {
2817 selectFlag = true;
2818 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2819 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2820 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
2821 selectFlag = true;
2822 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2823 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2824 } else if (startPosition <= start && endPosition >= end) {
2825 selectFlag = true;
2826 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
2827 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
2828 }
2829 if (selectFlag) {
2830 resultObject.valueResource = spanItem->GetResourceObject();
2831 resultObject.spanPosition.spanIndex = index;
2832 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2833 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2834 resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
2835 resultObject.valueString = UtfUtils::Str8ToStr16(std::to_string(spanItem->unicode));
2836 auto spanNode = DynamicCast<SpanNode>(uinode);
2837 resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
2838 }
2839 return resultObject;
2840 }
2841
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)2842 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
2843 {
2844 SymbolSpanStyle symbolSpanStyle;
2845 std::string symbolColorValue;
2846 auto symbolColors = node->GetSymbolColorList();
2847 if (symbolColors.has_value()) {
2848 for (const auto& color : *symbolColors) {
2849 symbolColorValue += color.ColorToString() + ",";
2850 }
2851 }
2852 symbolColorValue =
2853 symbolColorValue.substr(0, !symbolColorValue.empty() ? static_cast<int32_t>(symbolColorValue.size()) - 1 : 0);
2854 symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
2855 if (AceApplicationInfo::GetInstance().GreatOrEqualTargetAPIVersion(PlatformVersion::VERSION_TWELVE)) {
2856 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToFp();
2857 } else {
2858 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
2859 }
2860 symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
2861 symbolSpanStyle.renderingStrategy = static_cast<int32_t>(node->GetSymbolRenderingStrategyValue(0));
2862 symbolSpanStyle.effectStrategy = static_cast<int32_t>(node->GetSymbolEffectStrategyValue(0));
2863 return symbolSpanStyle;
2864 }
2865
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)2866 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
2867 {
2868 int32_t itemLength = 1;
2869 ResultObject resultObject;
2870 if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
2871 return resultObject;
2872 }
2873 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
2874 int32_t startPosition = endPosition - itemLength;
2875 if ((start <= startPosition) && (end >= endPosition)) {
2876 auto imageNode = DynamicCast<FrameNode>(uinode);
2877 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
2878 resultObject.spanPosition.spanIndex = index;
2879 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2880 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2881 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2882 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2883 resultObject.type = SelectSpanType::TYPEIMAGE;
2884 if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
2885 resultObject.valueString = UtfUtils::Str8DebugToStr16(imageLayoutProperty->GetImageSourceInfo()->GetSrc());
2886 } else {
2887 resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
2888 }
2889 auto geometryNode = imageNode->GetGeometryNode();
2890 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
2891 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
2892 if (imageLayoutProperty->HasImageFit()) {
2893 resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
2894 }
2895 if (imageLayoutProperty->HasVerticalAlign()) {
2896 resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
2897 }
2898 if (imageLayoutProperty->GetMarginProperty()) {
2899 resultObject.imageStyle.margin = imageLayoutProperty->GetMarginProperty()->ToString();
2900 }
2901 auto imageRenderCtx = imageNode->GetRenderContext();
2902 if (imageRenderCtx->GetBorderRadius()) {
2903 BorderRadiusProperty brp;
2904 auto jsonObject = JsonUtil::Create(true);
2905 auto jsonBorder = JsonUtil::Create(true);
2906 InspectorFilter emptyFilter;
2907 imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder, emptyFilter);
2908 resultObject.imageStyle.borderRadius = jsonObject->GetValue("borderRadius")->IsObject()
2909 ? jsonObject->GetValue("borderRadius")->ToString()
2910 : jsonObject->GetString("borderRadius");
2911 }
2912 }
2913 return resultObject;
2914 }
2915
2916 // ===========================================================
2917 // TextDragBase implementations
GetLineHeight() const2918 float TextPattern::GetLineHeight() const
2919 {
2920 auto selectedRects = pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
2921 CHECK_NULL_RETURN(selectedRects.size(), {});
2922 return selectedRects.front().Height();
2923 }
2924
GetTextBoxes()2925 std::vector<RectF> TextPattern::GetTextBoxes()
2926 {
2927 return pManager_->GetRects(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
2928 }
2929
GetParentGlobalOffset() const2930 OffsetF TextPattern::GetParentGlobalOffset() const
2931 {
2932 selectOverlay_->UpdateHandleGlobalOffset();
2933 auto host = GetHost();
2934 CHECK_NULL_RETURN(host, {});
2935 auto pipeline = host->GetContext();
2936 CHECK_NULL_RETURN(pipeline, {});
2937 auto rootOffset = pipeline->GetRootRect().GetOffset();
2938 return host->GetPaintRectOffset(false, true) - rootOffset;
2939 }
2940
CreateHandles()2941 void TextPattern::CreateHandles()
2942 {
2943 if (IsDragging()) {
2944 TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging");
2945 return;
2946 }
2947 ShowSelectOverlay({ .menuIsShow = false });
2948 }
2949
BetweenSelectedPosition(const Offset & globalOffset)2950 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
2951 {
2952 auto host = GetHost();
2953 CHECK_NULL_RETURN(host, false);
2954 auto offset = host->GetPaintRectOffset(false, true);
2955 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
2956 if (selectOverlay_->HasRenderTransform()) {
2957 localOffset = ConvertGlobalToLocalOffset(globalOffset);
2958 }
2959 return IsDraggable(localOffset);
2960 }
2961
LogForFormRender(const std::string & logTag)2962 void TextPattern::LogForFormRender(const std::string& logTag)
2963 {
2964 auto host = GetHost();
2965 CHECK_NULL_VOID(host);
2966 auto pipeline = host->GetContext();
2967 CHECK_NULL_VOID(pipeline);
2968 if (pipeline->IsFormRender() && !IsSetObscured() && !IsSensitiveEnalbe()) {
2969 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2970 CHECK_NULL_VOID(textLayoutProperty);
2971 auto content = textLayoutProperty->GetContent().value_or(u"");
2972 if (content.length() == 1) {
2973 TAG_LOGI(AceLogTag::ACE_TEXT, "%{public}s, content:%{public}s id:%{public}d", logTag.c_str(),
2974 UtfUtils::Str16ToStr8(content).c_str(), host->GetId());
2975 }
2976 }
2977 }
2978
2979 // end of TextDragBase implementations
2980 // ===========================================================
2981
OnModifyDone()2982 void TextPattern::OnModifyDone()
2983 {
2984 Pattern::OnModifyDone();
2985 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2986 CHECK_NULL_VOID(textLayoutProperty);
2987 auto host = GetHost();
2988 CHECK_NULL_VOID(host);
2989 auto renderContext = host->GetRenderContext();
2990 CHECK_NULL_VOID(renderContext);
2991 auto nowTime = static_cast<unsigned long long>(GetSystemTimestamp());
2992 ACE_TEXT_SCOPED_TRACE("OnModifyDone[Text][id:%d][time:%llu]", host->GetId(), nowTime);
2993 auto logTag = "OnModifyDone:" + std::to_string(nowTime);
2994 LogForFormRender(logTag);
2995 auto pipeline = host->GetContext();
2996 if (!(pipeline && pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
2997 bool shouldClipToContent =
2998 textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
2999 host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
3000 }
3001 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
3002 if (!renderContext->GetClipEdge().has_value()) {
3003 renderContext->UpdateClipEdge(true);
3004 renderContext->SetClipToFrame(true);
3005 }
3006 UpdateMarqueeStartPolicy();
3007 }
3008 const auto& children = host->GetChildren();
3009 if (children.empty()) {
3010 std::u16string textCache = textForDisplay_;
3011 if (!isSpanStringMode_) {
3012 textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
3013 }
3014 if (textCache != textForDisplay_) {
3015 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
3016 UtfUtils::Str16DebugToStr8(textForDisplay_));
3017 dataDetectorAdapter_->aiDetectInitialized_ = false;
3018 CloseSelectOverlay();
3019 ResetSelection();
3020 }
3021
3022 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
3023 ParseOriText(textForDisplay_);
3024 }
3025 }
3026 RecoverCopyOption();
3027 }
3028
UpdateMarqueeStartPolicy()3029 void TextPattern::UpdateMarqueeStartPolicy()
3030 {
3031 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3032 CHECK_NULL_VOID(textLayoutProperty);
3033 if (!textLayoutProperty->HasTextMarqueeStartPolicy()) {
3034 auto host = GetHost();
3035 CHECK_NULL_VOID(host);
3036 auto context = host->GetContext();
3037 CHECK_NULL_VOID(context);
3038 auto theme = context->GetTheme<TextTheme>();
3039 CHECK_NULL_VOID(theme);
3040 textLayoutProperty->UpdateTextMarqueeStartPolicy(theme->GetMarqueeStartPolicy());
3041 }
3042 if (textLayoutProperty->GetTextMarqueeStartPolicyValue(MarqueeStartPolicy::DEFAULT) ==
3043 MarqueeStartPolicy::ON_FOCUS) {
3044 InitFocusEvent();
3045 InitHoverEvent();
3046 }
3047 }
3048
SetActionExecSubComponent()3049 bool TextPattern::SetActionExecSubComponent()
3050 {
3051 auto host = GetHost();
3052 CHECK_NULL_RETURN(host, false);
3053 auto accessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
3054 CHECK_NULL_RETURN(accessibilityProperty, false);
3055 accessibilityProperty->SetActionExecSubComponent([weakPtr = WeakClaim(this)](int32_t spanId) -> bool {
3056 const auto& pattern = weakPtr.Upgrade();
3057 CHECK_NULL_RETURN(pattern, false);
3058 return pattern->ExecSubComponent(spanId);
3059 });
3060 return true;
3061 }
3062
GetSubComponentInfos(std::vector<SubComponentInfo> & subComponentInfos)3063 size_t TextPattern::GetSubComponentInfos(std::vector<SubComponentInfo>& subComponentInfos)
3064 {
3065 subComponentInfos.clear();
3066 subComponentInfos_.clear();
3067 if (spans_.empty()) {
3068 GetSubComponentInfosForAISpans(subComponentInfos);
3069 } else {
3070 GetSubComponentInfosForSpans(subComponentInfos);
3071 }
3072 SetActionExecSubComponent();
3073 return subComponentInfos.size();
3074 }
3075
GetSubComponentInfosForAISpans(std::vector<SubComponentInfo> & subComponentInfos)3076 void TextPattern::GetSubComponentInfosForAISpans(std::vector<SubComponentInfo>& subComponentInfos)
3077 {
3078 CHECK_NULL_VOID(dataDetectorAdapter_);
3079 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
3080 auto& aiSpan = kv.second;
3081 AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3082 }
3083 }
3084
GetSubComponentInfosForSpans(std::vector<SubComponentInfo> & subComponentInfos)3085 void TextPattern::GetSubComponentInfosForSpans(std::vector<SubComponentInfo>& subComponentInfos)
3086 {
3087 for (const auto& span : spans_) {
3088 if (span == nullptr) {
3089 continue; // skip null
3090 }
3091 if ((span->imageNodeId >= 0) || (span->unicode > 0)) {
3092 continue; // skip ImageSpan and SymbolSpan
3093 }
3094 if (span->spanItemType == SpanItemType::CustomSpan) {
3095 continue; // skip CustomSpan
3096 }
3097 auto placeholderSpan = DynamicCast<PlaceholderSpanItem>(span);
3098 if ((placeholderSpan != nullptr) && (placeholderSpan->placeholderSpanNodeId >=0)) {
3099 continue; // skip PlaceholderSpan
3100 }
3101 if (span->content.empty()) {
3102 continue; // skip empty text
3103 }
3104 AddSubComponentInfoForSpan(subComponentInfos, UtfUtils::Str16DebugToStr8(span->content), span);
3105 AddSubComponentInfosByDataDetectorForSpan(subComponentInfos, span);
3106 }
3107 }
3108
AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo> & subComponentInfos,const RefPtr<SpanItem> & span)3109 void TextPattern::AddSubComponentInfosByDataDetectorForSpan(std::vector<SubComponentInfo>& subComponentInfos,
3110 const RefPtr<SpanItem>& span)
3111 {
3112 CHECK_NULL_VOID(span);
3113 CHECK_NULL_VOID(dataDetectorAdapter_);
3114 int32_t wSpanContentLength = static_cast<int32_t>(span->content.length());
3115 int32_t spanStart = span->position - wSpanContentLength;
3116 if (span->needRemoveNewLine) {
3117 spanStart -= 1;
3118 }
3119 int32_t preEnd = spanStart;
3120 auto aiSpanMap = dataDetectorAdapter_->aiSpanMap_;
3121 while (!aiSpanMap.empty()) {
3122 auto aiSpan = aiSpanMap.begin()->second;
3123 if (aiSpan.start >= span->position || preEnd >= span->position) {
3124 break;
3125 }
3126 int32_t aiSpanStartInSpan = std::max(spanStart, aiSpan.start);
3127 int32_t aiSpanEndInSpan = std::min(span->position, aiSpan.end);
3128 if (aiSpan.end <= spanStart || aiSpanStartInSpan < preEnd) {
3129 TAG_LOGI(AceLogTag::ACE_TEXT, "Error prediction");
3130 aiSpanMap.erase(aiSpanMap.begin());
3131 continue;
3132 }
3133 AddSubComponentInfoForAISpan(subComponentInfos, aiSpan.content, aiSpan);
3134 preEnd = aiSpanEndInSpan;
3135 if (aiSpan.end > span->position) {
3136 return;
3137 } else {
3138 aiSpanMap.erase(aiSpanMap.begin());
3139 }
3140 }
3141 }
3142
ExecSubComponent(int32_t spanId)3143 bool TextPattern::ExecSubComponent(int32_t spanId)
3144 {
3145 if ((spanId < 0) || (spanId >= static_cast<int32_t>(subComponentInfos_.size()))) {
3146 return false;
3147 }
3148 auto subComponentInfo = subComponentInfos_[spanId];
3149 if (subComponentInfo.aiSpan.has_value()) {
3150 CHECK_NULL_RETURN(dataDetectorAdapter_, false);
3151 dataDetectorAdapter_->ResponseBestMatchItem(subComponentInfo.aiSpan.value());
3152 return true;
3153 }
3154 const auto& span = subComponentInfo.span.Upgrade();
3155 CHECK_NULL_RETURN(span, false);
3156 CHECK_NULL_RETURN(span->onClick, false);
3157 GestureEvent info;
3158 std::chrono::microseconds microseconds(GetMicroTickCount());
3159 TimeStamp time(microseconds);
3160 info.SetTimeStamp(time);
3161 span->onClick(info);
3162 return true;
3163 }
3164
AddSubComponentInfoForSpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const RefPtr<SpanItem> & span)3165 void TextPattern::AddSubComponentInfoForSpan(std::vector<SubComponentInfo>& subComponentInfos,
3166 const std::string& content, const RefPtr<SpanItem>& span)
3167 {
3168 CHECK_NULL_VOID(span);
3169 CHECK_NULL_VOID(span->onClick); // skip null onClick
3170 SubComponentInfo subComponentInfo;
3171 subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
3172 subComponentInfo.spanText = content;
3173 if (span->accessibilityProperty == nullptr) {
3174 subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
3175 } else {
3176 subComponentInfo.accessibilityText = span->accessibilityProperty->GetAccessibilityText();
3177 subComponentInfo.accessibilityDescription =
3178 span->accessibilityProperty->GetAccessibilityDescription();
3179 subComponentInfo.accessibilityLevel = span->accessibilityProperty->GetAccessibilityLevel();
3180 }
3181 subComponentInfos.emplace_back(subComponentInfo);
3182
3183 SubComponentInfoEx subComponentInfoEx;
3184 subComponentInfoEx.span = span;
3185 subComponentInfos_.emplace_back(subComponentInfoEx);
3186 }
3187
AddSubComponentInfoForAISpan(std::vector<SubComponentInfo> & subComponentInfos,const std::string & content,const AISpan & aiSpan)3188 void TextPattern::AddSubComponentInfoForAISpan(std::vector<SubComponentInfo>& subComponentInfos,
3189 const std::string& content, const AISpan& aiSpan)
3190 {
3191 SubComponentInfo subComponentInfo;
3192 subComponentInfo.spanId = static_cast<int32_t>(subComponentInfos.size());
3193 subComponentInfo.spanText = content;
3194 subComponentInfo.accessibilityLevel = AccessibilityProperty::Level::AUTO;
3195 subComponentInfos.emplace_back(subComponentInfo);
3196
3197 SubComponentInfoEx subComponentInfoEx;
3198 subComponentInfoEx.aiSpan = aiSpan;
3199 subComponentInfos_.emplace_back(subComponentInfoEx);
3200 }
3201
ToJsonValue(std::unique_ptr<JsonValue> & json,const InspectorFilter & filter) const3202 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json, const InspectorFilter& filter) const
3203 {
3204 json->PutFixedAttr("content", UtfUtils::Str16ToStr8(textForDisplay_).c_str(), filter, FIXED_ATTR_CONTENT);
3205 /* no fixed attr below, just return */
3206 if (filter.IsFastFilter()) {
3207 return;
3208 }
3209 json->PutExtAttr("enableDataDetector", textDetectEnable_ ? "true" : "false", filter);
3210 json->PutExtAttr("dataDetectorConfig", dataDetectorAdapter_->textDetectConfigStr_.c_str(), filter);
3211 const auto& selector = GetTextSelector();
3212 auto result = "[" + std::to_string(selector.GetTextStart()) + "," + std::to_string(selector.GetTextEnd()) + "]";
3213 json->PutExtAttr("selection", result.c_str(), filter);
3214 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3215 CHECK_NULL_VOID(textLayoutProp);
3216 json->PutExtAttr("fontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
3217 if (textStyle_.has_value() && textStyle_->GetAdaptTextSize()) {
3218 auto adaptedFontSize = textStyle_->GetFontSize();
3219 json->PutExtAttr("actualFontSize", adaptedFontSize.ToString().c_str(), filter);
3220 } else {
3221 json->PutExtAttr("actualFontSize", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str(), filter);
3222 }
3223 json->PutExtAttr("font", GetFontInJson().c_str(), filter);
3224 json->PutExtAttr("bindSelectionMenu", GetBindSelectionMenuInJson().c_str(), filter);
3225 json->PutExtAttr("caretColor", GetCaretColor().c_str(), filter);
3226 json->PutExtAttr("selectedBackgroundColor", GetSelectedBackgroundColor().c_str(), filter);
3227 json->PutExtAttr("enableHapticFeedback", isEnableHapticFeedback_ ? "true" : "false", filter);
3228 }
3229
GetBindSelectionMenuInJson() const3230 std::string TextPattern::GetBindSelectionMenuInJson() const
3231 {
3232 auto jsonArray = JsonUtil::CreateArray(true);
3233 for (auto& [spanResponsePair, params] : selectionMenuMap_) {
3234 auto& [spanType, responseType] = spanResponsePair;
3235 auto jsonItem = JsonUtil::Create(true);
3236 jsonItem->Put("spanType", TextSpanTypeMapper::GetJsSpanType(spanType, params->isValid));
3237 jsonItem->Put("responseType", static_cast<int32_t>(responseType));
3238 jsonItem->Put("menuType", static_cast<int32_t>(SelectionMenuType::SELECTION_MENU));
3239 jsonArray->Put(jsonItem);
3240 }
3241 FillPreviewMenuInJson(jsonArray);
3242 return StringUtils::RestoreBackslash(jsonArray->ToString());
3243 }
3244
GetFontInJson() const3245 std::string TextPattern::GetFontInJson() const
3246 {
3247 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3248 CHECK_NULL_RETURN(textLayoutProp, "");
3249 auto jsonValue = JsonUtil::Create(true);
3250 jsonValue->Put("style", GetFontStyleInJson(textLayoutProp->GetItalicFontStyle()).c_str());
3251 jsonValue->Put("size", GetFontSizeWithThemeInJson(textLayoutProp->GetFontSize()).c_str());
3252 jsonValue->Put("weight", GetFontWeightInJson(textLayoutProp->GetFontWeight()).c_str());
3253 jsonValue->Put("variableFontWeight", std::to_string(textLayoutProp->GetVariableFontWeight().value_or(0)).c_str());
3254 jsonValue->Put("enableVariableFontWeight",
3255 textLayoutProp->GetEnableVariableFontWeight().value_or(false) ? "true" : "false");
3256 jsonValue->Put("family", GetFontFamilyInJson(textLayoutProp->GetFontFamily()).c_str());
3257 return jsonValue->ToString();
3258 }
3259
GetFontSizeWithThemeInJson(const std::optional<Dimension> & value) const3260 std::string TextPattern::GetFontSizeWithThemeInJson(const std::optional<Dimension>& value) const
3261 {
3262 auto host = GetHost();
3263 CHECK_NULL_RETURN(host, "");
3264 auto pipeline = host->GetContext();
3265 CHECK_NULL_RETURN(pipeline, "");
3266 auto theme = pipeline->GetTheme<TextTheme>();
3267 CHECK_NULL_RETURN(theme, "");
3268 return value.value_or(theme->GetTextStyle().GetFontSize()).ToString();
3269 }
3270
ToTreeJson(std::unique_ptr<JsonValue> & json,const InspectorConfig & config) const3271 void TextPattern::ToTreeJson(std::unique_ptr<JsonValue>& json, const InspectorConfig& config) const
3272 {
3273 Pattern::ToTreeJson(json, config);
3274 if (!textForDisplay_.empty()) {
3275 json->Put(TreeKey::CONTENT, UtfUtils::Str16DebugToStr8(textForDisplay_).c_str());
3276 }
3277 }
3278
OnAfterModifyDone()3279 void TextPattern::OnAfterModifyDone()
3280 {
3281 auto host = GetHost();
3282 CHECK_NULL_VOID(host);
3283 auto inspectorId = host->GetInspectorId().value_or("");
3284 if (!inspectorId.empty()) {
3285 auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
3286 Recorder::NodeDataCache::Get().PutString(host, inspectorId, prop->GetText());
3287 }
3288 }
3289
ActSetSelection(int32_t start,int32_t end)3290 void TextPattern::ActSetSelection(int32_t start, int32_t end)
3291 {
3292 int32_t min = 0;
3293 int32_t textSize = static_cast<int32_t>(textForDisplay_.length()) + placeholderCount_;
3294 start = start < min ? min : start;
3295 end = end < min ? min : end;
3296 start = start > textSize ? textSize : start;
3297 end = end > textSize ? textSize : end;
3298 if (start >= end) {
3299 ResetSelection();
3300 CloseSelectOverlay();
3301 return;
3302 }
3303 HandleSelectionChange(start, end);
3304 parentGlobalOffset_ = GetParentGlobalOffset();
3305 CalculateHandleOffsetAndShowOverlay();
3306 showSelected_ = true;
3307 if (textSelector_.firstHandle == textSelector_.secondHandle && pManager_) {
3308 ResetSelection();
3309 CloseSelectOverlay();
3310 return;
3311 }
3312 showSelected_ = false;
3313 if (IsShowHandle()) {
3314 ShowSelectOverlay();
3315 } else {
3316 CloseSelectOverlay();
3317 if (IsSelected()) {
3318 selectOverlay_->SetSelectionHoldCallback();
3319 }
3320 }
3321 auto host = GetHost();
3322 CHECK_NULL_VOID(host);
3323 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
3324 }
3325
IsShowHandle()3326 bool TextPattern::IsShowHandle()
3327 {
3328 auto host = GetHost();
3329 CHECK_NULL_RETURN(host, false);
3330 auto pipeline = host->GetContext();
3331 CHECK_NULL_RETURN(pipeline, false);
3332 auto theme = pipeline->GetTheme<TextTheme>(GetThemeScopeId());
3333 CHECK_NULL_RETURN(theme, false);
3334 return !theme->IsShowHandle();
3335 }
3336
GetUrlHoverColor()3337 Color TextPattern::GetUrlHoverColor()
3338 {
3339 auto host = GetHost();
3340 CHECK_NULL_RETURN(host, Color());
3341 auto pipeline = host->GetContext();
3342 CHECK_NULL_RETURN(pipeline, Color());
3343 auto theme = pipeline->GetTheme<TextTheme>();
3344 CHECK_NULL_RETURN(theme, Color());
3345 return theme->GetUrlHoverColor();
3346 }
3347
GetUrlPressColor()3348 Color TextPattern::GetUrlPressColor()
3349 {
3350 auto host = GetHost();
3351 CHECK_NULL_RETURN(host, Color());
3352 auto pipeline = host->GetContext();
3353 CHECK_NULL_RETURN(pipeline, Color());
3354 auto theme = pipeline->GetTheme<TextTheme>();
3355 CHECK_NULL_RETURN(theme, Color());
3356 return theme->GetUrlPressColor();
3357 }
3358
GetUrlSpanColor()3359 Color TextPattern::GetUrlSpanColor()
3360 {
3361 auto host = GetHost();
3362 CHECK_NULL_RETURN(host, Color());
3363 auto pipeline = host->GetContext();
3364 CHECK_NULL_RETURN(pipeline, Color());
3365 auto theme = pipeline->GetTheme<TextTheme>();
3366 CHECK_NULL_RETURN(theme, Color());
3367
3368 auto eventHub = host->GetEventHub<EventHub>();
3369 CHECK_NULL_RETURN(eventHub, Color());
3370
3371 if (eventHub && !eventHub->IsEnabled()) {
3372 return theme->GetUrlDisabledColor();
3373 } else {
3374 return theme->GetUrlDefaultColor();
3375 }
3376 }
3377
3378 // Deprecated: Use the TextSelectOverlay::ProcessOverlay() instead.
3379 // It is currently used by RichEditorPattern.
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)3380 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
3381 {
3382 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
3383 SelectHandleInfo firstHandleInfo;
3384 firstHandleInfo.paintRect = textSelector_.firstHandle;
3385 CheckHandles(firstHandleInfo);
3386
3387 SelectHandleInfo secondHandleInfo;
3388 secondHandleInfo.paintRect = textSelector_.secondHandle;
3389 CheckHandles(secondHandleInfo);
3390
3391 auto start = textSelector_.GetTextStart();
3392 auto end = textSelector_.GetTextEnd();
3393 selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
3394 if (selectInfo.isNewAvoid) {
3395 selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
3396 }
3397 selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
3398 selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
3399 } else {
3400 auto host = GetHost();
3401 CHECK_NULL_VOID(host);
3402 auto pipeline = host->GetContext();
3403 CHECK_NULL_VOID(pipeline);
3404 pipeline->AddOnAreaChangeNode(host->GetId());
3405 selectInfo.callerFrameNode = GetHost();
3406 selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
3407 if (!selectInfo.isUsingMouse) {
3408 CheckHandles(selectInfo.firstHandle);
3409 CheckHandles(selectInfo.secondHandle);
3410 }
3411 selectOverlayProxy_ =
3412 pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
3413 CHECK_NULL_VOID(selectOverlayProxy_);
3414 auto start = textSelector_.GetTextStart();
3415 auto end = textSelector_.GetTextEnd();
3416 selectOverlayProxy_->SetSelectInfo(UtfUtils::Str16DebugToStr8(GetSelectedText(start, end)));
3417 }
3418 }
3419
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)3420 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
3421 {
3422 if (config.skipMeasure || dirty->SkipMeasureContent()) {
3423 return false;
3424 }
3425
3426 contentRect_ = dirty->GetGeometryNode()->GetContentRect();
3427 dataDetectorAdapter_->aiSpanRects_.clear();
3428
3429 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
3430 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
3431 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
3432 CHECK_NULL_RETURN(textLayoutAlgorithm, false);
3433 baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
3434 contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
3435 textStyle_ = textLayoutAlgorithm->GetTextStyle();
3436 ProcessOverlayAfterLayout();
3437 return true;
3438 }
3439
ProcessOverlayAfterLayout()3440 void TextPattern::ProcessOverlayAfterLayout()
3441 {
3442 if (selectOverlay_->SelectOverlayIsOn() || showSelected_) {
3443 showSelected_ = false;
3444 CalculateHandleOffsetAndShowOverlay();
3445 selectOverlay_->UpdateAllHandlesOffset();
3446 selectOverlay_->UpdateViewPort();
3447 }
3448 }
3449
PreCreateLayoutWrapper()3450 void TextPattern::PreCreateLayoutWrapper()
3451 {
3452 auto host = GetHost();
3453 CHECK_NULL_VOID(host);
3454
3455 auto paintProperty = GetPaintProperty<PaintProperty>();
3456 CHECK_NULL_VOID(paintProperty);
3457 auto flag = paintProperty->GetPropertyChangeFlag();
3458 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3459 CHECK_NULL_VOID(textLayoutProperty);
3460 auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
3461 if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
3462 return;
3463 }
3464 auto beforeSpanSize = spans_.size();
3465 spans_.clear();
3466 childNodes_.clear();
3467
3468 // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
3469 const auto& children = host->GetChildren();
3470 if (children.empty()) {
3471 placeholderCount_ = 0;
3472 return;
3473 }
3474
3475 // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
3476 // tree.
3477 std::stack<SpanNodeInfo> nodes;
3478 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
3479 nodes.push({ .node = *iter });
3480 }
3481
3482 InitSpanItem(nodes);
3483 CHECK_NULL_VOID(beforeSpanSize != spans_.size());
3484 textLayoutProperty->OnPropertyChangeMeasure();
3485 }
3486
InitSpanItem(std::stack<SpanNodeInfo> nodes)3487 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
3488 {
3489 auto host = GetHost();
3490 CHECK_NULL_VOID(host);
3491 std::u16string textCache;
3492 std::u16string textForAICache;
3493 int32_t oldPlaceholderCount = placeholderCount_;
3494 placeholderCount_ = 0;
3495 if (!nodes.empty()) {
3496 textCache = textForDisplay_;
3497 textForAICache = dataDetectorAdapter_->textForAI_;
3498 textForDisplay_.clear();
3499 dataDetectorAdapter_->textForAI_.clear();
3500 }
3501
3502 bool isSpanHasClick = false;
3503 CollectSpanNodes(nodes, isSpanHasClick);
3504 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3505 CHECK_NULL_VOID(textLayoutProperty);
3506 if (childNodes_.empty()) {
3507 textForDisplay_ = textLayoutProperty->GetContent().value_or(u"");
3508 }
3509 if (oldPlaceholderCount != placeholderCount_) {
3510 CloseSelectOverlay();
3511 ResetSelection();
3512 }
3513
3514 if (textCache != textForDisplay_) {
3515 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, UtfUtils::Str16DebugToStr8(textCache),
3516 UtfUtils::Str16DebugToStr8(textForDisplay_));
3517 OnAfterModifyDone();
3518 for (const auto& item : spans_) {
3519 if (item->inspectId.empty()) {
3520 continue;
3521 }
3522 Recorder::NodeDataCache::Get().PutString(host, item->inspectId, UtfUtils::Str16DebugToStr8(item->content));
3523 }
3524 ResetAfterTextChange();
3525 }
3526 if (isSpanHasClick) {
3527 auto gestureEventHub = host->GetOrCreateGestureEventHub();
3528 InitClickEvent(gestureEventHub);
3529 }
3530 if (textForAICache != dataDetectorAdapter_->textForAI_) {
3531 dataDetectorAdapter_->aiDetectInitialized_ = false;
3532 }
3533 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
3534 ParseOriText(textLayoutProperty->GetContent().value_or(u""));
3535 if (!dataDetectorAdapter_->aiDetectInitialized_) {
3536 dataDetectorAdapter_->StartAITask();
3537 }
3538 }
3539 }
3540
ResetAfterTextChange()3541 void TextPattern::ResetAfterTextChange()
3542 {
3543 CloseSelectOverlay();
3544 ResetSelection();
3545 ResetOriginCaretPosition();
3546 }
3547
ParseOriText(const std::u16string & currentText)3548 void TextPattern::ParseOriText(const std::u16string& currentText)
3549 {
3550 auto entityJson = JsonUtil::ParseJsonString(UtfUtils::Str16DebugToStr8(currentText));
3551 bool entityIsJson = !entityJson->IsNull();
3552 TAG_LOGI(AceLogTag::ACE_TEXT, "text content is the json format: %{public}d", entityIsJson);
3553 if (entityIsJson && !entityJson->GetValue("bundleName")->IsNull() &&
3554 dataDetectorAdapter_->ParseOriText(entityJson, textForDisplay_)) {
3555 if (childNodes_.empty()) {
3556 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3557 CHECK_NULL_VOID(textLayoutProperty);
3558 textLayoutProperty->UpdateContent(textForDisplay_);
3559 }
3560 }
3561 }
3562
BeforeCreateLayoutWrapper()3563 void TextPattern::BeforeCreateLayoutWrapper()
3564 {
3565 if (!isSpanStringMode_) {
3566 PreCreateLayoutWrapper();
3567 }
3568 selectOverlay_->MarkOverlayDirty();
3569 }
3570
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick)3571 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick)
3572 {
3573 while (!nodes.empty()) {
3574 auto current = nodes.top();
3575 nodes.pop();
3576 if (!current.node) {
3577 continue;
3578 }
3579 UpdateContainerChildren(current.containerSpanNode, current.node);
3580 auto spanNode = DynamicCast<SpanNode>(current.node);
3581 auto tag = current.node->GetTag();
3582 if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG && spanNode->GetSpanItem()->GetSymbolUnicode() != 0) {
3583 spanNode->CleanSpanItemChildren();
3584 spanNode->MountToParagraph();
3585 textForDisplay_.append(u" ");
3586 dataDetectorAdapter_->textForAI_.append(SYMBOL_TRANS);
3587 childNodes_.push_back(current.node);
3588 } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
3589 CollectTextSpanNodes(spanNode, isSpanHasClick);
3590 childNodes_.push_back(current.node);
3591 } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
3592 placeholderCount_++;
3593 AddChildSpanItem(current.node);
3594 dataDetectorAdapter_->textForAI_.append(u"\n");
3595 auto imageNode = DynamicCast<FrameNode>(current.node);
3596 if (!imageNode) {
3597 continue;
3598 }
3599 auto focus_hub = imageNode->GetOrCreateFocusHub();
3600 if (focus_hub && focus_hub->GetOnClickCallback()) {
3601 isSpanHasClick = true;
3602 }
3603 childNodes_.push_back(current.node);
3604 } else if (tag == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
3605 placeholderCount_++;
3606 AddChildSpanItem(current.node);
3607 dataDetectorAdapter_->textForAI_.append(u"\n");
3608 childNodes_.emplace_back(current.node);
3609 }
3610 if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
3611 continue;
3612 }
3613 const auto& nextChildren = current.node->GetChildren();
3614 if (nextChildren.empty()) {
3615 continue;
3616 }
3617 auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
3618 for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
3619 nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
3620 }
3621 }
3622 }
3623
CollectTextSpanNodes(const RefPtr<SpanNode> & spanNode,bool & isSpanHasClick)3624 void TextPattern::CollectTextSpanNodes(const RefPtr<SpanNode>& spanNode, bool& isSpanHasClick)
3625 {
3626 spanNode->CleanSpanItemChildren();
3627 spanNode->MountToParagraph();
3628 textForDisplay_.append(spanNode->GetSpanItem()->content);
3629 dataDetectorAdapter_->textForAI_.append(spanNode->GetSpanItem()->content);
3630 if (spanNode->GetSpanItem()->onClick) {
3631 isSpanHasClick = true;
3632 }
3633 }
3634
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)3635 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
3636 {
3637 CHECK_NULL_VOID(child);
3638 auto parent = DynamicCast<ContainerSpanNode>(parentNode);
3639 CHECK_NULL_VOID(parent);
3640 auto baseSpan = DynamicCast<BaseSpan>(child);
3641 if (baseSpan) {
3642 if (baseSpan->HasTextBackgroundStyle()) {
3643 return;
3644 }
3645 baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
3646 return;
3647 }
3648 if (child->GetTag() == V2::IMAGE_ETS_TAG) {
3649 auto imageNode = DynamicCast<FrameNode>(child);
3650 CHECK_NULL_VOID(imageNode);
3651 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
3652 CHECK_NULL_VOID(imageLayoutProperty);
3653 if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
3654 return;
3655 }
3656 if (parent->GetTextBackgroundStyle().has_value()) {
3657 imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
3658 }
3659 }
3660 }
3661
GetGlobalOffset(Offset & offset)3662 void TextPattern::GetGlobalOffset(Offset& offset)
3663 {
3664 auto host = GetHost();
3665 CHECK_NULL_VOID(host);
3666 auto pipeline = host->GetContext();
3667 CHECK_NULL_VOID(pipeline);
3668 auto rootOffset = pipeline->GetRootRect().GetOffset();
3669 auto globalOffset = host->GetPaintRectOffset(false, true) - rootOffset;
3670 offset = Offset(globalOffset.GetX(), globalOffset.GetY());
3671 }
3672
OnVisibleChange(bool isVisible)3673 void TextPattern::OnVisibleChange(bool isVisible)
3674 {
3675 if (!isVisible) {
3676 if (textSelector_.IsValid()) {
3677 CloseSelectOverlay();
3678 ResetSelection();
3679 }
3680 if (textDetectEnable_) {
3681 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
3682 }
3683 PauseSymbolAnimation();
3684 } else {
3685 if (CanStartAITask()) {
3686 dataDetectorAdapter_->StartAITask();
3687 }
3688 ResumeSymbolAnimation();
3689 }
3690 }
3691
ProcessVisibleAreaCallback()3692 void TextPattern::ProcessVisibleAreaCallback()
3693 {
3694 auto host = GetHost();
3695 CHECK_NULL_VOID(host);
3696 auto pipeline = GetContext();
3697 CHECK_NULL_VOID(pipeline);
3698 auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
3699 auto pattern = weak.Upgrade();
3700 CHECK_NULL_VOID(pattern);
3701 pattern->OnVisibleChange(visible);
3702 };
3703 std::vector<double> ratioList = { 0.0 };
3704 pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
3705 }
3706
PauseSymbolAnimation()3707 void TextPattern::PauseSymbolAnimation()
3708 {
3709 auto host = GetHost();
3710 CHECK_NULL_VOID(host);
3711 if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
3712 return;
3713 }
3714 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3715 CHECK_NULL_VOID(layoutProperty);
3716 if (!layoutProperty->GetIsLoopAnimation()) {
3717 return;
3718 }
3719 auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
3720 if (!symbolEffectOptions.GetIsTxtActive()) {
3721 return;
3722 }
3723 symbolEffectOptions.SetIsTxtActive(false);
3724 layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
3725 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3726 }
3727
ResumeSymbolAnimation()3728 void TextPattern::ResumeSymbolAnimation()
3729 {
3730 auto host = GetHost();
3731 CHECK_NULL_VOID(host);
3732 if (host->GetTag() != V2::SYMBOL_ETS_TAG) {
3733 return;
3734 }
3735 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
3736 CHECK_NULL_VOID(layoutProperty);
3737 if (!layoutProperty->GetIsLoopAnimation()) {
3738 return;
3739 }
3740 auto symbolEffectOptions = layoutProperty->GetSymbolEffectOptionsValue(SymbolEffectOptions());
3741 if (symbolEffectOptions.GetIsTxtActive()) {
3742 return;
3743 }
3744 symbolEffectOptions.SetIsTxtActive(true);
3745 layoutProperty->UpdateSymbolEffectOptions(symbolEffectOptions);
3746 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
3747 }
3748
InitSurfaceChangedCallback()3749 void TextPattern::InitSurfaceChangedCallback()
3750 {
3751 auto host = GetHost();
3752 CHECK_NULL_VOID(host);
3753 auto pipeline = host->GetContext();
3754 CHECK_NULL_VOID(pipeline);
3755 if (!HasSurfaceChangedCallback()) {
3756 auto callbackId = pipeline->RegisterSurfaceChangedCallback(
3757 [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
3758 WindowSizeChangeReason type) {
3759 auto pattern = weak.Upgrade();
3760 if (pattern) {
3761 pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight, type);
3762 }
3763 });
3764 UpdateSurfaceChangedCallbackId(callbackId);
3765 }
3766 }
3767
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight,WindowSizeChangeReason type)3768 void TextPattern::HandleSurfaceChanged(
3769 int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight, WindowSizeChangeReason type)
3770 {
3771 if (newWidth == prevWidth && newHeight == prevHeight) {
3772 return;
3773 }
3774 if (type != WindowSizeChangeReason::DRAG) {
3775 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
3776 CHECK_NULL_VOID(textLayoutProperty);
3777 textLayoutProperty->OnPropertyChangeMeasure();
3778 }
3779 CHECK_NULL_VOID(selectOverlay_->SelectOverlayIsOn());
3780 if (selectOverlay_->IsShowMouseMenu()) {
3781 CloseSelectOverlay();
3782 } else {
3783 auto host = GetHost();
3784 CHECK_NULL_VOID(host);
3785 auto context = host->GetContext();
3786 if (context) {
3787 context->AddAfterLayoutTask([weak = WeakClaim(this)]() {
3788 auto pattern = weak.Upgrade();
3789 CHECK_NULL_VOID(pattern);
3790 pattern->CalculateHandleOffsetAndShowOverlay();
3791 pattern->ShowSelectOverlay({ .menuIsShow = false });
3792 });
3793 }
3794 }
3795 }
3796
InitSurfacePositionChangedCallback()3797 void TextPattern::InitSurfacePositionChangedCallback()
3798 {
3799 auto host = GetHost();
3800 CHECK_NULL_VOID(host);
3801 auto pipeline = host->GetContext();
3802 CHECK_NULL_VOID(pipeline);
3803 if (!HasSurfacePositionChangedCallback()) {
3804 auto callbackId =
3805 pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
3806 auto pattern = weak.Upgrade();
3807 if (pattern) {
3808 pattern->HandleSurfacePositionChanged(posX, posY);
3809 }
3810 });
3811 UpdateSurfacePositionChangedCallbackId(callbackId);
3812 }
3813 }
3814
AddChildSpanItem(const RefPtr<UINode> & child)3815 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
3816 {
3817 CHECK_NULL_VOID(child);
3818 auto chidNode = DynamicCast<FrameNode>(child);
3819 if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
3820 return;
3821 }
3822
3823 if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
3824 auto spanNode = DynamicCast<SpanNode>(child);
3825 if (spanNode) {
3826 spans_.emplace_back(spanNode->GetSpanItem());
3827 }
3828 } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
3829 AddImageToSpanItem(child);
3830 } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
3831 auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
3832 if (placeholderSpanNode) {
3833 auto placeholderSpan = placeholderSpanNode->GetSpanItem();
3834 placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
3835 spans_.emplace_back(placeholderSpan);
3836 }
3837 } else if (child->GetTag() == V2::CUSTOM_SPAN_NODE_ETS_TAG) {
3838 auto customSpanNode = DynamicCast<CustomSpanNode>(child);
3839 if (customSpanNode) {
3840 auto customSpan = customSpanNode->GetSpanItem();
3841 customSpan->placeholderSpanNodeId = customSpanNode->GetId();
3842 spans_.emplace_back(customSpan);
3843 }
3844 }
3845 }
3846
AddImageToSpanItem(const RefPtr<UINode> & child)3847 void TextPattern::AddImageToSpanItem(const RefPtr<UINode>& child)
3848 {
3849 auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
3850 if (imageSpanNode) {
3851 auto host = GetHost();
3852 CHECK_NULL_VOID(host);
3853 auto imageSpanItem = imageSpanNode->GetSpanItem();
3854 if (host->GetTag() != V2::RICH_EDITOR_ETS_TAG) {
3855 auto focus_hub = imageSpanNode->GetOrCreateFocusHub();
3856 CHECK_NULL_VOID(focus_hub);
3857 auto clickCall = focus_hub->GetOnClickCallback();
3858 if (clickCall) {
3859 imageSpanItem->SetOnClickEvent(std::move(clickCall));
3860 }
3861 auto gesture = imageSpanNode->GetOrCreateGestureEventHub();
3862 CHECK_NULL_VOID(gesture);
3863 gesture->SetHitTestMode(HitTestMode::HTMNONE);
3864 }
3865 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageSpanNode);
3866 spans_.emplace_back(imageSpanItem);
3867 spans_.back()->imageNodeId = imageSpanNode->GetId();
3868 return;
3869 }
3870 auto imageNode = DynamicCast<FrameNode>(child);
3871 if (imageNode) {
3872 auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
3873 imageSpanItem->imageNodeId = imageNode->GetId();
3874 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
3875 auto focus_hub = imageNode->GetOrCreateFocusHub();
3876 CHECK_NULL_VOID(focus_hub);
3877 auto clickCall = focus_hub->GetOnClickCallback();
3878 if (clickCall) {
3879 imageSpanItem->SetOnClickEvent(std::move(clickCall));
3880 }
3881 spans_.emplace_back(imageSpanItem);
3882 auto gesture = imageNode->GetOrCreateGestureEventHub();
3883 CHECK_NULL_VOID(gesture);
3884 gesture->SetHitTestMode(HitTestMode::HTMNONE);
3885 return;
3886 }
3887 }
3888
DumpSimplifyInfo(std::unique_ptr<JsonValue> & json)3889 void TextPattern::DumpSimplifyInfo(std::unique_ptr<JsonValue>& json)
3890 {
3891 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3892 CHECK_NULL_VOID(textLayoutProp);
3893 if (!IsSetObscured()) {
3894 json->Put("content", UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" ")).c_str());
3895 }
3896 }
3897
DumpAdvanceInfo()3898 void TextPattern::DumpAdvanceInfo()
3899 {
3900 DumpLog::GetInstance().AddDesc(std::string("-----DumpAdvanceInfo-----"));
3901 DumpLog::GetInstance().AddDesc(
3902 std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
3903 DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
3904 }
3905
DumpInfo()3906 void TextPattern::DumpInfo()
3907 {
3908 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3909 CHECK_NULL_VOID(textLayoutProp);
3910 auto& dumpLog = DumpLog::GetInstance();
3911 auto nowTime = GetSystemTimestamp();
3912 dumpLog.AddDesc(std::string("frameRecord: ").append(frameRecord_));
3913 dumpLog.AddDesc(std::string("time: ").append(std::to_string(nowTime)));
3914 if (!IsSetObscured() && !IsSensitiveEnalbe()) {
3915 dumpLog.AddDesc(std::string("Content: ").append(
3916 UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" "))));
3917 }
3918 dumpLog.AddDesc(std::string("isSpanStringMode: ").append(std::to_string(isSpanStringMode_)));
3919 dumpLog.AddDesc(std::string("externalParagraph: ").append(std::to_string(externalParagraph_.has_value())));
3920 DumpTextStyleInfo();
3921 if (contentMod_) {
3922 contentMod_->ContentModifierDump();
3923 }
3924 dumpLog.AddDesc(
3925 std::string("HeightAdaptivePolicy: ")
3926 .append(V2::ConvertWrapTextHeightAdaptivePolicyToString(
3927 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))));
3928 if (pManager_) {
3929 auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
3930 dumpLog.AddDesc(std::string("Paragraphs num: ").append(std::to_string(num)));
3931 dumpLog.AddDesc(std::string("PaintInfo: ").append(paintInfo_));
3932 }
3933 DumpScaleInfo();
3934 DumpTextEngineInfo();
3935 if (SystemProperties::GetDebugEnabled()) {
3936 DumpAdvanceInfo();
3937 }
3938 DumpSpanItem();
3939 }
3940
DumpSpanItem()3941 void TextPattern::DumpSpanItem()
3942 {
3943 CHECK_NULL_VOID(isSpanStringMode_);
3944 auto& dumpLog = DumpLog::GetInstance();
3945 dumpLog.AddDesc(std::string("-----SpanDumpInfo-----"));
3946 for (const auto& item : spans_) {
3947 if (!item) {
3948 continue;
3949 }
3950 item->SpanDumpInfo();
3951 }
3952 }
3953
DumpTextStyleInfo()3954 void TextPattern::DumpTextStyleInfo()
3955 {
3956 auto& dumpLog = DumpLog::GetInstance();
3957 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
3958 CHECK_NULL_VOID(textLayoutProp);
3959 auto host = GetHost();
3960 CHECK_NULL_VOID(host);
3961 auto renderContext = host->GetRenderContext();
3962 CHECK_NULL_VOID(renderContext);
3963 dumpLog.AddDesc(
3964 std::string("FontColor: ")
3965 .append((textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString())
3966 .append(" pro: ")
3967 .append(
3968 textLayoutProp->HasTextColor() ? textLayoutProp->GetTextColorValue(Color::BLACK).ColorToString() : "Na")
3969 .append(" ForegroundColor: ")
3970 .append(
3971 renderContext->HasForegroundColor() ? renderContext->GetForegroundColorValue().ColorToString() : "Na"));
3972 if (renderContext->HasForegroundColorStrategy()) {
3973 auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
3974 DumpLog::GetInstance().AddDesc(std::string("ForegroundColorStrategy: ").append(std::to_string(strategy)));
3975 }
3976 dumpLog.AddDesc(
3977 std::string("FontSize: ")
3978 .append((textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
3979 .ToString())
3980 .append(" pro: ")
3981 .append(textLayoutProp->HasFontSize()
3982 ? textLayoutProp->GetFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
3983 : "Na"));
3984 if (textStyle_.has_value()) {
3985 dumpLog.AddDesc(
3986 std::string("MaxFontSize: ")
3987 .append(textStyle_->GetAdaptMaxFontSize().ToString())
3988 .append(" pro: ")
3989 .append(textLayoutProp->HasAdaptMaxFontSize()
3990 ? textLayoutProp->GetAdaptMaxFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
3991 : "Na"));
3992 dumpLog.AddDesc(
3993 std::string("MinFontSize: ")
3994 .append(textStyle_->GetAdaptMinFontSize().ToString())
3995 .append(" pro: ")
3996 .append(textLayoutProp->HasAdaptMinFontSize()
3997 ? textLayoutProp->GetAdaptMinFontSizeValue(Dimension(0.0, DimensionUnit::FP)).ToString()
3998 : "Na"));
3999 }
4000 DumpTextStyleInfo2();
4001 DumpTextStyleInfo3();
4002 }
4003
DumpTextStyleInfo2()4004 void TextPattern::DumpTextStyleInfo2()
4005 {
4006 auto& dumpLog = DumpLog::GetInstance();
4007 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4008 CHECK_NULL_VOID(textLayoutProp);
4009 if (textStyle_.has_value()) {
4010 dumpLog.AddDesc(std::string("FontWeight: ")
4011 .append(StringUtils::ToString(textStyle_->GetFontWeight()))
4012 .append(" pro: ")
4013 .append(textLayoutProp->HasFontWeight()
4014 ? StringUtils::ToString(textLayoutProp->GetFontWeightValue(FontWeight::NORMAL))
4015 : "Na"));
4016 dumpLog.AddDesc(std::string("FontStyle: ").append(StringUtils::ToString(textStyle_->GetFontStyle())));
4017 dumpLog.AddDesc(
4018 std::string("LineHeight: ")
4019 .append(textStyle_->GetLineHeight().ToString())
4020 .append(" pro: ")
4021 .append(textLayoutProp->HasLineHeight()
4022 ? textLayoutProp->GetLineHeightValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4023 : "Na"));
4024 dumpLog.AddDesc(
4025 std::string("LineSpacing: ")
4026 .append(textStyle_->GetLineSpacing().ToString())
4027 .append(" pro: ").append(textLayoutProp->HasLineSpacing()
4028 ? textLayoutProp->GetLineSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4029 : "Na"));
4030 dumpLog.AddDesc(
4031 std::string("maxLines: ")
4032 .append(std::to_string(textStyle_->GetMaxLines()))
4033 .append(" pro: ")
4034 .append(textLayoutProp->HasMaxLines() ? std::to_string(textLayoutProp->GetMaxLinesValue(UINT32_MAX))
4035 : "Na"));
4036 dumpLog.AddDesc(
4037 std::string("BaselineOffset: ")
4038 .append(textStyle_->GetBaselineOffset().ToString())
4039 .append(" pro: ")
4040 .append(textLayoutProp->HasBaselineOffset()
4041 ? textLayoutProp->GetBaselineOffsetValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4042 : "Na"));
4043 dumpLog.AddDesc(
4044 std::string("TextIndent: ")
4045 .append(textStyle_->GetTextIndent().ToString())
4046 .append(" pro: ")
4047 .append(textLayoutProp->HasTextIndent()
4048 ? textLayoutProp->GetTextIndentValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4049 : "Na"));
4050 }
4051 }
4052
DumpTextStyleInfo3()4053 void TextPattern::DumpTextStyleInfo3()
4054 {
4055 auto& dumpLog = DumpLog::GetInstance();
4056 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4057 CHECK_NULL_VOID(textLayoutProp);
4058 if (textStyle_.has_value()) {
4059 dumpLog.AddDesc(
4060 std::string("fontFamily: ")
4061 .append(GetFontFamilyInJson(textStyle_->GetFontFamilies()))
4062 .append(" pro: ")
4063 .append(textLayoutProp->HasFontFamily() ? GetFontFamilyInJson(textLayoutProp->GetFontFamily().value())
4064 : "Na"));
4065 dumpLog.AddDesc(
4066 std::string("LetterSpacing: ")
4067 .append(textStyle_->GetLetterSpacing().ToString())
4068 .append(" pro: ")
4069 .append(textLayoutProp->HasLetterSpacing()
4070 ? textLayoutProp->GetLetterSpacingValue(Dimension(0.0, DimensionUnit::FP)).ToString()
4071 : "Na"));
4072 dumpLog.AddDesc(std::string("TextOverflow: ").append(StringUtils::ToString(textStyle_->GetTextOverflow())));
4073 dumpLog.AddDesc(std::string("TextAlign: ")
4074 .append(StringUtils::ToString(textStyle_->GetTextAlign()))
4075 .append(" pro: ")
4076 .append(textLayoutProp->HasTextAlign()
4077 ? StringUtils::ToString(textLayoutProp->GetTextAlignValue(TextAlign::START))
4078 : "Na"));
4079 dumpLog.AddDesc(std::string("WordBreak: ").append(StringUtils::ToString(textStyle_->GetWordBreak())));
4080 dumpLog.AddDesc(std::string("TextCase: ").append(StringUtils::ToString(textStyle_->GetTextCase())));
4081 dumpLog.AddDesc(std::string("EllipsisMode: ").append(StringUtils::ToString(textStyle_->GetEllipsisMode())));
4082 dumpLog.AddDesc(
4083 std::string("LineBreakStrategy: ").append(GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy())));
4084 dumpLog.AddDesc(std::string("SymbolColorList: ")
4085 .append(StringUtils::SymbolColorListToString(textStyle_->GetSymbolColorList())));
4086 }
4087 }
4088
DumpScaleInfo()4089 void TextPattern::DumpScaleInfo()
4090 {
4091 auto& dumpLog = DumpLog::GetInstance();
4092 dumpLog.AddDesc(std::string("-----DumpScaleInfo-----"));
4093 auto host = GetHost();
4094 CHECK_NULL_VOID(host);
4095 auto pipeline = host->GetContext();
4096 CHECK_NULL_VOID(pipeline);
4097 auto fontScale = pipeline->GetFontScale();
4098 auto fontWeightScale = pipeline->GetFontWeightScale();
4099 auto followSystem = pipeline->IsFollowSystem();
4100 float maxFontScale = pipeline->GetMaxAppFontScale();
4101 auto halfLeading = pipeline->GetHalfLeading();
4102 dumpLog.AddDesc(std::string("fontScale: ").append(std::to_string(fontScale))
4103 .append(std::string(", fontWeightScale: ")).append(std::to_string(fontWeightScale))
4104 .append(std::string(", IsFollowSystem: ")).append(std::to_string(followSystem))
4105 .append(std::string(", maxFontScale: ")).append(std::to_string(maxFontScale))
4106 .append(std::string(", ConfigHalfLeading: ")).append(std::to_string(halfLeading)));
4107 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
4108 CHECK_NULL_VOID(textLayoutProp);
4109 auto minFontScale = textLayoutProp->GetMinFontScale().value_or(0.0f);
4110 auto maxfontScale = textLayoutProp->GetMaxFontScale().value_or(static_cast<float>(INT32_MAX));
4111 dumpLog.AddDesc(std::string("minFontScale: ").append(std::to_string(minFontScale))
4112 .append(std::string(", maxFontScale: ")).append(std::to_string(maxfontScale)));
4113 auto flag = textLayoutProp->HasHalfLeading();
4114 dumpLog.AddDesc(
4115 std::string("HalfLeading: ").append(flag ? std::to_string(textLayoutProp->GetHalfLeadingValue(false)) : "NA"));
4116 }
4117
DumpTextEngineInfo()4118 void TextPattern::DumpTextEngineInfo()
4119 {
4120 auto& dumpLog = DumpLog::GetInstance();
4121 dumpLog.AddDesc(std::string("-----TextEngine paragraphs_ info-----"));
4122 dumpLog.AddDesc(std::string("contentRect :").append(contentRect_.ToString()));
4123 if (pManager_) {
4124 dumpLog.AddDesc(std::string("from TextEngine paragraphs_ info :"));
4125 auto paragraphs = pManager_->GetParagraphs();
4126 if (paragraphs.empty()) {
4127 dumpLog.AddDesc(std::string("paragraphs is empty!"));
4128 return;
4129 }
4130 dumpLog.AddDesc(std::string("DidExceedMaxLines:").append(std::to_string(pManager_->DidExceedMaxLines())));
4131 dumpLog.AddDesc(std::string("GetTextWidth:")
4132 .append(std::to_string(pManager_->GetTextWidth()))
4133 .append(" GetHeight:")
4134 .append(std::to_string(pManager_->GetHeight()))
4135 .append(" GetMaxWidth:")
4136 .append(std::to_string(pManager_->GetMaxWidth()))
4137 .append(" GetMaxIntrinsicWidth:")
4138 .append(std::to_string(pManager_->GetMaxIntrinsicWidth())));
4139 dumpLog.AddDesc(std::string("GetLineCount:")
4140 .append(std::to_string(pManager_->GetLineCount()))
4141 .append(" GetLongestLine:")
4142 .append(std::to_string(pManager_->GetLongestLine()))
4143 .append(std::to_string(pManager_->GetLongestLineWithIndent())));
4144 }
4145 dumpLog.AddDesc(std::string("spans size :").append(std::to_string(spans_.size())));
4146 if (!IsSetObscured() && !IsSensitiveEnalbe()) {
4147 DumpParagraphsInfo();
4148 }
4149 }
4150
DumpParagraphsInfo()4151 void TextPattern::DumpParagraphsInfo()
4152 {
4153 CHECK_NULL_VOID(pManager_);
4154 auto& dumpLog = DumpLog::GetInstance();
4155 auto paragraphs = pManager_->GetParagraphs();
4156 if (paragraphs.empty()) {
4157 dumpLog.AddDesc(std::string("paragraphs is empty!"));
4158 return;
4159 }
4160 dumpLog.AddDesc(std::string("paragraphs size:").append(std::to_string(paragraphs.size())));
4161 for (auto&& info : paragraphs) {
4162 auto paragraph = info.paragraph;
4163 if (paragraph) {
4164 auto text = StringUtils::Str16ToStr8(paragraph->GetParagraphText());
4165 auto textStyle = paragraph->GetParagraphStyle();
4166 auto direction = V2::ConvertTextDirectionToString(textStyle.direction);
4167 dumpLog.AddDesc(std::string("paragraph: ").append(text).append("; direction:").append(direction));
4168 }
4169 }
4170 }
4171
SetAccessibilityAction()4172 void TextPattern::SetAccessibilityAction()
4173 {
4174 auto host = GetHost();
4175 CHECK_NULL_VOID(host);
4176 auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
4177 CHECK_NULL_VOID(textAccessibilityProperty);
4178 textAccessibilityProperty->SetActionSetSelection(
4179 [weakPtr = WeakClaim(this)](int32_t start, int32_t end, bool isForward) {
4180 const auto& pattern = weakPtr.Upgrade();
4181 CHECK_NULL_VOID(pattern);
4182 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
4183 CHECK_NULL_VOID(textLayoutProperty);
4184 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
4185 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
4186 mode != TextSelectableMode::UNSELECTABLE) {
4187 pattern->ActSetSelection(start, end);
4188 }
4189 });
4190
4191 textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
4192 const auto& pattern = weakPtr.Upgrade();
4193 CHECK_NULL_VOID(pattern);
4194 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
4195 CHECK_NULL_VOID(textLayoutProperty);
4196 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
4197 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
4198 mode != TextSelectableMode::UNSELECTABLE) {
4199 pattern->CloseSelectOverlay(true);
4200 pattern->ResetSelection();
4201 }
4202 });
4203
4204 textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
4205 const auto& pattern = weakPtr.Upgrade();
4206 CHECK_NULL_VOID(pattern);
4207 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
4208 CHECK_NULL_VOID(textLayoutProperty);
4209 auto mode = textLayoutProperty->GetTextSelectableModeValue(TextSelectableMode::SELECTABLE_UNFOCUSABLE);
4210 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None &&
4211 mode != TextSelectableMode::UNSELECTABLE) {
4212 pattern->HandleOnCopy();
4213 pattern->CloseSelectOverlay(true);
4214 pattern->ResetSelection();
4215 }
4216 });
4217 }
4218
OnColorConfigurationUpdate()4219 void TextPattern::OnColorConfigurationUpdate()
4220 {
4221 auto host = GetHost();
4222 CHECK_NULL_VOID(host);
4223 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4224 CHECK_NULL_VOID(textLayoutProperty);
4225 if (!textLayoutProperty->HasTextColor()) {
4226 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4227 }
4228 if (GetOrCreateMagnifier()) {
4229 magnifierController_->SetColorModeChange(true);
4230 }
4231 ACE_TEXT_SCOPED_TRACE("OnColorConfigurationUpdate[Text][self:%d]", host->GetId());
4232 }
4233
OnThemeScopeUpdate(int32_t themeScopeId)4234 bool TextPattern::OnThemeScopeUpdate(int32_t themeScopeId)
4235 {
4236 auto host = GetHost();
4237 CHECK_NULL_RETURN(host, false);
4238 auto contex = host->GetRenderContext();
4239 CHECK_NULL_RETURN(contex, false);
4240 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4241 CHECK_NULL_RETURN(textLayoutProperty, false);
4242
4243 if (!textLayoutProperty->HasTextColor() && !contex->HasForegroundColor()) {
4244 auto pipeline = host->GetContext();
4245 CHECK_NULL_RETURN(pipeline, false);
4246 auto textTheme = pipeline->GetTheme<TextTheme>(themeScopeId);
4247 CHECK_NULL_RETURN(textTheme, false);
4248 UpdateFontColor(textTheme->GetTextStyle().GetTextColor());
4249 }
4250 return false;
4251 }
4252
4253 // return: whether the offset is valid, return false if invalid
GetOriginCaretPosition(OffsetF & offset) const4254 bool TextPattern::GetOriginCaretPosition(OffsetF& offset) const
4255 {
4256 if (!originCaretPosition_.NonNegative()) {
4257 return false;
4258 }
4259 offset = originCaretPosition_;
4260 return true;
4261 }
4262
ResetOriginCaretPosition()4263 void TextPattern::ResetOriginCaretPosition()
4264 {
4265 originCaretPosition_ = DEFAULT_NEGATIVE_CARET_OFFSET;
4266 }
4267
4268 // Record current caret position if originCaretPosition_ is invalid
4269 // return: whether the current offset is recorded and valid
RecordOriginCaretPosition(const OffsetF & offset)4270 bool TextPattern::RecordOriginCaretPosition(const OffsetF& offset)
4271 {
4272 if (originCaretPosition_.NonNegative() || !offset.NonNegative()) {
4273 return false;
4274 }
4275 originCaretPosition_ = offset;
4276 return true;
4277 }
4278
ResetCustomFontColor()4279 void TextPattern::ResetCustomFontColor()
4280 {
4281 auto host = GetHost();
4282 CHECK_NULL_VOID(host);
4283 auto pipeline = host->GetContext();
4284 CHECK_NULL_VOID(pipeline);
4285 auto textTheme = pipeline->GetTheme<TextTheme>(host->GetThemeScopeId());
4286 CHECK_NULL_VOID(textTheme);
4287 auto color = textTheme->GetTextStyle().GetTextColor();
4288 UpdateFontColor(color);
4289 }
4290
GetDragUpperLeftCoordinates()4291 OffsetF TextPattern::GetDragUpperLeftCoordinates()
4292 {
4293 auto dragBoxes = GetTextBoxes();
4294 if (dragBoxes.empty()) {
4295 return { 0.0f, 0.0f };
4296 }
4297 auto startY = dragBoxes.front().Top();
4298 auto startX = dragBoxes.front().Left();
4299
4300 auto endY = dragBoxes.back().Top();
4301 OffsetF offset;
4302 if (NearEqual(startY, endY)) {
4303 offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
4304 } else {
4305 offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
4306 }
4307
4308 return GetParentGlobalOffset() + offset;
4309 }
4310
ProcessBoundRectByTextShadow(RectF & rect)4311 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
4312 {
4313 auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
4314 auto shadows = property->GetTextShadow();
4315 if (!shadows.has_value()) {
4316 return;
4317 }
4318 float leftOffsetX = 0.0f;
4319 float rightOffsetX = 0.0f;
4320 float upOffsetY = 0.0f;
4321 float downOffsetY = 0.0f;
4322 for (const auto& shadow : shadows.value()) {
4323 auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
4324 if (LessNotEqual(shadow.GetOffset().GetX() - shadowBlurRadius, leftOffsetX)) {
4325 leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
4326 }
4327
4328 if (GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
4329 rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
4330 }
4331
4332 if (LessNotEqual(shadow.GetOffset().GetY() - shadowBlurRadius, upOffsetY)) {
4333 upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
4334 }
4335
4336 if (GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
4337 downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
4338 }
4339 }
4340 rect.SetRect(
4341 leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
4342 }
4343
ProcessBoundRectByTextMarquee(RectF & rect)4344 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
4345 {
4346 auto host = GetHost();
4347 CHECK_NULL_VOID(host);
4348 auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
4349 CHECK_NULL_VOID(textLayoutProperty);
4350 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE) {
4351 return;
4352 }
4353 auto geometryNode = host->GetGeometryNode();
4354 CHECK_NULL_VOID(geometryNode);
4355 auto contentSize = geometryNode->GetContentSize();
4356 CHECK_NULL_VOID(pManager_);
4357 if (pManager_->GetTextWidth() < contentSize.Width()) {
4358 return;
4359 }
4360 auto frameSize = geometryNode->GetFrameSize();
4361 auto relativeSelfLeftOffsetX =
4362 std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - pManager_->GetTextWidth());
4363 rect.SetLeft(relativeSelfLeftOffsetX);
4364 rect.SetWidth(frameSize.Width() + pManager_->GetTextWidth() - relativeSelfLeftOffsetX);
4365 }
4366
CreateNodePaintMethod()4367 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
4368 {
4369 CreateModifier();
4370 auto paintMethod = MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
4371 auto host = GetHost();
4372 CHECK_NULL_RETURN(host, paintMethod);
4373 auto context = host->GetRenderContext();
4374 CHECK_NULL_RETURN(context, paintMethod);
4375 auto geometryNode = host->GetGeometryNode();
4376 CHECK_NULL_RETURN(geometryNode, paintMethod);
4377 auto frameSize = geometryNode->GetFrameSize();
4378 if (context->GetClipEdge().value_or(host->LessThanAPITargetVersion(PlatformVersion::VERSION_TWELVE))) {
4379 SetResponseRegion(frameSize, frameSize);
4380 return paintMethod;
4381 }
4382 CHECK_NULL_RETURN(pManager_, paintMethod);
4383 RectF boundsRect = overlayMod_->GetBoundsRect();
4384 auto boundsWidth = contentRect_.GetX() + std::ceil(pManager_->GetLongestLineWithIndent());
4385 auto boundsHeight = contentRect_.GetY() + static_cast<float>(pManager_->GetHeight() + std::fabs(baselineOffset_));
4386 boundsRect.SetWidth(boundsWidth);
4387 boundsRect.SetHeight(boundsHeight);
4388 SetResponseRegion(frameSize, boundsRect.GetSize());
4389 ProcessBoundRectByTextShadow(boundsRect);
4390 ProcessBoundRectByTextMarquee(boundsRect);
4391 boundsRect.SetWidth(std::max(frameSize.Width(), boundsRect.Width()));
4392 boundsRect.SetHeight(std::max(frameSize.Height(), boundsRect.Height()));
4393 auto baselineOffset = LessOrEqual(baselineOffset_, 0) ? std::fabs(baselineOffset_) : 0;
4394 pManager_->GetPaintRegion(boundsRect, contentRect_.GetX(), contentRect_.GetY() + baselineOffset);
4395 overlayMod_->SetBoundsRect(boundsRect);
4396 return paintMethod;
4397 }
4398
SetResponseRegion(const SizeF & frameSize,const SizeF & boundsSize)4399 void TextPattern::SetResponseRegion(const SizeF& frameSize, const SizeF& boundsSize)
4400 {
4401 auto host = GetHost();
4402 CHECK_NULL_VOID(host);
4403 auto gestureHub = host->GetOrCreateGestureEventHub();
4404 CHECK_NULL_VOID(gestureHub);
4405 if (isUserSetResponseRegion_) {
4406 return;
4407 }
4408 std::vector<DimensionRect> hotZoneRegions;
4409 DimensionRect hotZoneRegion;
4410 hotZoneRegion.SetSize(DimensionSize(Dimension(std::max(boundsSize.Width(), frameSize.Width())),
4411 Dimension(std::max(frameSize.Height(), boundsSize.Height()))));
4412 hotZoneRegions.emplace_back(hotZoneRegion);
4413 gestureHub->SetResponseRegion(hotZoneRegions);
4414 }
4415
CreateModifier()4416 void TextPattern::CreateModifier()
4417 {
4418 if (!contentMod_) {
4419 contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_, WeakClaim(this));
4420 }
4421 if (!overlayMod_) {
4422 overlayMod_ = MakeRefPtr<TextOverlayModifier>();
4423 }
4424 if (isCustomFont_) {
4425 contentMod_->SetIsCustomFont(true);
4426 }
4427 }
4428
GetHandleIndex(const Offset & offset) const4429 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
4430 {
4431 return pManager_->GetGlyphIndexByCoordinate(offset);
4432 }
4433
OnHandleAreaChanged()4434 void TextPattern::OnHandleAreaChanged()
4435 {
4436 if (selectOverlay_->SelectOverlayIsOn()) {
4437 auto parentGlobalOffset = GetParentGlobalOffset();
4438 if (parentGlobalOffset != parentGlobalOffset_) {
4439 parentGlobalOffset_ = parentGlobalOffset;
4440 CalculateHandleOffsetAndShowOverlay();
4441 ShowSelectOverlay({ .menuIsShow = false, .animation = true });
4442 }
4443 }
4444 }
4445
RemoveAreaChangeInner()4446 void TextPattern::RemoveAreaChangeInner()
4447 {
4448 auto host = GetHost();
4449 CHECK_NULL_VOID(host);
4450 auto pipeline = host->GetContext();
4451 CHECK_NULL_VOID(pipeline);
4452 auto eventHub = host->GetEventHub<TextEventHub>();
4453 CHECK_NULL_VOID(eventHub);
4454 if (eventHub->HasOnAreaChanged()) {
4455 return;
4456 }
4457 pipeline->RemoveOnAreaChangeNode(host->GetId());
4458 }
4459
SetTextDetectEnable(bool enable)4460 void TextPattern::SetTextDetectEnable(bool enable)
4461 {
4462 auto host = GetHost();
4463 CHECK_NULL_VOID(host);
4464 dataDetectorAdapter_->frameNode_ = host;
4465 if (enable == textDetectEnable_) {
4466 return;
4467 }
4468 textDetectEnable_ = enable;
4469 if (textDetectEnable_) {
4470 auto pipeline = host->GetContext();
4471 CHECK_NULL_VOID(pipeline);
4472 auto callback = [weak = WeakClaim(this)]() {
4473 auto pattern = weak.Upgrade();
4474 CHECK_NULL_VOID(pattern);
4475 pattern->dataDetectorAdapter_->GetAIEntityMenu();
4476 };
4477 pipeline->SetConfigChangedCallback(host->GetId(), callback);
4478 } else {
4479 dataDetectorAdapter_->CancelAITask();
4480 }
4481 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
4482 }
4483
CanStartAITask()4484 bool TextPattern::CanStartAITask()
4485 {
4486 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
4487 if (textLayoutProperty) {
4488 return textDetectEnable_ && enabled_ && !IsSetObscured() &&
4489 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) != TextOverflow::MARQUEE;
4490 } else {
4491 return textDetectEnable_ && enabled_;
4492 }
4493 }
4494
NeedShowAIDetect()4495 bool TextPattern::NeedShowAIDetect()
4496 {
4497 return CanStartAITask() && !dataDetectorAdapter_->aiSpanMap_.empty();
4498 }
4499
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,const SelectMenuParam & menuParam)4500 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
4501 std::function<void()>& menuBuilder, const SelectMenuParam& menuParam)
4502 {
4503 auto key = std::make_pair(spanType, responseType);
4504 auto it = selectionMenuMap_.find(key);
4505 if (it != selectionMenuMap_.end()) {
4506 if (menuBuilder == nullptr) {
4507 selectionMenuMap_.erase(it);
4508 return;
4509 }
4510 it->second->buildFunc = menuBuilder;
4511 it->second->onAppear = menuParam.onAppear;
4512 it->second->onDisappear = menuParam.onDisappear;
4513 it->second->onMenuShow = menuParam.onMenuShow;
4514 it->second->onMenuHide = menuParam.onMenuHide;
4515 it->second->isValid = menuParam.isValid;
4516 return;
4517 }
4518
4519 auto selectionMenuParams = std::make_shared<SelectionMenuParams>(
4520 spanType, menuBuilder, menuParam.onAppear, menuParam.onDisappear, responseType);
4521 selectionMenuParams->onMenuShow = menuParam.onMenuShow;
4522 selectionMenuParams->onMenuHide = menuParam.onMenuHide;
4523 selectionMenuParams->isValid = menuParam.isValid;
4524 selectionMenuMap_[key] = selectionMenuParams;
4525 auto host = GetHost();
4526 CHECK_NULL_VOID(host);
4527 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4528 }
4529
CloseSelectionMenu()4530 void TextPattern::CloseSelectionMenu()
4531 {
4532 textResponseType_ = TextResponseType::NONE;
4533 CloseSelectOverlay(true);
4534 }
4535
GetMenuParams(TextSpanType spanType,TextResponseType responseType)4536 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
4537 {
4538 // JS TextSpanType.DEFAULT = TextSpanType::NONE
4539 // JS TextResponseType.DEFAULT = TextResponseType::NONE
4540 std::vector<std::pair<TextSpanType, TextResponseType>> searchPairs = {
4541 { spanType, responseType },
4542 { spanType, TextResponseType::NONE },
4543 };
4544 if (spanType != TextSpanType::NONE) {
4545 searchPairs.push_back({ TextSpanType::NONE, responseType });
4546 searchPairs.push_back({ TextSpanType::NONE, TextResponseType::NONE });
4547 }
4548 for (const auto& key : searchPairs) {
4549 auto it = selectionMenuMap_.find(key);
4550 if (it != selectionMenuMap_.end()) {
4551 return it->second;
4552 }
4553 }
4554 TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
4555 return nullptr;
4556 }
4557
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)4558 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
4559 {
4560 auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
4561 std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
4562 menuParams = GetMenuParams(currentSpanType, responseType);
4563 if (menuParams == nullptr || !menuParams->isValid) {
4564 return;
4565 }
4566 CopyBindSelectionMenuParams(selectInfo, menuParams);
4567 }
4568
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)4569 void TextPattern::CopyBindSelectionMenuParams(
4570 SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
4571 {
4572 selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
4573 auto weak = AceType::WeakClaim(this);
4574 selectInfo.menuCallback.onAppear = [weak, menuParams]() {
4575 auto pattern = weak.Upgrade();
4576 CHECK_NULL_VOID(pattern);
4577 pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_APPEAR, menuParams);
4578 };
4579 selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
4580 selectInfo.menuCallback.onMenuShow = [weak, menuParams]() {
4581 auto pattern = weak.Upgrade();
4582 CHECK_NULL_VOID(pattern);
4583 pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_SHOW, menuParams);
4584 };
4585 selectInfo.menuCallback.onMenuHide = [weak, menuParams]() {
4586 auto pattern = weak.Upgrade();
4587 CHECK_NULL_VOID(pattern);
4588 pattern->OnHandleSelectionMenuCallback(SelectionMenuCalblackId::MENU_HIDE, menuParams);
4589 };
4590 }
4591
OnHandleSelectionMenuCallback(SelectionMenuCalblackId callbackId,std::shared_ptr<SelectionMenuParams> menuParams)4592 void TextPattern::OnHandleSelectionMenuCallback(
4593 SelectionMenuCalblackId callbackId, std::shared_ptr<SelectionMenuParams> menuParams)
4594 {
4595 std::function<void(int32_t, int32_t)> callback;
4596 switch (callbackId) {
4597 case SelectionMenuCalblackId::MENU_SHOW:
4598 callback = menuParams->onMenuShow;
4599 break;
4600 case SelectionMenuCalblackId::MENU_HIDE:
4601 callback = menuParams->onMenuHide;
4602 break;
4603 case SelectionMenuCalblackId::MENU_APPEAR:
4604 callback = menuParams->onAppear;
4605 break;
4606 default:
4607 callback = nullptr;
4608 }
4609 CHECK_NULL_VOID(callback);
4610 auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset);
4611 auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset);
4612 callback(selectStart, selectEnd);
4613 }
4614
FireOnSelectionChange(int32_t start,int32_t end)4615 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
4616 {
4617 auto host = GetHost();
4618 CHECK_NULL_VOID(host);
4619 auto eventHub = host->GetEventHub<TextEventHub>();
4620 CHECK_NULL_VOID(eventHub);
4621 eventHub->FireOnSelectionChange(start, end);
4622 }
4623
FireOnMarqueeStateChange(const TextMarqueeState & state)4624 void TextPattern::FireOnMarqueeStateChange(const TextMarqueeState& state)
4625 {
4626 auto host = GetHost();
4627 CHECK_NULL_VOID(host);
4628 auto eventHub = host->GetEventHub<TextEventHub>();
4629 CHECK_NULL_VOID(eventHub);
4630 eventHub->FireOnMarqueeStateChange(static_cast<int32_t>(state));
4631
4632 if (TextMarqueeState::START == state) {
4633 CloseSelectOverlay();
4634 ResetSelection();
4635 isMarqueeRunning_ = true;
4636 } else if (TextMarqueeState::FINISH == state) {
4637 isMarqueeRunning_ = false;
4638 }
4639
4640 RecoverCopyOption();
4641 }
4642
OnSelectionMenuOptionsUpdate(const NG::OnCreateMenuCallback && onCreateMenuCallback,const NG::OnMenuItemClickCallback && onMenuItemClick)4643 void TextPattern::OnSelectionMenuOptionsUpdate(
4644 const NG::OnCreateMenuCallback&& onCreateMenuCallback, const NG::OnMenuItemClickCallback&& onMenuItemClick)
4645 {
4646 selectOverlay_->OnSelectionMenuOptionsUpdate(std::move(onCreateMenuCallback), std::move(onMenuItemClick));
4647 }
4648
StartVibratorByIndexChange(int32_t currentIndex,int32_t preIndex)4649 void TextPattern::StartVibratorByIndexChange(int32_t currentIndex, int32_t preIndex)
4650 {
4651 CHECK_NULL_VOID(isEnableHapticFeedback_ && (currentIndex != preIndex));
4652 VibratorUtils::StartVibraFeedback("slide");
4653 }
4654
HandleSelectionChange(int32_t start,int32_t end)4655 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
4656 {
4657 if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
4658 return;
4659 }
4660
4661 bool changeSymbolEffect = false;
4662 for (auto& span: spans_) {
4663 if (span->GetSymbolUnicode() == 0) {
4664 continue;
4665 }
4666 bool nextEffectSwitch = start != -1 && end != -1 ? false : true;
4667 if (span->GetSymbolEffectSwitch() != nextEffectSwitch) {
4668 span->SetSymbolEffectSwitch(nextEffectSwitch);
4669 changeSymbolEffect = true;
4670 }
4671 }
4672 textSelector_.Update(start, end);
4673 UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
4674 FireOnSelectionChange(std::min(start, end), std::max(start, end));
4675 if (changeSymbolEffect) {
4676 auto host = GetHost();
4677 CHECK_NULL_VOID(host);
4678 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE_SELF);
4679 }
4680 }
4681
IsSelectedBindSelectionMenu()4682 bool TextPattern::IsSelectedBindSelectionMenu()
4683 {
4684 auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
4685 return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
4686 }
4687
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)4688 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
4689 {
4690 UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
4691 if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
4692 textSelector_.StartEqualToDest()) {
4693 selectedType_ = TextSpanType::TEXT;
4694 }
4695 }
4696
UpdateSelectionType(const SelectionInfo & selection)4697 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
4698 {
4699 selectedType_ = TextSpanType::NONE;
4700 auto list = selection.GetSelection().resultObjects;
4701 bool imageSelected = false;
4702 bool textSelected = false;
4703 bool builderSelected = false;
4704 for (const auto& obj : list) {
4705 if (obj.type == SelectSpanType::TYPEIMAGE) {
4706 imageSelected = true;
4707 } else if (obj.type == SelectSpanType::TYPESPAN) {
4708 textSelected = true;
4709 } else if (obj.type == SelectSpanType::TYPEBUILDERSPAN) {
4710 builderSelected = true;
4711 }
4712 if ((imageSelected && textSelected) || (builderSelected && textSelected) ||
4713 (imageSelected && builderSelected)) {
4714 selectedType_ = TextSpanType::MIXED;
4715 return;
4716 }
4717 }
4718 if (imageSelected) {
4719 selectedType_ = TextSpanType::IMAGE;
4720 } else if (textSelected) {
4721 selectedType_ = TextSpanType::TEXT;
4722 } else if (builderSelected) {
4723 selectedType_ = TextSpanType::BUILDER;
4724 }
4725
4726 TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
4727 }
4728
GetSelectionSpanItemIndex(const MouseInfo & info)4729 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
4730 {
4731 RectF textContentRect = contentRect_;
4732 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
4733 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
4734 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
4735 info.GetLocalLocation().GetY() - textContentRect.GetY() };
4736 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
4737 spans_.empty() || pManager_->GetParagraphs().empty()) {
4738 return -1;
4739 }
4740 int32_t start = 0;
4741 bool isFind = false;
4742 int32_t index = -1;
4743 for (const auto& item : spans_) {
4744 index++;
4745 if (!item) {
4746 continue;
4747 }
4748 auto selectedRects = pManager_->GetRects(start, item->position);
4749 start = item->position;
4750 for (auto&& rect : selectedRects) {
4751 if (rect.IsInRegion(textOffset)) {
4752 isFind = true;
4753 break;
4754 }
4755 }
4756 if (isFind) {
4757 TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
4758 return index;
4759 }
4760 }
4761 return -1;
4762 }
4763
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)4764 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
4765 {
4766 int32_t itemLength = 1;
4767 ResultObject resultObject;
4768 resultObject.isDraggable = true;
4769 if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
4770 return resultObject;
4771 }
4772 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
4773 int32_t startPosition = endPosition - itemLength;
4774 if ((start <= startPosition) && (end >= endPosition)) {
4775 auto builderNode = DynamicCast<FrameNode>(uiNode);
4776 CHECK_NULL_RETURN(builderNode, resultObject);
4777 resultObject.spanPosition.spanIndex = index;
4778 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
4779 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
4780 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
4781 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
4782 resultObject.type = SelectSpanType::TYPEIMAGE;
4783 auto geometryNode = builderNode->GetGeometryNode();
4784 CHECK_NULL_RETURN(geometryNode, resultObject);
4785 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
4786 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
4787 resultObject.valueString = u" ";
4788 }
4789 return resultObject;
4790 }
4791
SetStyledString(const RefPtr<SpanString> & value,bool closeSelectOverlay)4792 void TextPattern::SetStyledString(const RefPtr<SpanString>& value, bool closeSelectOverlay)
4793 {
4794 AllocStyledString();
4795 isSpanStringMode_ = true;
4796 auto host = GetHost();
4797 CHECK_NULL_VOID(host);
4798 if (closeSelectOverlay) {
4799 CloseSelectOverlay();
4800 }
4801 auto length = styledString_->GetLength();
4802 styledString_->RemoveCustomSpan();
4803 styledString_->ReplaceSpanString(0, length, value);
4804 spans_ = styledString_->GetSpanItems();
4805 ProcessSpanString();
4806 styledString_->AddCustomSpan();
4807 styledString_->SetFramNode(WeakClaim(Referenced::RawPtr(host)));
4808 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
4809 }
4810
MountImageNode(const RefPtr<ImageSpanItem> & imageItem)4811 void TextPattern::MountImageNode(const RefPtr<ImageSpanItem>& imageItem)
4812 {
4813 auto host = GetHost();
4814 CHECK_NULL_VOID(host);
4815 auto imageNode = ImageSpanNode::GetOrCreateSpanNode(V2::IMAGE_ETS_TAG,
4816 ElementRegister::GetInstance()->MakeUniqueId(), []() { return AceType::MakeRefPtr<ImagePattern>(); });
4817 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
4818 auto options = imageItem->options;
4819 imageLayoutProperty->UpdateImageSourceInfo(CreateImageSourceInfo(options));
4820 imageNode->MountToParent(host, host->GetChildren().size());
4821 SetImageNodeGesture(imageNode);
4822 if (options.imageAttribute.has_value()) {
4823 auto imgAttr = options.imageAttribute.value();
4824 auto imagePattern = imageNode->GetPattern<ImagePattern>();
4825 CHECK_NULL_VOID(imagePattern);
4826 imagePattern->SetSyncLoad(imgAttr.syncLoad);
4827 if (imgAttr.size.has_value()) {
4828 imageLayoutProperty->UpdateUserDefinedIdealSize(imgAttr.size->GetSize());
4829 }
4830 if (imgAttr.verticalAlign.has_value()) {
4831 imageLayoutProperty->UpdateVerticalAlign(imgAttr.verticalAlign.value());
4832 }
4833 if (imgAttr.objectFit.has_value()) {
4834 imageLayoutProperty->UpdateImageFit(imgAttr.objectFit.value());
4835 }
4836 if (imgAttr.marginProp.has_value()) {
4837 imageLayoutProperty->UpdateMargin(imgAttr.marginProp.value());
4838 }
4839 if (imgAttr.paddingProp.has_value()) {
4840 imageLayoutProperty->UpdatePadding(imgAttr.paddingProp.value());
4841 }
4842 if (imgAttr.borderRadius.has_value()) {
4843 auto imageRenderCtx = imageNode->GetRenderContext();
4844 imageRenderCtx->UpdateBorderRadius(imgAttr.borderRadius.value());
4845 imageRenderCtx->SetClipToBounds(true);
4846 }
4847 auto paintProperty = imageNode->GetPaintProperty<ImageRenderProperty>();
4848 if (imgAttr.colorFilterMatrix.has_value() && paintProperty) {
4849 paintProperty->UpdateColorFilter(imgAttr.colorFilterMatrix.value());
4850 paintProperty->ResetDrawingColorFilter();
4851 } else if (imgAttr.drawingColorFilter.has_value() && paintProperty) {
4852 paintProperty->UpdateDrawingColorFilter(imgAttr.drawingColorFilter.value());
4853 paintProperty->ResetColorFilter();
4854 }
4855 }
4856 imageNode->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
4857 imageNode->MarkModifyDone();
4858 imageItem->imageNodeId = imageNode->GetId();
4859 imageNode->SetImageItem(imageItem);
4860 childNodes_.emplace_back(imageNode);
4861 }
4862
SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)4863 void TextPattern::SetImageNodeGesture(RefPtr<ImageSpanNode> imageNode)
4864 {
4865 auto gesture = imageNode->GetOrCreateGestureEventHub();
4866 CHECK_NULL_VOID(gesture);
4867 gesture->SetHitTestMode(HitTestMode::HTMNONE);
4868 }
4869
CreateImageSourceInfo(const ImageSpanOptions & options)4870 ImageSourceInfo TextPattern::CreateImageSourceInfo(const ImageSpanOptions& options)
4871 {
4872 std::string src;
4873 RefPtr<PixelMap> pixMap = nullptr;
4874 std::string bundleName;
4875 std::string moduleName;
4876 if (options.image.has_value()) {
4877 src = options.image.value();
4878 }
4879 if (options.imagePixelMap.has_value()) {
4880 pixMap = options.imagePixelMap.value();
4881 }
4882 if (options.bundleName.has_value()) {
4883 bundleName = options.bundleName.value();
4884 }
4885 if (options.moduleName.has_value()) {
4886 moduleName = options.moduleName.value();
4887 }
4888 ImageSourceInfo info;
4889 #if defined(PIXEL_MAP_SUPPORTED)
4890 if (!options.imagePixelMap.has_value()) {
4891 info = ImageSourceInfo{ src, bundleName, moduleName };
4892 } else {
4893 info = ImageSourceInfo(pixMap);
4894 }
4895 #else
4896 info = ImageSourceInfo{ src, bundleName, moduleName };
4897 #endif
4898 info.SetIsUriPureNumber(options.isUriPureNumber.value_or(false));
4899 return info;
4900 }
4901
ProcessSpanString()4902 void TextPattern::ProcessSpanString()
4903 {
4904 auto host = GetHost();
4905 CHECK_NULL_VOID(host);
4906 textForDisplay_.clear();
4907 childNodes_.clear();
4908 dataDetectorAdapter_->textForAI_.clear();
4909 host->Clean();
4910 hasSpanStringLongPressEvent_ = false;
4911 hasUrlSpan_ = false;
4912
4913 // 适配AI&&挂载image节点
4914 for (const auto& span : spans_) {
4915 auto imageSpan = DynamicCast<ImageSpanItem>(span);
4916 if (imageSpan) {
4917 dataDetectorAdapter_->textForAI_ += u'\n';
4918 MountImageNode(imageSpan);
4919 } else {
4920 dataDetectorAdapter_->textForAI_ += span->content;
4921 }
4922 if (span->onClick || span->urlOnRelease) {
4923 auto gestureEventHub = host->GetOrCreateGestureEventHub();
4924 InitClickEvent(gestureEventHub);
4925 }
4926 if (span->onLongPress) {
4927 auto gestureEventHub = host->GetOrCreateGestureEventHub();
4928 InitLongPressEvent(gestureEventHub);
4929 hasSpanStringLongPressEvent_ = true;
4930 }
4931 if (span->urlOnRelease) {
4932 hasUrlSpan_ = true;
4933 InitUrlMouseEvent();
4934 InitUrlTouchEvent();
4935 }
4936 textForDisplay_ += span->content;
4937 }
4938 if (dataDetectorAdapter_->textForAI_ != textForDisplay_) {
4939 dataDetectorAdapter_->aiDetectInitialized_ = false;
4940 }
4941 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
4942 dataDetectorAdapter_->StartAITask();
4943 }
4944
4945 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4946 CHECK_NULL_VOID(layoutProperty);
4947 layoutProperty->UpdateContent(textForDisplay_);
4948 }
4949
OnSensitiveStyleChange(bool isSensitive)4950 void TextPattern::OnSensitiveStyleChange(bool isSensitive)
4951 {
4952 auto host = GetHost();
4953 CHECK_NULL_VOID(host);
4954 isSensitive_ = isSensitive;
4955 host->MarkDirtyWithOnProChange(PROPERTY_UPDATE_MEASURE);
4956 }
4957
IsSensitiveEnalbe()4958 bool TextPattern::IsSensitiveEnalbe()
4959 {
4960 auto host = GetHost();
4961 CHECK_NULL_RETURN(host, false);
4962 return isSensitive_ && host->IsPrivacySensitive();
4963 }
4964
ConvertGlobalToLocalOffset(const Offset & globalOffset)4965 Offset TextPattern::ConvertGlobalToLocalOffset(const Offset& globalOffset)
4966 {
4967 auto localPoint = OffsetF(globalOffset.GetX(), globalOffset.GetY());
4968 selectOverlay_->RevertLocalPointWithTransform(localPoint);
4969 return Offset(localPoint.GetX(), localPoint.GetY());
4970 }
4971
SetExternalSpanItem(const std::list<RefPtr<SpanItem>> & spans)4972 void TextPattern::SetExternalSpanItem(const std::list<RefPtr<SpanItem>>& spans)
4973 {
4974 isSpanStringMode_ = !spans.empty();
4975 if (isSpanStringMode_) {
4976 AllocStyledString();
4977 }
4978 spans_ = spans;
4979 ProcessSpanString();
4980 auto layoutProperty = GetLayoutProperty<TextLayoutProperty>();
4981 CHECK_NULL_VOID(layoutProperty);
4982 layoutProperty->UpdateContent(textForDisplay_);
4983 }
4984
GetTextContentRect(bool isActualText) const4985 RectF TextPattern::GetTextContentRect(bool isActualText) const
4986 {
4987 auto textRect = contentRect_;
4988 auto host = GetHost();
4989 CHECK_NULL_RETURN(host, textRect);
4990 auto renderContext = host->GetRenderContext();
4991 CHECK_NULL_RETURN(renderContext, textRect);
4992 CHECK_NULL_RETURN(pManager_, textRect);
4993 if (!renderContext->GetClipEdge().value_or(false) &&
4994 LessNotEqual(textRect.Width(), pManager_->GetLongestLine())) {
4995 textRect.SetWidth(pManager_->GetLongestLine());
4996 }
4997 if (isActualText && !renderContext->GetClipEdge().value_or(false) &&
4998 LessNotEqual(textRect.Height(), pManager_->GetHeight())) {
4999 textRect.SetHeight(pManager_->GetHeight());
5000 }
5001 return textRect;
5002 }
5003
GetLineCount() const5004 size_t TextPattern::GetLineCount() const
5005 {
5006 CHECK_NULL_RETURN(pManager_, 0);
5007 return pManager_->GetLineCount();
5008 }
5009
DidExceedMaxLines() const5010 bool TextPattern::DidExceedMaxLines() const
5011 {
5012 CHECK_NULL_RETURN(pManager_, false);
5013 return pManager_->DidExceedMaxLines();
5014 }
5015
IsSetObscured()5016 bool TextPattern::IsSetObscured()
5017 {
5018 auto host = GetHost();
5019 CHECK_NULL_RETURN(host, false);
5020 auto renderContext = host->GetRenderContext();
5021 CHECK_NULL_RETURN(renderContext, false);
5022 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
5023 bool ifHaveObscured = spans_.empty() && std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
5024 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
5025 return ifHaveObscured;
5026 }
5027
GetLineMetrics(int32_t lineNumber)5028 TextLineMetrics TextPattern::GetLineMetrics(int32_t lineNumber)
5029 {
5030 CHECK_NULL_RETURN(pManager_, TextLineMetrics());
5031 if (lineNumber < 0 || GetLineCount() == 0 || lineNumber > static_cast<int32_t>(GetLineCount()) - 1) {
5032 TAG_LOGI(AceLogTag::ACE_TEXT, "GetLineMetrics failed, lineNumber not between 0 and max lines:%{public}d",
5033 lineNumber);
5034 return TextLineMetrics();
5035 }
5036 auto lineMetrics = pManager_->GetLineMetrics(lineNumber);
5037 RectF textContentRect = contentRect_;
5038 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5039 lineMetrics.x += textContentRect.GetX();
5040 lineMetrics.y += textContentRect.GetY();
5041 lineMetrics.baseline += textContentRect.GetY();
5042 return lineMetrics;
5043 }
5044
GetRectsForRange(int32_t start,int32_t end,RectHeightStyle heightStyle,RectWidthStyle widthStyle)5045 std::vector<ParagraphManager::TextBox> TextPattern::GetRectsForRange(
5046 int32_t start, int32_t end, RectHeightStyle heightStyle, RectWidthStyle widthStyle)
5047 {
5048 if (start < 0 || end < 0 || start > end) {
5049 return {};
5050 }
5051 std::vector<ParagraphManager::TextBox> textBoxes = pManager_->GetRectsForRange(start, end, heightStyle, widthStyle);
5052 RectF textContentRect = contentRect_;
5053 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5054 std::vector<ParagraphManager::TextBox> adjustedTextBoxes;
5055 for (auto& textBox : textBoxes) {
5056 ParagraphManager::TextBox adjustedTextBox = textBox;
5057 adjustedTextBox.rect_.SetLeft(textBox.rect_.Left() + textContentRect.Left());
5058 adjustedTextBox.rect_.SetTop(textBox.rect_.Top() + textContentRect.Top());
5059 adjustedTextBoxes.push_back(adjustedTextBox);
5060 }
5061 return adjustedTextBoxes;
5062 }
5063
ConvertLocalOffsetToParagraphOffset(const Offset & offset)5064 Offset TextPattern::ConvertLocalOffsetToParagraphOffset(const Offset& offset)
5065 {
5066 RectF textContentRect = contentRect_;
5067 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
5068 Offset paragraphOffset = { offset.GetX() - textContentRect.GetX(), offset.GetY() - textContentRect.GetY() };
5069 return paragraphOffset;
5070 }
5071
GetGlyphPositionAtCoordinate(int32_t x,int32_t y)5072 PositionWithAffinity TextPattern::GetGlyphPositionAtCoordinate(int32_t x, int32_t y)
5073 {
5074 Offset offset(x, y);
5075 return pManager_->GetGlyphPositionAtCoordinate(ConvertLocalOffsetToParagraphOffset(offset));
5076 }
5077
ProcessMarqueeVisibleAreaCallback()5078 void TextPattern::ProcessMarqueeVisibleAreaCallback()
5079 {
5080 OnTextOverflowChanged();
5081 if (!IsMarqueeOverflow()) {
5082 return;
5083 }
5084 auto host = GetHost();
5085 CHECK_NULL_VOID(host);
5086 auto pipeline = GetContext();
5087 CHECK_NULL_VOID(pipeline);
5088 auto callback = [weak = WeakClaim(this)](bool visible, double ratio) {
5089 auto pattern = weak.Upgrade();
5090 CHECK_NULL_VOID(pattern);
5091 CHECK_NULL_VOID(pattern->contentMod_);
5092 if (!pattern->IsMarqueeOverflow()) {
5093 return;
5094 }
5095 if (visible && Positive(ratio)) {
5096 pattern->contentMod_->ResumeAnimation();
5097 }
5098 if (!visible && NonPositive(ratio)) {
5099 pattern->contentMod_->PauseAnimation();
5100 }
5101 };
5102 std::vector<double> ratioList = { 0.0 };
5103 pipeline->AddVisibleAreaChangeNode(host, ratioList, callback, false, true);
5104 }
5105
OnTextOverflowChanged()5106 void TextPattern::OnTextOverflowChanged()
5107 {
5108 auto host = GetHost();
5109 CHECK_NULL_VOID(host);
5110 if (host->GetTag() == V2::SYMBOL_ETS_TAG) {
5111 return;
5112 }
5113 auto pipeline = GetContext();
5114 CHECK_NULL_VOID(pipeline);
5115 auto eventHub = host->GetEventHub<TextEventHub>();
5116 CHECK_NULL_VOID(eventHub);
5117 auto hasInnerCallabck = eventHub->HasVisibleAreaCallback(false);
5118 if (!hasInnerCallabck) {
5119 return;
5120 }
5121 auto hasUserCallback = eventHub->HasVisibleAreaCallback(true);
5122 if (!hasUserCallback) {
5123 pipeline->RemoveVisibleAreaChangeNode(host->GetId());
5124 }
5125 eventHub->CleanVisibleAreaCallback(false);
5126 }
5127
OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)5128 void TextPattern::OnFrameNodeChanged(FrameNodeChangeInfoFlag flag)
5129 {
5130 if (selectOverlay_->SelectOverlayIsOn()) {
5131 selectOverlay_->OnAncestorNodeChanged(flag);
5132 }
5133 if (leftMousePressed_ && mouseStatus_ == MouseStatus::MOVE && scrollableParent_.Upgrade()) {
5134 auto host = GetHost();
5135 CHECK_NULL_VOID(host);
5136 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
5137 NG::PointF localPoint(lastLeftMouseMoveLocation_.GetX(), lastLeftMouseMoveLocation_.GetY());
5138 NG::NGGestureRecognizer::Transform(localPoint, WeakClaim(Referenced::RawPtr(host)), true);
5139 Offset textOffset = { localPoint.GetX() - textPaintOffset.GetX(), localPoint.GetY() - textPaintOffset.GetY() };
5140 CHECK_NULL_VOID(pManager_);
5141 auto end = pManager_->GetGlyphIndexByCoordinate(textOffset);
5142 HandleSelectionChange(textSelector_.baseOffset, end);
5143 isAutoScrollByMouse_ = true;
5144 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5145 }
5146 }
5147
IsMarqueeOverflow() const5148 bool TextPattern::IsMarqueeOverflow() const
5149 {
5150 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5151 CHECK_NULL_RETURN(textLayoutProperty, false);
5152 return textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE;
5153 }
5154
UpdateFontColor(const Color & value)5155 void TextPattern::UpdateFontColor(const Color& value)
5156 {
5157 auto host = GetHost();
5158 CHECK_NULL_VOID(host);
5159 const auto& children = host->GetChildren();
5160 if (children.empty() && spans_.empty() && !NeedShowAIDetect()) {
5161 if (contentMod_) {
5162 contentMod_->TextColorModifier(value);
5163 } else if (pManager_) {
5164 if (textStyle_.has_value()) {
5165 textStyle_->SetTextColor(value);
5166 }
5167 for (auto&& info : pManager_->GetParagraphs()) {
5168 auto paragraph = info.paragraph;
5169 CHECK_NULL_VOID(paragraph);
5170 auto length = paragraph->GetParagraphText().length();
5171 paragraph->UpdateColor(0, length, value);
5172 }
5173 }
5174 } else {
5175 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
5176 }
5177 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5178 CHECK_NULL_VOID(textLayoutProperty);
5179 textLayoutProperty->OnPropertyChangeMeasure();
5180 }
5181
MarkDirtyNodeRender()5182 void TextPattern::MarkDirtyNodeRender()
5183 {
5184 auto host = GetHost();
5185 CHECK_NULL_VOID(host);
5186 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5187 }
5188
BeforeCreatePaintWrapper()5189 void TextPattern::BeforeCreatePaintWrapper()
5190 {
5191 // mark content dirty
5192 if (contentMod_) {
5193 contentMod_->ContentChange();
5194 }
5195 }
5196
StartGestureSelection(int32_t start,int32_t end,const Offset & startOffset)5197 void TextPattern::StartGestureSelection(int32_t start, int32_t end, const Offset& startOffset)
5198 {
5199 scrollableParent_ = selectOverlay_->FindScrollableParent();
5200 SetupMagnifier();
5201 TextGestureSelector::StartGestureSelection(start, end, startOffset);
5202 }
5203
GetTouchIndex(const OffsetF & offset)5204 int32_t TextPattern::GetTouchIndex(const OffsetF& offset)
5205 {
5206 OffsetF deltaOffset;
5207 if (scrollableParent_.Upgrade()) {
5208 auto parentGlobalOffset = GetParentGlobalOffset();
5209 deltaOffset = parentGlobalOffset - parentGlobalOffset_;
5210 }
5211 auto paragraphOffset =
5212 offset - deltaOffset - GetTextContentRect().GetOffset() + OffsetF(0.0f, std::min(GetBaselineOffset(), 0.0f));
5213 return GetHandleIndex({ paragraphOffset.GetX(), paragraphOffset.GetY() });
5214 }
5215
OnTextGestureSelectionUpdate(int32_t start,int32_t end,const TouchEventInfo & info)5216 void TextPattern::OnTextGestureSelectionUpdate(int32_t start, int32_t end, const TouchEventInfo& info)
5217 {
5218 if (!HasContent()) {
5219 return;
5220 }
5221 selectOverlay_->TriggerScrollableParentToScroll(
5222 scrollableParent_.Upgrade(), info.GetTouches().front().GetGlobalLocation(), false);
5223 auto localOffset = info.GetTouches().front().GetLocalLocation();
5224 if (GetOrCreateMagnifier()) {
5225 magnifierController_->SetLocalOffset({ localOffset.GetX(), localOffset.GetY() });
5226 }
5227 if (start != textSelector_.GetStart()) {
5228 StartVibratorByIndexChange(start, textSelector_.GetStart());
5229 } else if (end != textSelector_.GetEnd()) {
5230 StartVibratorByIndexChange(end, textSelector_.GetEnd());
5231 }
5232 auto host = GetHost();
5233 CHECK_NULL_VOID(host);
5234 HandleSelectionChange(start, end);
5235 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
5236 }
5237
OnTextGenstureSelectionEnd(const TouchLocationInfo & locationInfo)5238 void TextPattern::OnTextGenstureSelectionEnd(const TouchLocationInfo& locationInfo)
5239 {
5240 selectOverlay_->TriggerScrollableParentToScroll(scrollableParent_.Upgrade(), Offset(), true);
5241 if (magnifierController_) {
5242 magnifierController_->RemoveMagnifierFrameNode();
5243 }
5244 if (HasContent()) {
5245 CalculateHandleOffsetAndShowOverlay();
5246 oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
5247 ShowSelectOverlay({ .animation = true });
5248 }
5249 }
5250
ChangeHandleHeight(const GestureEvent & event,bool isFirst,bool isOverlayMode)5251 void TextPattern::ChangeHandleHeight(const GestureEvent& event, bool isFirst, bool isOverlayMode)
5252 {
5253 auto touchOffset = event.GetGlobalLocation();
5254 auto& currentHandle = isFirst ? textSelector_.firstHandle : textSelector_.secondHandle;
5255 bool isChangeFirstHandle = isFirst ? (!textSelector_.StartGreaterDest()) : textSelector_.StartGreaterDest();
5256 if (isChangeFirstHandle) {
5257 ChangeFirstHandleHeight(touchOffset, currentHandle);
5258 } else {
5259 ChangeSecondHandleHeight(touchOffset, currentHandle);
5260 }
5261 }
5262
ChangeFirstHandleHeight(const Offset & touchOffset,RectF & handleRect)5263 void TextPattern::ChangeFirstHandleHeight(const Offset& touchOffset, RectF& handleRect)
5264 {
5265 auto height = handleRect.Height();
5266 CalculateDefaultHandleHeight(height);
5267 bool isTouchHandleCircle = LessNotEqual(touchOffset.GetY(), handleRect.Top());
5268 if (!isTouchHandleCircle) {
5269 handleRect.SetTop(static_cast<float>(touchOffset.GetY()) - height / 2.0f);
5270 }
5271 handleRect.SetHeight(height);
5272 }
5273
ChangeSecondHandleHeight(const Offset & touchOffset,RectF & handleRect)5274 void TextPattern::ChangeSecondHandleHeight(const Offset& touchOffset, RectF& handleRect)
5275 {
5276 auto height = handleRect.Height();
5277 CalculateDefaultHandleHeight(height);
5278 bool isTouchHandleCircle = GreatNotEqual(touchOffset.GetY(), handleRect.Bottom());
5279 auto handleOffsetY = isTouchHandleCircle
5280 ? handleRect.Bottom() - height
5281 : static_cast<float>(touchOffset.GetY()) - height / 2.0f;
5282 handleRect.SetTop(handleOffsetY);
5283 handleRect.SetHeight(height);
5284 }
5285
CalculateDefaultHandleHeight(float & height)5286 void TextPattern::CalculateDefaultHandleHeight(float& height)
5287 {
5288 CHECK_NULL_VOID(textStyle_.has_value());
5289 #ifdef ENABLE_ROSEN_BACKEND
5290 MeasureContext content;
5291 content.textContent = "a";
5292 content.fontSize = textStyle_.value().GetFontSize();
5293 auto fontweight = StringUtils::FontWeightToString(textStyle_.value().GetFontWeight());
5294 content.fontWeight = fontweight;
5295 height = std::max(static_cast<float>(RosenRenderCustomPaint::MeasureTextSizeInner(content).Height()), 0.0f);
5296 #endif
5297 }
5298
DumpAdvanceInfo(std::unique_ptr<JsonValue> & json)5299 void TextPattern::DumpAdvanceInfo(std::unique_ptr<JsonValue>& json)
5300 {
5301 json->Put("contentRect", contentRect_.ToString().c_str());
5302 if (SystemProperties::GetDebugEnabled() && pManager_) {
5303 std::unique_ptr<JsonValue> children = JsonUtil::Create(true);
5304 children->Put("DidExceedMaxLines", std::to_string(pManager_->DidExceedMaxLines()).c_str());
5305 children->Put("GetTextWidth", std::to_string(pManager_->GetTextWidth()).c_str());
5306 children->Put("GetHeight", std::to_string(pManager_->GetHeight()).c_str());
5307 children->Put("GetMaxWidth", std::to_string(pManager_->GetMaxWidth()).c_str());
5308 children->Put("GetMaxIntrinsicWidth", std::to_string(pManager_->GetMaxIntrinsicWidth()).c_str());
5309 children->Put("GetLineCount", std::to_string(pManager_->GetLineCount()).c_str());
5310 children->Put("GetLongestLine", std::to_string(pManager_->GetLongestLine()).c_str());
5311 children->Put("GetLongestLineWithIndent", std::to_string(pManager_->GetLongestLineWithIndent()).c_str());
5312 json->Put("from TextEngine paragraphs_ info", children);
5313 }
5314 json->Put("BindSelectionMenu", std::to_string(selectionMenuMap_.empty()).c_str());
5315 auto host = GetHost();
5316 CHECK_NULL_VOID(host);
5317 auto pipeline = host->GetContext();
5318 CHECK_NULL_VOID(pipeline);
5319 auto fontScale = pipeline->GetFontScale();
5320 auto fontWeightScale = pipeline->GetFontWeightScale();
5321 json->Put("fontScale", std::to_string(fontScale).c_str());
5322 json->Put("fontWeightScale", std::to_string(fontWeightScale).c_str());
5323 auto renderContext = host->GetRenderContext();
5324 CHECK_NULL_VOID(renderContext);
5325 if (renderContext->HasForegroundColor()) {
5326 json->Put("ForegroundColor", renderContext->GetForegroundColorValue().ColorToString().c_str());
5327 }
5328 if (renderContext->GetForegroundColorStrategy().has_value()) {
5329 auto strategy = static_cast<int32_t>(renderContext->GetForegroundColorStrategyValue());
5330 json->Put("ForegroundColorStrategy", strategy);
5331 }
5332 }
5333
SetTextStyleDumpInfo(std::unique_ptr<JsonValue> & json)5334 void TextPattern::SetTextStyleDumpInfo(std::unique_ptr<JsonValue>& json)
5335 {
5336 if (textStyle_.has_value()) {
5337 json->Put("MaxFontSize", textStyle_->GetAdaptMaxFontSize().ToString().c_str());
5338 json->Put("MinFontSize", textStyle_->GetAdaptMinFontSize().ToString().c_str());
5339 json->Put("FontWeight", StringUtils::ToString(textStyle_->GetFontWeight()).c_str());
5340 json->Put("FontStyle", StringUtils::ToString(textStyle_->GetFontStyle()).c_str());
5341 json->Put("LineHeight", textStyle_->GetLineHeight().ToString().c_str());
5342 json->Put("LineSpacing", textStyle_->GetLineSpacing().ToString().c_str());
5343 json->Put("BaselineOffset", textStyle_->GetBaselineOffset().ToString().c_str());
5344 json->Put("TextIndent", textStyle_->GetTextIndent().ToString().c_str());
5345 json->Put("LetterSpacing", textStyle_->GetLetterSpacing().ToString().c_str());
5346 json->Put("TextOverflow", StringUtils::ToString(textStyle_->GetTextOverflow()).c_str());
5347 json->Put("TextAlign", StringUtils::ToString(textStyle_->GetTextAlign()).c_str());
5348 json->Put("WordBreak", StringUtils::ToString(textStyle_->GetWordBreak()).c_str());
5349 json->Put("TextCase", StringUtils::ToString(textStyle_->GetTextCase()).c_str());
5350 json->Put("EllipsisMode", StringUtils::ToString(textStyle_->GetEllipsisMode()).c_str());
5351 json->Put("LineBreakStrategy", GetLineBreakStrategyInJson(textStyle_->GetLineBreakStrategy()).c_str());
5352 }
5353 }
5354
DumpInfo(std::unique_ptr<JsonValue> & json)5355 void TextPattern::DumpInfo(std::unique_ptr<JsonValue>& json)
5356 {
5357 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
5358 CHECK_NULL_VOID(textLayoutProp);
5359 auto nowTime = GetSystemTimestamp();
5360 json->Put("time", std::to_string(nowTime).c_str());
5361 if (!IsSetObscured() && !IsSensitiveEnalbe()) {
5362 json->Put("Content", UtfUtils::Str16DebugToStr8(textLayoutProp->GetContent().value_or(u" ")).c_str());
5363 }
5364 json->Put("ConteFontColornt",
5365 (textStyle_.has_value() ? textStyle_->GetTextColor() : Color::BLACK).ColorToString().c_str());
5366 json->Put(
5367 "FontSize", (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(DIMENSION_VALUE, DimensionUnit::FP))
5368 .ToString()
5369 .c_str());
5370 SetTextStyleDumpInfo(json);
5371 json->Put("HeightAdaptivePolicy",
5372 V2::ConvertWrapTextHeightAdaptivePolicyToString(
5373 textLayoutProp->GetHeightAdaptivePolicy().value_or(TextHeightAdaptivePolicy::MAX_LINES_FIRST))
5374 .c_str());
5375
5376 json->Put("Selection", textSelector_.ToString().c_str());
5377
5378 if (pManager_ && !pManager_->GetParagraphs().empty()) {
5379 auto num = static_cast<int32_t>(pManager_->GetParagraphs().size());
5380 json->Put("Paragraphs num", std::to_string(num).c_str());
5381 json->Put("PaintInfo", paintInfo_.c_str());
5382 }
5383 if (SystemProperties::GetDebugEnabled()) {
5384 DumpAdvanceInfo(json);
5385 }
5386 }
5387
HasContent()5388 bool TextPattern::HasContent()
5389 {
5390 if (GetTextForDisplay().empty()) {
5391 for (const auto& span : spans_) {
5392 if (span->spanItemType != SpanItemType::NORMAL) {
5393 return true;
5394 }
5395 }
5396 return false;
5397 }
5398 return true;
5399 }
5400
SetupMagnifier()5401 void TextPattern::SetupMagnifier()
5402 {
5403 GetOrCreateMagnifier();
5404 CHECK_NULL_VOID(magnifierController_);
5405 auto host = GetHost();
5406 CHECK_NULL_VOID(host);
5407 auto renderContext = host->GetRenderContext();
5408 CHECK_NULL_VOID(renderContext);
5409 if (renderContext->GetClipEdge().value_or(false)) {
5410 return;
5411 }
5412 RectF viewPort;
5413 if (selectOverlay_->GetClipHandleViewPort(viewPort)) {
5414 viewPort.SetHeight(std::min(pManager_->GetHeight(), viewPort.Height()));
5415 magnifierController_->SetHostViewPort(viewPort);
5416 }
5417 }
5418
DoTextSelectionTouchCancel()5419 void TextPattern::DoTextSelectionTouchCancel()
5420 {
5421 CHECK_NULL_VOID(magnifierController_);
5422 magnifierController_->RemoveMagnifierFrameNode();
5423 ResetSelection();
5424 }
5425
BeforeSyncGeometryProperties(const DirtySwapConfig & config)5426 void TextPattern::BeforeSyncGeometryProperties(const DirtySwapConfig& config)
5427 {
5428 if (afterLayoutCallback_.has_value()) {
5429 (*afterLayoutCallback_)();
5430 }
5431 }
5432
GetCaretColor() const5433 std::string TextPattern::GetCaretColor() const
5434 {
5435 auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
5436 CHECK_NULL_RETURN(context, "");
5437 auto theme = context->GetTheme<TextTheme>();
5438 CHECK_NULL_RETURN(theme, "");
5439 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5440 CHECK_NULL_RETURN(textLayoutProperty, "");
5441 return textLayoutProperty->GetCursorColorValue(theme->GetCaretColor()).ColorToString();
5442 }
5443
GetSelectedBackgroundColor() const5444 std::string TextPattern::GetSelectedBackgroundColor() const
5445 {
5446 auto context = PipelineContext::GetCurrentContextSafelyWithCheck();
5447 CHECK_NULL_RETURN(context, "");
5448 auto theme = context->GetTheme<TextTheme>();
5449 CHECK_NULL_RETURN(theme, "");
5450 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
5451 CHECK_NULL_RETURN(textLayoutProperty, "");
5452 return textLayoutProperty->GetSelectedBackgroundColorValue(theme->GetSelectedColor()).ColorToString();
5453 }
5454
OnWindowSizeChanged(int32_t width,int32_t height,WindowSizeChangeReason type)5455 void TextPattern::OnWindowSizeChanged(int32_t width, int32_t height, WindowSizeChangeReason type)
5456 {
5457 CHECK_NULL_VOID(selectOverlay_);
5458 selectOverlay_->UpdateMenuOnWindowSizeChanged(type);
5459 }
5460
IsLocationInFrameRegion(const Offset & localOffset) const5461 bool TextPattern::IsLocationInFrameRegion(const Offset& localOffset) const
5462 {
5463 auto host = GetHost();
5464 CHECK_NULL_RETURN(host, false);
5465 auto geometryNode = host->GetGeometryNode();
5466 CHECK_NULL_RETURN(geometryNode, false);
5467 auto frameSize = geometryNode->GetFrameSize();
5468 auto frameRect = RectF(OffsetF(0.0f, 0.0f), frameSize);
5469 return frameRect.IsInRegion(PointF(localOffset.GetX(), localOffset.GetY()));
5470 }
5471 } // namespace OHOS::Ace::NG
5472