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