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