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 <stack>
20 #include <string>
21
22 #include "base/geometry/ng/offset_t.h"
23 #include "base/geometry/ng/rect_t.h"
24 #include "base/geometry/offset.h"
25 #include "base/log/dump_log.h"
26 #include "base/log/log_wrapper.h"
27 #include "base/utils/string_utils.h"
28 #include "base/utils/utils.h"
29 #include "base/window/drag_window.h"
30 #include "core/common/ace_engine_ext.h"
31 #include "core/common/ai/data_detector_mgr.h"
32 #include "core/common/container.h"
33 #include "core/common/container_scope.h"
34 #include "core/common/font_manager.h"
35 #include "core/common/recorder/event_recorder.h"
36 #include "core/common/recorder/node_data_cache.h"
37 #include "core/common/udmf/udmf_client.h"
38 #include "core/components/text_overlay/text_overlay_theme.h"
39 #include "core/components_ng/base/ui_node.h"
40 #include "core/components_ng/base/view_stack_processor.h"
41 #include "core/components_ng/event/gesture_event_hub.h"
42 #include "core/components_ng/event/long_press_event.h"
43 #include "core/components_ng/manager/select_overlay/select_overlay_manager.h"
44 #include "core/components_ng/pattern/image/image_layout_property.h"
45 #include "core/components_ng/pattern/rich_editor_drag/rich_editor_drag_pattern.h"
46 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
47 #include "core/components_ng/pattern/text/text_event_hub.h"
48 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
49 #include "core/components_ng/pattern/text/text_layout_property.h"
50 #include "core/components_ng/pattern/text_drag/text_drag_pattern.h"
51 #include "core/components_ng/property/property.h"
52
53 namespace OHOS::Ace::NG {
54 namespace {
55 constexpr double DIMENSION_VALUE = 16.0;
56 constexpr const char COPY_ACTION[] = "copy";
57 constexpr const char SELECT_ACTION[] = "select";
58 constexpr const char SYMBOL_COLOR[] = "BLACK";
59 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
60 // uncertainty range when comparing selectedTextBox to contentRect
61 constexpr float BOX_EPSILON = 0.5f;
62 constexpr float DOUBLECLICK_INTERVAL_MS = 300.0f;
63 constexpr uint32_t SECONDS_TO_MILLISECONDS = 1000;
64 const std::u16string SYMBOL_TRANS = u"\uF0001";
65 const std::string NEWLINE = "\n";
66 const std::wstring WIDE_NEWLINE = StringUtils::ToWstring(NEWLINE);
67 }; // namespace
68
OnAttachToFrameNode()69 void TextPattern::OnAttachToFrameNode()
70 {
71 auto pipeline = PipelineContext::GetCurrentContext();
72 CHECK_NULL_VOID(pipeline);
73 auto host = GetHost();
74 CHECK_NULL_VOID(host);
75 if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
76 host->GetRenderContext()->UpdateClipEdge(true);
77 }
78 InitSurfaceChangedCallback();
79 InitSurfacePositionChangedCallback();
80 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
81 textLayoutProperty->UpdateTextAlign(TextAlign::START);
82 textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
83 }
84
OnDetachFromFrameNode(FrameNode * node)85 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
86 {
87 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
88 CloseSelectOverlay();
89 auto pipeline = PipelineContext::GetCurrentContext();
90 CHECK_NULL_VOID(pipeline);
91 if (HasSurfaceChangedCallback()) {
92 pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
93 }
94 if (HasSurfacePositionChangedCallback()) {
95 pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
96 }
97 auto frameNode = WeakClaim(node);
98 pipeline->RemoveFontNodeNG(frameNode);
99 auto fontManager = pipeline->GetFontManager();
100 if (fontManager) {
101 fontManager->UnRegisterCallbackNG(frameNode);
102 fontManager->RemoveVariationNodeNG(frameNode);
103 }
104 pipeline->RemoveOnAreaChangeNode(node->GetId());
105 }
106
CloseSelectOverlay()107 void TextPattern::CloseSelectOverlay()
108 {
109 CloseSelectOverlay(false);
110 }
111
CloseSelectOverlay(bool animation)112 void TextPattern::CloseSelectOverlay(bool animation)
113 {
114 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
115 selectOverlayProxy_->Close(animation);
116 RemoveAreaChangeInner();
117 }
118 }
119
ResetSelection()120 void TextPattern::ResetSelection()
121 {
122 if (textSelector_.IsValid()) {
123 HandleSelectionChange(-1, -1);
124 auto host = GetHost();
125 CHECK_NULL_VOID(host);
126 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
127 }
128 }
129
InitSelection(const Offset & pos)130 void TextPattern::InitSelection(const Offset& pos)
131 {
132 CHECK_NULL_VOID(paragraph_);
133 int32_t extend = paragraph_->GetGlyphIndexByCoordinate(pos, true);
134 if (IsLineBreakOrEndOfParagraph(extend)) {
135 extend--;
136 }
137 int32_t start = 0;
138 int32_t end = 0;
139 if (!paragraph_->GetWordBoundary(extend, start, end)) {
140 start = extend;
141 end = std::min(static_cast<int32_t>(GetWideText().length()) + placeholderCount_,
142 extend + GetGraphemeClusterLength(GetWideText(), extend));
143 }
144 HandleSelectionChange(start, end);
145 }
146
IsLineBreakOrEndOfParagraph(int32_t pos) const147 bool TextPattern::IsLineBreakOrEndOfParagraph(int32_t pos) const
148 {
149 CHECK_NULL_RETURN(pos < static_cast<int32_t>(GetWideText().length() + placeholderCount_), true);
150 auto data = GetWideText();
151 CHECK_NULL_RETURN(data[pos] == WIDE_NEWLINE[0], false);
152 return true;
153 }
154
CalcCaretMetricsByPosition(int32_t extent,CaretMetricsF & caretCaretMetric,TextAffinity textAffinity)155 void TextPattern::CalcCaretMetricsByPosition(int32_t extent, CaretMetricsF& caretCaretMetric, TextAffinity textAffinity)
156 {
157 auto host = GetHost();
158 CHECK_NULL_VOID(host);
159 auto rect = host->GetGeometryNode()->GetFrameRect();
160 CHECK_NULL_VOID(paragraph_);
161 auto computeSuccess = paragraph_->CalcCaretMetricsByPosition(extent, caretCaretMetric, textAffinity);
162 if (!computeSuccess) {
163 caretCaretMetric = CaretMetricsF(OffsetF(0.0f, rect.Width()), 0.0f);
164 }
165 }
166
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)167 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
168 {
169 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
170 auto paragraphPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
171
172 // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
173 CaretMetricsF firstHandleMetrics;
174 CaretMetricsF secondHandleMetrics;
175 CalcCaretMetricsByPosition(textSelector_.baseOffset, firstHandleMetrics, TextAffinity::DOWNSTREAM);
176 CalcCaretMetricsByPosition(textSelector_.destinationOffset, secondHandleMetrics, TextAffinity::UPSTREAM);
177 OffsetF firstHandleOffset = firstHandleMetrics.offset + paragraphPaintOffset;
178 OffsetF secondHandleOffset = secondHandleMetrics.offset + paragraphPaintOffset;
179
180 textSelector_.selectionBaseOffset = firstHandleOffset;
181 textSelector_.selectionDestinationOffset = secondHandleOffset;
182
183 RectF firstHandle;
184 firstHandle.SetOffset(firstHandleOffset);
185 firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), firstHandleMetrics.height });
186 textSelector_.firstHandle = firstHandle;
187
188 RectF secondHandle;
189 secondHandle.SetOffset(secondHandleOffset);
190 secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), secondHandleMetrics.height });
191 secondHandle.SetHeight(secondHandleMetrics.height);
192 textSelector_.secondHandle = secondHandle;
193 }
194
GetSpansInfo(int32_t start,int32_t end,GetSpansMethod method)195 SelectionInfo TextPattern::GetSpansInfo(int32_t start, int32_t end, GetSpansMethod method)
196 {
197 int32_t index = 0;
198 std::int32_t realEnd = 0;
199 std::int32_t realStart = 0;
200 SelectionInfo selection;
201 std::list<ResultObject> resultObjects;
202 auto length = GetTextContentLength();
203 if (method == GetSpansMethod::GETSPANS) {
204 realStart = (start == -1) ? 0 : start;
205 realEnd = (end == -1) ? length : end;
206 if (realStart > realEnd) {
207 std::swap(realStart, realEnd);
208 }
209 realStart = std::max(0, realStart);
210 realEnd = std::min(length, realEnd);
211 } else if (method == GetSpansMethod::ONSELECT) {
212 realEnd = std::min(length, end);
213 realStart = std::min(length, start);
214 }
215 selection.SetSelectionEnd(realEnd);
216 selection.SetSelectionStart(realStart);
217 // Verify that realStart, realEnd, and spans_ are valid
218 if (realStart > length || realEnd < 0 || spans_.empty() || (start > length && end > length) ||
219 (method == GetSpansMethod::ONSELECT && realStart == realEnd)) {
220 selection.SetResultObjectList(resultObjects);
221 return selection;
222 }
223 auto children = GetAllChildren();
224 for (const auto& uinode : children) {
225 if (uinode->GetTag() == V2::IMAGE_ETS_TAG) {
226 ResultObject resultObject = GetImageResultObject(uinode, index, realStart, realEnd);
227 if (!resultObject.valueString.empty() || resultObject.valuePixelMap) {
228 resultObjects.emplace_back(resultObject);
229 }
230 } else if (uinode->GetTag() == V2::SPAN_ETS_TAG) {
231 ResultObject resultObject = GetTextResultObject(uinode, index, realStart, realEnd);
232 if (!resultObject.valueString.empty()) {
233 resultObjects.emplace_back(resultObject);
234 }
235 } else if (uinode->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
236 ResultObject resultObject = GetSymbolSpanResultObject(uinode, index, realStart, realEnd);
237 if (!resultObject.valueString.empty()) {
238 resultObjects.emplace_back(resultObject);
239 }
240 } else if (uinode->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
241 ResultObject resultObject = GetBuilderResultObject(uinode, index, realStart, realEnd);
242 if (!resultObject.valueString.empty()) {
243 resultObjects.emplace_back(resultObject);
244 }
245 }
246 index++;
247 }
248 selection.SetResultObjectList(resultObjects);
249 return selection;
250 }
251
GetTextContentLength()252 int32_t TextPattern::GetTextContentLength()
253 {
254 if (!spans_.empty()) {
255 return static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
256 }
257 return 0;
258 }
259
HandleLongPress(GestureEvent & info)260 void TextPattern::HandleLongPress(GestureEvent& info)
261 {
262 if (copyOption_ == CopyOptions::None || isMousePressed_) {
263 return;
264 }
265 auto host = GetHost();
266 CHECK_NULL_VOID(host);
267 auto hub = host->GetEventHub<EventHub>();
268 CHECK_NULL_VOID(hub);
269 auto gestureHub = hub->GetOrCreateGestureEventHub();
270 CHECK_NULL_VOID(gestureHub);
271 if (IsDraggable(info.GetLocalLocation())) {
272 dragBoxes_ = GetTextBoxes();
273 // prevent long press event from being triggered when dragging
274 gestureHub->SetIsTextDraggable(true);
275 return;
276 }
277 gestureHub->SetIsTextDraggable(false);
278 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
279 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
280 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
281 InitSelection(textOffset);
282 textResponseType_ = TextResponseType::LONG_PRESS;
283 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
284 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
285 oldSelectedType_ = selectedType_.value_or(TextSpanType::NONE);
286 parentGlobalOffset_ = GetParentGlobalOffset();
287 CalculateHandleOffsetAndShowOverlay();
288 CloseSelectOverlay(true);
289 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
290 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
291 }
292
OnHandleMove(const RectF & handleRect,bool isFirstHandle)293 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
294 {
295 auto host = GetHost();
296 CHECK_NULL_VOID(host);
297 auto textContentGlobalOffset = parentGlobalOffset_ + contentRect_.GetOffset();
298 auto textPaintOffset = textContentGlobalOffset - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
299
300 auto localOffset = handleRect.GetOffset();
301
302 if (localOffset.GetX() < textContentGlobalOffset.GetX()) {
303 localOffset.SetX(textContentGlobalOffset.GetX());
304 } else if (GreatOrEqual(localOffset.GetX(), textContentGlobalOffset.GetX() + contentRect_.Width())) {
305 localOffset.SetX(textContentGlobalOffset.GetX() + contentRect_.Width());
306 }
307
308 if (localOffset.GetY() < textContentGlobalOffset.GetY()) {
309 localOffset.SetY(textContentGlobalOffset.GetY());
310 } else if (GreatNotEqual(localOffset.GetY(), textContentGlobalOffset.GetY() + contentRect_.Height())) {
311 localOffset.SetY(textContentGlobalOffset.GetY() + contentRect_.Height());
312 }
313
314 localOffset -= textPaintOffset;
315
316 CHECK_NULL_VOID(paragraph_);
317 // the handle position is calculated based on the middle of the handle height.
318 if (isFirstHandle) {
319 auto start = GetHandleIndex(Offset(localOffset.GetX(),
320 localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() ? handleRect.Height() : 0)));
321 HandleSelectionChange(start, textSelector_.destinationOffset);
322 } else {
323 auto end = GetHandleIndex(Offset(localOffset.GetX(),
324 localOffset.GetY() + (selectOverlayProxy_->IsHandleReverse() || NearEqual(localOffset.GetY(), 0)
325 ? 0
326 : handleRect.Height())));
327 HandleSelectionChange(textSelector_.baseOffset, end);
328 }
329 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
330
331 CHECK_NULL_VOID(selectOverlayProxy_);
332 auto start = textSelector_.GetTextStart();
333 auto end = textSelector_.GetTextEnd();
334 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
335 }
336
OnHandleMoveDone(const RectF & handleRect,bool isFirstHandle)337 void TextPattern::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
338 {
339 textResponseType_ = TextResponseType::LONG_PRESS;
340 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
341 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
342 CalculateHandleOffsetAndShowOverlay();
343 if (selectOverlayProxy_) {
344 SelectHandleInfo handleInfo;
345 if (isFirstHandle) {
346 handleInfo.paintRect = textSelector_.firstHandle;
347 CheckHandles(handleInfo);
348 selectOverlayProxy_->UpdateFirstSelectHandleInfo(handleInfo);
349 } else {
350 handleInfo.paintRect = textSelector_.secondHandle;
351 CheckHandles(handleInfo);
352 selectOverlayProxy_->UpdateSecondSelectHandleInfo(handleInfo);
353 }
354 if (IsSelectAll() && selectMenuInfo_.showCopyAll == true) {
355 selectMenuInfo_.showCopyAll = false;
356 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
357 } else if (!IsSelectAll() && selectMenuInfo_.showCopyAll == false) {
358 selectMenuInfo_.showCopyAll = true;
359 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
360 }
361 if (!selectOverlayProxy_->IsClosed() && selectedType_.has_value() &&
362 oldSelectedType_ != selectedType_.value()) {
363 oldSelectedType_ = selectedType_.value();
364 CloseSelectOverlay();
365 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
366 }
367 if (!selectOverlayProxy_->IsMenuShow()) {
368 selectOverlayProxy_->ShowOrHiddenMenu(false);
369 }
370 return;
371 }
372 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
373 auto host = GetHost();
374 CHECK_NULL_VOID(host);
375 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
376 }
377
IsSelectAll()378 bool TextPattern::IsSelectAll()
379 {
380 return textSelector_.GetTextStart() == 0 &&
381 textSelector_.GetTextEnd() == static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
382 }
GetWideText() const383 std::wstring TextPattern::GetWideText() const
384 {
385 return StringUtils::ToWstring(textForDisplay_);
386 }
387
GetSelectedText(int32_t start,int32_t end) const388 std::string TextPattern::GetSelectedText(int32_t start, int32_t end) const
389 {
390 if (spans_.empty()) {
391 auto wideText = GetWideText();
392 auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(wideText.length()));
393 auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(wideText.length())), 0,
394 static_cast<int32_t>(wideText.length()));
395 return StringUtils::ToString(wideText.substr(min, max - min));
396 }
397 std::string value;
398 int32_t tag = 0;
399 for (const auto& span : spans_) {
400 if (span->GetSymbolUnicode() != 0) {
401 tag = span->position == -1 ? tag + 1 : span->position;
402 continue;
403 }
404 if (span->position - 1 >= start && span->placeholderIndex == -1 && span->position != -1) {
405 auto wideString = StringUtils::ToWstring(span->GetSpanContent());
406 auto max = std::min(span->position, end);
407 auto min = std::max(start, tag);
408 value += StringUtils::ToString(
409 wideString.substr(std::clamp((min - tag), 0, static_cast<int32_t>(wideString.length())),
410 std::clamp((max - min), 0, static_cast<int32_t>(wideString.length()))));
411 }
412 tag = span->position == -1 ? tag + 1 : span->position;
413 if (span->position >= end) {
414 break;
415 }
416 }
417 return value;
418 }
419
HandleOnCopy()420 void TextPattern::HandleOnCopy()
421 {
422 CHECK_NULL_VOID(clipboard_);
423 if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
424 HandleSelectionChange(-1, -1);
425 return;
426 }
427 auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
428 if (value.empty()) {
429 HandleSelectionChange(-1, -1);
430 return;
431 }
432 if (copyOption_ != CopyOptions::None) {
433 clipboard_->SetData(value, copyOption_);
434 }
435 ResetSelection();
436 CloseSelectOverlay(true);
437 auto host = GetHost();
438 CHECK_NULL_VOID(host);
439 auto eventHub = host->GetEventHub<TextEventHub>();
440 CHECK_NULL_VOID(eventHub);
441 eventHub->FireOnCopy(value);
442 }
443
SetTextSelection(int32_t selectionStart,int32_t selectionEnd)444 void TextPattern::SetTextSelection(int32_t selectionStart, int32_t selectionEnd)
445 {
446 auto host = GetHost();
447 CHECK_NULL_VOID(host);
448 auto eventHub = host->GetEventHub<EventHub>();
449 CHECK_NULL_VOID(eventHub);
450 auto context = PipelineContext::GetCurrentContext();
451 if (context) {
452 context->AddAfterLayoutTask([weak = WeakClaim(this), selectionStart, selectionEnd, eventHub]() {
453 auto textPattern = weak.Upgrade();
454 CHECK_NULL_VOID(textPattern);
455 auto renderContext = textPattern->GetRenderContext();
456 CHECK_NULL_VOID(renderContext);
457 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
458 bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
459 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
460 auto textLayoutProperty = textPattern->GetLayoutProperty<TextLayoutProperty>();
461 CHECK_NULL_VOID(textLayoutProperty);
462 if (textLayoutProperty->GetCalcLayoutConstraint() &&
463 textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize.has_value()) {
464 auto selfIdealSizeWidth = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Width();
465 auto selfIdealSizeHeight = textLayoutProperty->GetCalcLayoutConstraint()->selfIdealSize->Height();
466 auto constraint = textLayoutProperty->GetLayoutConstraint();
467 if ((selfIdealSizeWidth.has_value() && NearZero(selfIdealSizeWidth->GetDimension().ConvertToPxWithSize(
468 constraint->percentReference.Width()))) ||
469 (selfIdealSizeHeight.has_value() &&
470 NearZero(selfIdealSizeHeight->GetDimension().ConvertToPxWithSize(
471 constraint->percentReference.Height())))) {
472 return;
473 }
474 }
475 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) == CopyOptions::None ||
476 textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
477 return;
478 }
479 if ((!textPattern->GetAllChildren().empty() || !ifHaveObscured) && eventHub->IsEnabled()) {
480 textPattern->ActSetSelection(selectionStart, selectionEnd);
481 }
482 });
483 }
484 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
485 }
486
GetRenderContext()487 RefPtr<RenderContext> TextPattern::GetRenderContext()
488 {
489 auto frameNode = GetHost();
490 CHECK_NULL_RETURN(frameNode, nullptr);
491 return frameNode->GetRenderContext();
492 }
493
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle)494 void TextPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle)
495 {
496 ShowSelectOverlay(firstHandle, secondHandle, false);
497 }
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle,bool animation,bool isUsingMouse,bool isShowMenu)498 void TextPattern::ShowSelectOverlay(
499 const RectF& firstHandle, const RectF& secondHandle, bool animation, bool isUsingMouse, bool isShowMenu)
500 {
501 SelectOverlayInfo selectInfo;
502 selectInfo.firstHandle.paintRect = firstHandle;
503 selectInfo.secondHandle.paintRect = secondHandle;
504 selectInfo.onHandleMove = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
505 auto pattern = weak.Upgrade();
506 CHECK_NULL_VOID(pattern);
507 pattern->OnHandleMove(handleRect, isFirst);
508 };
509 selectInfo.onHandleMoveDone = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
510 auto pattern = weak.Upgrade();
511 CHECK_NULL_VOID(pattern);
512 pattern->OnHandleMoveDone(handleRect, isFirst);
513 };
514 selectInfo.rightClickOffset = GetRightClickOffset();
515 selectInfo.isUsingMouse = isUsingMouse;
516 selectInfo.menuInfo.menuIsShow = isShowMenu && (selectInfo.firstHandle.isShow || selectInfo.secondHandle.isShow);
517 selectInfo.menuInfo.showCut = false;
518 selectInfo.menuInfo.showCopy = textSelector_.IsValid() && !textSelector_.StartEqualToDest();
519 selectInfo.menuInfo.showPaste = false;
520 selectInfo.menuInfo.showCopyAll = !IsSelectAll();
521 selectInfo.menuCallback.onCopy = [weak = WeakClaim(this)]() {
522 auto pattern = weak.Upgrade();
523 CHECK_NULL_VOID(pattern);
524 pattern->HandleOnCopy();
525 pattern->RemoveAreaChangeInner();
526 };
527 selectInfo.menuCallback.onSelectAll = [weak = WeakClaim(this)]() {
528 auto pattern = weak.Upgrade();
529 CHECK_NULL_VOID(pattern);
530 pattern->HandleOnSelectAll();
531 };
532 selectInfo.onClose = [weak = WeakClaim(this), isUsingMouse](bool closedByGlobalEvent) {
533 auto pattern = weak.Upgrade();
534 CHECK_NULL_VOID(pattern);
535 if (closedByGlobalEvent && !isUsingMouse) {
536 pattern->ResetSelection();
537 pattern->RemoveAreaChangeInner();
538 }
539 };
540
541 if (!menuOptionItems_.empty()) {
542 selectInfo.menuOptionItems = GetMenuOptionItems();
543 }
544 selectMenuInfo_ = selectInfo.menuInfo;
545 CopySelectionMenuParams(selectInfo, textResponseType_.value_or(TextResponseType::NONE));
546 UpdateSelectOverlayOrCreate(selectInfo, animation);
547 }
548
HandleOnSelectAll()549 void TextPattern::HandleOnSelectAll()
550 {
551 auto textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
552 HandleSelectionChange(0, textSize);
553 auto pipeline = PipelineContext::GetCurrentContext();
554 CHECK_NULL_VOID(pipeline);
555 auto selectOverlayInfo = pipeline->GetSelectOverlayManager()->GetSelectOverlayInfo();
556 CalculateHandleOffsetAndShowOverlay();
557 CloseSelectOverlay(true);
558 if (!selectOverlayInfo.isUsingMouse) {
559 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
560 } else {
561 if (IsSelected()) {
562 PushSelectedByMouseInfoToManager();
563 }
564 }
565 selectMenuInfo_.showCopyAll = false;
566 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
567 auto host = GetHost();
568 CHECK_NULL_VOID(host);
569 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
570 }
571
CheckHandles(SelectHandleInfo & handleInfo)572 void TextPattern::CheckHandles(SelectHandleInfo& handleInfo)
573 {
574 auto host = GetHost();
575 CHECK_NULL_VOID(host);
576 auto renderContext = host->GetRenderContext();
577 CHECK_NULL_VOID(renderContext);
578 if (!renderContext->GetClipEdge().value_or(true)) {
579 return;
580 }
581 // use global offset.
582 RectF visibleContentRect(contentRect_.GetOffset() + parentGlobalOffset_, contentRect_.GetSize());
583 auto parent = host->GetAncestorNodeOfFrame();
584 visibleContentRect = GetVisibleContentRect(parent, visibleContentRect);
585 auto paintRect = handleInfo.paintRect;
586 PointF bottomPoint = { paintRect.Left(), paintRect.Bottom() - BOX_EPSILON };
587 PointF topPoint = { paintRect.Left(), paintRect.Top() + BOX_EPSILON };
588 handleInfo.isShow = visibleContentRect.IsInRegion(bottomPoint) && visibleContentRect.IsInRegion(topPoint);
589 }
590
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)591 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
592 {
593 constexpr int32_t longPressDelay = 600;
594 if (longPressEvent_) {
595 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
596 return;
597 }
598 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
599 auto pattern = weak.Upgrade();
600 CHECK_NULL_VOID(pattern);
601 pattern->HandleLongPress(info);
602 };
603 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
604
605 // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
606 // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
607 // be slightly longer to ensure that order.
608 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
609
610 auto onTextSelectorChange = [weak = WeakClaim(this)]() {
611 auto pattern = weak.Upgrade();
612 CHECK_NULL_VOID(pattern);
613 auto frameNode = pattern->GetHost();
614 CHECK_NULL_VOID(frameNode);
615 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
616 };
617 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
618 }
619
OnHandleTouchUp()620 void TextPattern::OnHandleTouchUp()
621 {
622 CloseSelectOverlay();
623 ResetSelection();
624 }
625
HandleClickEvent(GestureEvent & info)626 void TextPattern::HandleClickEvent(GestureEvent& info)
627 {
628 if (dataDetectorAdapter_->hasClickedAISpan_) {
629 dataDetectorAdapter_->hasClickedAISpan_ = false;
630 } else if (hasClicked_) {
631 hasClicked_ = false;
632 TimeStamp clickTimeStamp = info.GetTimeStamp();
633 std::chrono::duration<float, std::ratio<1, SECONDS_TO_MILLISECONDS>> timeout =
634 clickTimeStamp - lastClickTimeStamp_;
635 lastClickTimeStamp_ = info.GetTimeStamp();
636 if (timeout.count() < DOUBLECLICK_INTERVAL_MS) {
637 HandleDoubleClickEvent(info);
638 return;
639 }
640 }
641 HandleSingleClickEvent(info);
642 }
643
HandleSingleClickEvent(GestureEvent & info)644 void TextPattern::HandleSingleClickEvent(GestureEvent& info)
645 {
646 hasClicked_ = true;
647 lastClickTimeStamp_ = info.GetTimeStamp();
648
649 RectF textContentRect = contentRect_;
650 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
651 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
652 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
653 info.GetLocalLocation().GetY() - textContentRect.GetY() };
654 HandleClickAISpanEvent(textOffset);
655 if (dataDetectorAdapter_->hasClickedAISpan_) {
656 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
657 selectOverlayProxy_->DisableMenu(true);
658 }
659 return;
660 }
661
662 if (textSelector_.IsValid() && mouseStatus_ != MouseStatus::MOVE) {
663 CloseSelectOverlay(true);
664 ResetSelection();
665 }
666 bool isClickOnSpan = false;
667 HandleSpanSingleClickEvent(info, textContentRect, textOffset, isClickOnSpan);
668
669 if (onClick_ && !isClickOnSpan) {
670 auto onClick = onClick_;
671 onClick(info);
672 if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
673 auto host = GetHost();
674 CHECK_NULL_VOID(host);
675 auto text = host->GetAccessibilityProperty<NG::AccessibilityProperty>()->GetText();
676 Recorder::EventParamsBuilder builder;
677 builder.SetId(host->GetInspectorIdValue(""))
678 .SetType(host->GetTag())
679 .SetText(text)
680 .SetDescription(host->GetAutoEventParamValue(""));
681 Recorder::EventRecorder::Get().OnClick(std::move(builder));
682 }
683 }
684 }
685
HandleClickAISpanEvent(const PointF & textOffset)686 void TextPattern::HandleClickAISpanEvent(const PointF& textOffset)
687 {
688 dataDetectorAdapter_->hasClickedAISpan_ = false;
689 if (!NeedShowAIDetect()) {
690 return;
691 }
692
693 for (const auto& kv : dataDetectorAdapter_->aiSpanMap_) {
694 auto& aiSpan = kv.second;
695 ClickAISpan(textOffset, aiSpan);
696 if (dataDetectorAdapter_->hasClickedAISpan_) {
697 return;
698 }
699 }
700 }
701
HandleSpanSingleClickEvent(GestureEvent & info,RectF textContentRect,PointF textOffset,bool & isClickOnSpan)702 void TextPattern::HandleSpanSingleClickEvent(
703 GestureEvent& info, RectF textContentRect, PointF textOffset, bool& isClickOnSpan)
704 {
705 auto host = GetHost();
706 CHECK_NULL_VOID(host);
707 auto renderContext = host->GetRenderContext();
708 CHECK_NULL_VOID(renderContext);
709 if (renderContext->GetClipEdge().has_value() && !renderContext->GetClipEdge().value() && overlayMod_) {
710 textContentRect = overlayMod_->GetBoundsRect();
711 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
712 }
713 if (textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) &&
714 !spans_.empty() && paragraph_) {
715 int32_t start = 0;
716 for (const auto& item : spans_) {
717 if (!item) {
718 continue;
719 }
720 std::vector<RectF> selectedRects;
721 paragraph_->GetRectsForRange(start, item->position, selectedRects);
722 start = item->position;
723 for (auto&& rect : selectedRects) {
724 if (rect.IsInRegion(textOffset)) {
725 if (!item->onClick) {
726 break;
727 }
728 GestureEvent spanClickinfo = info;
729 EventTarget target = info.GetTarget();
730 target.area.SetWidth(Dimension(0.0f));
731 target.area.SetHeight(Dimension(0.0f));
732 spanClickinfo.SetTarget(target);
733 item->onClick(spanClickinfo);
734 if (Recorder::EventRecorder::Get().IsComponentRecordEnable()) {
735 Recorder::EventParamsBuilder builder;
736 builder.SetId(item->inspectId).SetText(item->content).SetDescription(item->description);
737 Recorder::EventRecorder::Get().OnClick(std::move(builder));
738 }
739 isClickOnSpan = true;
740 return;
741 }
742 }
743 }
744 }
745 }
746
ClickAISpan(const PointF & textOffset,const AISpan & aiSpan)747 bool TextPattern::ClickAISpan(const PointF& textOffset, const AISpan& aiSpan)
748 {
749 std::vector<RectF> aiRects;
750 paragraph_->GetRectsForRange(aiSpan.start, aiSpan.end, aiRects);
751 for (auto&& rect : aiRects) {
752 if (rect.IsInRegion(textOffset)) {
753 dataDetectorAdapter_->hasClickedAISpan_ = true;
754 if (isMousePressed_) {
755 dataDetectorAdapter_->ResponseBestMatchItem(aiSpan);
756 return true;
757 }
758 return ShowUIExtensionMenu(aiSpan);
759 }
760 }
761 return false;
762 }
763
SetOnClickMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)764 void TextPattern::SetOnClickMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
765 const ShowSelectOverlayFunc& showSelectOverlayFunc)
766
767 {
768 dataDetectorAdapter_->onClickMenu_ = [aiSpan, weak = WeakClaim(this), calculateHandleFunc, showSelectOverlayFunc](
769 const std::string& action) {
770 auto pattern = weak.Upgrade();
771 CHECK_NULL_VOID(pattern);
772 if (pattern->copyOption_ == CopyOptions::None) {
773 return;
774 }
775 pattern->CloseSelectOverlay();
776 if (action == std::string(COPY_ACTION)) {
777 pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
778 pattern->HandleOnCopy();
779 } else if (action == std::string(SELECT_ACTION)) {
780 pattern->HandleSelectionChange(aiSpan.start, aiSpan.end);
781 if (calculateHandleFunc == nullptr) {
782 pattern->CalculateHandleOffsetAndShowOverlay();
783 } else {
784 calculateHandleFunc();
785 }
786 if (showSelectOverlayFunc == nullptr) {
787 pattern->ShowSelectOverlay(
788 pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle, true);
789 } else {
790 showSelectOverlayFunc(pattern->textSelector_.firstHandle, pattern->textSelector_.secondHandle);
791 }
792 auto frameNode = pattern->GetHost();
793 CHECK_NULL_VOID(frameNode);
794 frameNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
795 }
796 };
797 }
798
ShowUIExtensionMenu(const AISpan & aiSpan,const CalculateHandleFunc & calculateHandleFunc,const ShowSelectOverlayFunc & showSelectOverlayFunc)799 bool TextPattern::ShowUIExtensionMenu(const AISpan& aiSpan, const CalculateHandleFunc& calculateHandleFunc,
800 const ShowSelectOverlayFunc& showSelectOverlayFunc)
801 {
802 auto host = GetHost();
803 CHECK_NULL_RETURN(host, false);
804 SetOnClickMenu(aiSpan, calculateHandleFunc, showSelectOverlayFunc);
805 auto baseOffset = textSelector_.baseOffset;
806 auto destinationOffset = textSelector_.destinationOffset;
807 HandleSelectionChange(aiSpan.start, aiSpan.end);
808 if (calculateHandleFunc == nullptr) {
809 CalculateHandleOffsetAndShowOverlay();
810 } else {
811 calculateHandleFunc();
812 }
813 HandleSelectionChange(baseOffset, destinationOffset);
814 RectF aiRect;
815 if (textSelector_.firstHandle.Top() != textSelector_.secondHandle.Top()) {
816 auto top = std::min(textSelector_.firstHandle.Top(), textSelector_.secondHandle.Top());
817 auto bottom = std::max(textSelector_.firstHandle.Bottom(), textSelector_.secondHandle.Bottom());
818 auto paintRect = host->GetPaintRectWithTransform();
819 auto left = paintRect.Left();
820 auto right = paintRect.Right();
821 aiRect = RectT(left, top, right - left, bottom - top);
822 } else {
823 aiRect = textSelector_.firstHandle.CombineRectT(textSelector_.secondHandle);
824 }
825
826 return dataDetectorAdapter_->ShowUIExtensionMenu(aiSpan, aiRect, host);
827 }
828
HandleDoubleClickEvent(GestureEvent & info)829 void TextPattern::HandleDoubleClickEvent(GestureEvent& info)
830 {
831 CheckOnClickEvent(info);
832 if (copyOption_ == CopyOptions::None || textForDisplay_.empty()) {
833 return;
834 }
835 auto host = GetHost();
836 CHECK_NULL_VOID(host);
837 auto hub = host->GetEventHub<EventHub>();
838 CHECK_NULL_VOID(hub);
839 auto gestureHub = hub->GetOrCreateGestureEventHub();
840 CHECK_NULL_VOID(gestureHub);
841 isDoubleClick_ = true;
842 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
843 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
844 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
845 InitSelection(textOffset);
846 textResponseType_ = TextResponseType::NONE;
847 UpdateSelectionSpanType(std::min(textSelector_.baseOffset, textSelector_.destinationOffset),
848 std::max(textSelector_.baseOffset, textSelector_.destinationOffset));
849 CalculateHandleOffsetAndShowOverlay();
850 if (!isMousePressed_) {
851 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
852 }
853 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
854 }
855
CheckOnClickEvent(GestureEvent & info)856 void TextPattern::CheckOnClickEvent(GestureEvent& info)
857 {
858 bool isClickOnSpan = false;
859 RectF textContentRect = contentRect_;
860 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
861 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
862 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
863 info.GetLocalLocation().GetY() - textContentRect.GetY() };
864 HandleSpanSingleClickEvent(info, textContentRect, textOffset, isClickOnSpan);
865 if (onClick_ && !isClickOnSpan) {
866 auto onClick = onClick_;
867 onClick(info);
868 }
869 }
870
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)871 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
872 {
873 CHECK_NULL_VOID(!clickEventInitialized_);
874 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
875 auto pattern = weak.Upgrade();
876 CHECK_NULL_VOID(pattern);
877 pattern->HandleClickEvent(info);
878 };
879 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
880 gestureHub->AddClickEvent(clickListener);
881 clickEventInitialized_ = true;
882 }
883
InitMouseEvent()884 void TextPattern::InitMouseEvent()
885 {
886 CHECK_NULL_VOID(!mouseEventInitialized_);
887 auto host = GetHost();
888 CHECK_NULL_VOID(host);
889 auto eventHub = host->GetEventHub<EventHub>();
890 CHECK_NULL_VOID(eventHub);
891 auto inputHub = eventHub->GetOrCreateInputEventHub();
892 CHECK_NULL_VOID(inputHub);
893
894 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
895 auto pattern = weak.Upgrade();
896 CHECK_NULL_VOID(pattern);
897 pattern->HandleMouseEvent(info);
898 };
899 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
900 inputHub->AddOnMouseEvent(mouseEvent);
901 mouseEventInitialized_ = true;
902 }
903
HandleMouseEvent(const MouseInfo & info)904 void TextPattern::HandleMouseEvent(const MouseInfo& info)
905 {
906 if (copyOption_ == CopyOptions::None) {
907 return;
908 }
909
910 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
911 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
912 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
913 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
914 HandleMouseLeftButton(info, textOffset);
915 if (IsSelected()) {
916 PushSelectedByMouseInfoToManager();
917 }
918 } else if (info.GetButton() == MouseButton::RIGHT_BUTTON) {
919 HandleMouseRightButton(info, textOffset);
920 }
921 }
922
PushSelectedByMouseInfoToManager()923 void TextPattern::PushSelectedByMouseInfoToManager()
924 {
925 SelectedByMouseInfo selectedByMouseInfo;
926 selectedByMouseInfo.selectedNode = GetHost();
927 selectedByMouseInfo.onResetSelection = [weak = WeakClaim(this)]() {
928 auto pattern = weak.Upgrade();
929 CHECK_NULL_VOID(pattern);
930 pattern->ResetSelection();
931 };
932 SetSelectionNode(selectedByMouseInfo);
933 }
934
HandleMouseLeftButton(const MouseInfo & info,const Offset & textOffset)935 void TextPattern::HandleMouseLeftButton(const MouseInfo& info, const Offset& textOffset)
936 {
937 if (info.GetAction() == MouseAction::PRESS) {
938 HandleMouseLeftPressAction(info, textOffset);
939 } else if (info.GetAction() == MouseAction::MOVE) {
940 HandleMouseLeftMoveAction(info, textOffset);
941 } else if (info.GetAction() == MouseAction::RELEASE) {
942 HandleMouseLeftReleaseAction(info, textOffset);
943 }
944
945 auto host = GetHost();
946 CHECK_NULL_VOID(host);
947 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
948 }
949
HandleMouseLeftPressAction(const MouseInfo & info,const Offset & textOffset)950 void TextPattern::HandleMouseLeftPressAction(const MouseInfo& info, const Offset& textOffset)
951 {
952 isMousePressed_ = true;
953 if (BetweenSelectedPosition(info.GetGlobalLocation())) {
954 blockPress_ = true;
955 return;
956 }
957 mouseStatus_ = MouseStatus::PRESSED;
958 CHECK_NULL_VOID(paragraph_);
959 auto start = paragraph_->GetGlyphIndexByCoordinate(textOffset);
960 textSelector_.Update(start, start);
961 }
962
HandleMouseLeftReleaseAction(const MouseInfo & info,const Offset & textOffset)963 void TextPattern::HandleMouseLeftReleaseAction(const MouseInfo& info, const Offset& textOffset)
964 {
965 if (blockPress_) {
966 blockPress_ = false;
967 }
968 auto oldMouseStatus = mouseStatus_;
969 mouseStatus_ = MouseStatus::RELEASED;
970 if (isDoubleClick_) {
971 isDoubleClick_ = false;
972 isMousePressed_ = false;
973 return;
974 }
975
976 CHECK_NULL_VOID(paragraph_);
977 auto start = textSelector_.baseOffset;
978 auto end = textSelector_.destinationOffset;
979 if (!IsSelected()) {
980 start = -1;
981 end = -1;
982 }
983 if (isMousePressed_ || oldMouseStatus == MouseStatus::MOVE) {
984 HandleSelectionChange(start, end);
985 }
986
987 if (IsSelected() && oldMouseStatus == MouseStatus::MOVE && IsSelectedBindSelectionMenu()) {
988 mouseReleaseOffset_ = OffsetF(
989 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
990 textResponseType_ = TextResponseType::SELECTED_BY_MOUSE;
991 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true, true);
992 }
993 isMousePressed_ = false;
994 }
995
HandleMouseLeftMoveAction(const MouseInfo & info,const Offset & textOffset)996 void TextPattern::HandleMouseLeftMoveAction(const MouseInfo& info, const Offset& textOffset)
997 {
998 if (blockPress_) {
999 dragBoxes_ = GetTextBoxes();
1000 return;
1001 }
1002 if (isMousePressed_) {
1003 mouseStatus_ = MouseStatus::MOVE;
1004 CHECK_NULL_VOID(paragraph_);
1005 auto end = paragraph_->GetGlyphIndexByCoordinate(textOffset);
1006 HandleSelectionChange(textSelector_.baseOffset, end);
1007 }
1008 }
1009
HandleMouseRightButton(const MouseInfo & info,const Offset & textOffset)1010 void TextPattern::HandleMouseRightButton(const MouseInfo& info, const Offset& textOffset)
1011 {
1012 if (info.GetAction() == MouseAction::RELEASE) {
1013 HandleClickAISpanEvent(PointF(textOffset.GetX(), textOffset.GetY()));
1014 if (dataDetectorAdapter_->hasClickedAISpan_) {
1015 return;
1016 }
1017
1018 mouseReleaseOffset_ = OffsetF(
1019 static_cast<float>(info.GetGlobalLocation().GetX()), static_cast<float>(info.GetGlobalLocation().GetY()));
1020 CalculateHandleOffsetAndShowOverlay(true);
1021 if (SelectOverlayIsOn()) {
1022 CloseSelectOverlay(true);
1023 }
1024 textResponseType_ = TextResponseType::RIGHT_CLICK;
1025 if (!IsSelected()) {
1026 auto spanNode = DynamicCast<FrameNode>(GetChildByIndex(GetSelectionSpanItemIndex(info)));
1027 if (spanNode && spanNode->GetTag() == V2::IMAGE_ETS_TAG) {
1028 selectedType_ = TextSpanType::IMAGE;
1029 } else {
1030 selectedType_ = TextSpanType::TEXT;
1031 }
1032 }
1033 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true, true);
1034 auto host = GetHost();
1035 CHECK_NULL_VOID(host);
1036 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1037 } else if (info.GetAction() == MouseAction::PRESS) {
1038 CloseSelectOverlay(true);
1039 }
1040 }
1041
InitTouchEvent()1042 void TextPattern::InitTouchEvent()
1043 {
1044 CHECK_NULL_VOID(!touchEventInitialized_);
1045 auto host = GetHost();
1046 CHECK_NULL_VOID(host);
1047 auto gesture = host->GetOrCreateGestureEventHub();
1048 CHECK_NULL_VOID(gesture);
1049
1050 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
1051 auto pattern = weak.Upgrade();
1052 CHECK_NULL_VOID(pattern);
1053 pattern->HandleTouchEvent(info);
1054 };
1055 auto touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
1056 gesture->AddTouchEvent(touchListener_);
1057 touchEventInitialized_ = true;
1058 }
1059
HandleTouchEvent(const TouchEventInfo & info)1060 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
1061 {
1062 return;
1063 }
1064
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)1065 void TextPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
1066 {
1067 CHECK_NULL_VOID(!panEventInitialized_);
1068 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1069 auto pattern = weak.Upgrade();
1070 CHECK_NULL_VOID(pattern);
1071 pattern->HandlePanStart(info);
1072 };
1073 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1074 auto pattern = weak.Upgrade();
1075 CHECK_NULL_VOID(pattern);
1076 pattern->HandlePanUpdate(info);
1077 };
1078 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
1079 auto pattern = weak.Upgrade();
1080 CHECK_NULL_VOID(pattern);
1081 pattern->HandlePanEnd(info);
1082 };
1083 auto actionCancelTask = [weak = WeakClaim(this)]() {};
1084 auto panEvent = MakeRefPtr<PanEvent>(
1085 std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
1086
1087 PanDirection panDirection;
1088 panDirection.type = PanDirection::ALL;
1089 gestureHub->AddPanEvent(panEvent, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
1090 panEventInitialized_ = true;
1091 }
1092
HandlePanStart(const GestureEvent & info)1093 void TextPattern::HandlePanStart(const GestureEvent& info)
1094 {
1095 auto host = GetHost();
1096 CHECK_NULL_VOID(host);
1097 auto offset = info.GetLocalLocation();
1098 if (!IsDraggable(offset)) {
1099 return;
1100 }
1101 auto pipelineContext = PipelineContext::GetCurrentContext();
1102 CHECK_NULL_VOID(pipelineContext);
1103
1104 #if !defined(PREVIEW)
1105 if (!dragWindow_) {
1106 auto rect = pipelineContext->GetCurrentWindowRect();
1107 auto initTextPattern = AceType::Claim(this);
1108
1109 // create textdrag window
1110 dragWindow_ = DragWindow::CreateTextDragWindow("APP_DRAG_WINDOW",
1111 static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
1112 static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()),
1113 static_cast<int32_t>(contentRect_.Width() + contentRect_.GetX()),
1114 contentRect_.Height() + contentRect_.GetY());
1115 if (dragWindow_) {
1116 dragWindow_->SetOffset(static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
1117 static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()));
1118 // draw select text on drag window
1119 dragWindow_->DrawTextNG(paragraph_, initTextPattern);
1120 // add select data to clipboard
1121 auto manager = pipelineContext->GetDragDropManager();
1122 CHECK_NULL_VOID(manager);
1123 dragDropProxy_ = manager->CreateTextDragDropProxy();
1124 CHECK_NULL_VOID(dragDropProxy_);
1125 dragDropProxy_->OnTextDragStart(GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd()));
1126 }
1127 }
1128 #endif
1129 }
1130
IsDraggable(const Offset & offset)1131 bool TextPattern::IsDraggable(const Offset& offset)
1132 {
1133 auto host = GetHost();
1134 CHECK_NULL_RETURN(host, false);
1135 auto eventHub = host->GetEventHub<EventHub>();
1136 bool draggable = eventHub->HasOnDragStart();
1137 if (copyOption_ != CopyOptions::None && draggable &&
1138 GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
1139 // Determine if the pan location is in the selected area
1140 std::vector<RectF> selectedRects;
1141 paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
1142 auto panOffset = OffsetF(offset.GetX(), offset.GetY()) - contentRect_.GetOffset() +
1143 OffsetF(0.0f, std::min(baselineOffset_, 0.0f));
1144 for (const auto& selectedRect : selectedRects) {
1145 if (selectedRect.IsInRegion(PointF(panOffset.GetX(), panOffset.GetY()))) {
1146 return true;
1147 }
1148 }
1149 }
1150 return false;
1151 }
1152
HandlePanUpdate(const GestureEvent & info)1153 void TextPattern::HandlePanUpdate(const GestureEvent& info)
1154 {
1155 if (dragWindow_) {
1156 dragWindow_->TextDragWindowMove(info.GetOffsetX(), info.GetOffsetY());
1157 }
1158 return;
1159 }
1160
HandlePanEnd(const GestureEvent & info)1161 void TextPattern::HandlePanEnd(const GestureEvent& info)
1162 {
1163 if (dragWindow_) {
1164 dragWindow_->Destroy();
1165 dragWindow_ = nullptr;
1166 if (dragDropProxy_) {
1167 dragDropProxy_->OnDragEnd(info, true);
1168 }
1169 return;
1170 }
1171 }
1172
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)1173 NG::DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
1174 {
1175 DragDropInfo itemInfo;
1176 auto host = GetHost();
1177 CHECK_NULL_RETURN(host, itemInfo);
1178 auto hub = host->GetEventHub<EventHub>();
1179 auto gestureHub = hub->GetOrCreateGestureEventHub();
1180 auto selectStart = textSelector_.GetTextStart();
1181 auto selectEnd = textSelector_.GetTextEnd();
1182 recoverStart_ = selectStart;
1183 recoverEnd_ = selectEnd;
1184 auto textSelectInfo = GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT);
1185 dragResultObjects_ = textSelectInfo.GetSelection().resultObjects;
1186 ResetDragRecordSize(dragResultObjects_.empty() ? -1 : 1);
1187 status_ = Status::DRAGGING;
1188 if (dragResultObjects_.empty() || !gestureHub->GetIsTextDraggable()) {
1189 return itemInfo;
1190 }
1191 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
1192 auto resultProcessor = [unifiedData, weak = WeakClaim(this)](const ResultObject& result) {
1193 auto pattern = weak.Upgrade();
1194 CHECK_NULL_VOID(pattern);
1195 if (result.type == SelectSpanType::TYPESPAN) {
1196 auto data = pattern->GetSelectedSpanText(StringUtils::ToWstring(result.valueString),
1197 result.offsetInSpan[RichEditorSpanRange::RANGESTART],
1198 result.offsetInSpan[RichEditorSpanRange::RANGEEND]);
1199 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, data);
1200 return;
1201 }
1202 if (result.type == SelectSpanType::TYPEIMAGE) {
1203 if (result.valuePixelMap) {
1204 const uint8_t* pixels = result.valuePixelMap->GetPixels();
1205 CHECK_NULL_VOID(pixels);
1206 int32_t length = result.valuePixelMap->GetByteCount();
1207 std::vector<uint8_t> data(pixels, pixels + length);
1208 PixelMapRecordDetails details = { result.valuePixelMap->GetWidth(), result.valuePixelMap->GetHeight(),
1209 result.valuePixelMap->GetPixelFormat(), result.valuePixelMap->GetAlphaType() };
1210 UdmfClient::GetInstance()->AddPixelMapRecord(unifiedData, data, details);
1211 } else {
1212 UdmfClient::GetInstance()->AddImageRecord(unifiedData, result.valueString);
1213 }
1214 }
1215 };
1216 for (const auto& resultObj : dragResultObjects_) {
1217 resultProcessor(resultObj);
1218 }
1219 event->SetData(unifiedData);
1220 CloseOperate();
1221 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1222 return itemInfo;
1223 }
1224
CloseOperate()1225 void TextPattern::CloseOperate()
1226 {
1227 UpdateSpanItemDragStatus(dragResultObjects_, true);
1228 recoverDragResultObjects_ = dragResultObjects_;
1229 AceEngineExt::GetInstance().DragStartExt();
1230 CloseKeyboard(true);
1231 CloseSelectOverlay();
1232 ResetSelection();
1233 }
1234
OnDragStartNoChild(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)1235 DragDropInfo TextPattern::OnDragStartNoChild(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
1236 {
1237 auto weakPtr = WeakClaim(this);
1238 DragDropInfo itemInfo;
1239 auto pattern = weakPtr.Upgrade();
1240 auto host = pattern->GetHost();
1241 auto hub = host->GetEventHub<EventHub>();
1242 auto gestureHub = hub->GetOrCreateGestureEventHub();
1243 CHECK_NULL_RETURN(gestureHub, itemInfo);
1244 if (!gestureHub->GetIsTextDraggable()) {
1245 return itemInfo;
1246 }
1247 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
1248 pattern->status_ = Status::DRAGGING;
1249 pattern->contentMod_->ChangeDragStatus();
1250 pattern->showSelect_ = false;
1251 auto start = textSelector_.GetTextStart();
1252 pattern->recoverStart_ = start;
1253 auto end = textSelector_.GetTextEnd();
1254 pattern->recoverEnd_ = end;
1255 auto beforeStr = GetSelectedText(0, start);
1256 auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
1257 auto afterStr = GetSelectedText(end, GetWideText().length());
1258 pattern->dragContents_ = { beforeStr, selectedStr, afterStr };
1259
1260 itemInfo.extraInfo = selectedStr;
1261 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
1262 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedStr);
1263 event->SetData(unifiedData);
1264 host->MarkDirtyNode(layoutProperty->GetMaxLinesValue(Infinity<float>()) <= 1 ? PROPERTY_UPDATE_MEASURE_SELF
1265 : PROPERTY_UPDATE_MEASURE);
1266
1267 CloseSelectOverlay();
1268 ResetSelection();
1269 return itemInfo;
1270 }
1271
UpdateSpanItemDragStatus(const std::list<ResultObject> & resultObjects,bool isDragging)1272 void TextPattern::UpdateSpanItemDragStatus(const std::list<ResultObject>& resultObjects, bool isDragging)
1273 {
1274 if (resultObjects.empty()) {
1275 return;
1276 }
1277 auto dragStatusUpdateAction = [weakPtr = WeakClaim(this), isDragging](const ResultObject& resultObj) {
1278 auto pattern = weakPtr.Upgrade();
1279 CHECK_NULL_VOID(pattern);
1280 if (pattern->spans_.empty()) {
1281 return;
1282 }
1283 auto it = pattern->spans_.begin();
1284 if (resultObj.spanPosition.spanIndex >= static_cast<int32_t>(pattern->spans_.size())) {
1285 std::advance(it, pattern->spans_.size() - 1);
1286 TAG_LOGW(AceLogTag::ACE_RICH_TEXT, "resultObj.spanPosition.spanIndex is larger than spans size.");
1287 } else {
1288 std::advance(it, resultObj.spanPosition.spanIndex);
1289 }
1290 auto spanItem = *it;
1291 CHECK_NULL_VOID(spanItem);
1292 if (resultObj.type == SelectSpanType::TYPESPAN) {
1293 if (isDragging) {
1294 spanItem->StartDrag(resultObj.offsetInSpan[RichEditorSpanRange::RANGESTART],
1295 resultObj.offsetInSpan[RichEditorSpanRange::RANGEEND]);
1296 } else {
1297 spanItem->EndDrag();
1298 }
1299 return;
1300 }
1301
1302 if (resultObj.type == SelectSpanType::TYPEIMAGE) {
1303 auto imageNode = DynamicCast<FrameNode>(pattern->GetChildByIndex(resultObj.spanPosition.spanIndex));
1304 CHECK_NULL_VOID(imageNode);
1305 auto renderContext = imageNode->GetRenderContext();
1306 CHECK_NULL_VOID(renderContext);
1307 renderContext->UpdateOpacity(isDragging ? (double)DRAGGED_TEXT_OPACITY / 255 : 1);
1308 imageNode->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1309 }
1310 };
1311 for (const auto& resultObj : resultObjects) {
1312 dragStatusUpdateAction(resultObj);
1313 }
1314 }
1315
OnDragEnd(const RefPtr<Ace::DragEvent> & event)1316 void TextPattern::OnDragEnd(const RefPtr<Ace::DragEvent>& event)
1317 {
1318 ResetDragRecordSize(-1);
1319 auto wk = WeakClaim(this);
1320 auto pattern = wk.Upgrade();
1321 CHECK_NULL_VOID(pattern);
1322 auto host = GetHost();
1323 CHECK_NULL_VOID(host);
1324 if (status_ == Status::DRAGGING) {
1325 status_ = Status::NONE;
1326 }
1327 if (dragResultObjects_.empty()) {
1328 return;
1329 }
1330 UpdateSpanItemDragStatus(dragResultObjects_, false);
1331 dragResultObjects_.clear();
1332 if (event && event->GetResult() != DragRet::DRAG_SUCCESS) {
1333 HandleSelectionChange(recoverStart_, recoverEnd_);
1334 showSelect_ = true;
1335 isShowMenu_ = false;
1336 CalculateHandleOffsetAndShowOverlay();
1337 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, false, false, false);
1338 }
1339 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1340 }
1341
OnDragEndNoChild(const RefPtr<Ace::DragEvent> & event)1342 void TextPattern::OnDragEndNoChild(const RefPtr<Ace::DragEvent>& event)
1343 {
1344 auto wk = WeakClaim(this);
1345 auto pattern = wk.Upgrade();
1346 CHECK_NULL_VOID(pattern);
1347 auto host = pattern->GetHost();
1348 CHECK_NULL_VOID(host);
1349 if (pattern->status_ == Status::DRAGGING) {
1350 pattern->status_ = Status::NONE;
1351 pattern->MarkContentChange();
1352 pattern->contentMod_->ChangeDragStatus();
1353 if (event && event->GetResult() != DragRet::DRAG_SUCCESS) {
1354 HandleSelectionChange(recoverStart_, recoverEnd_);
1355 pattern->showSelect_ = true;
1356 isShowMenu_ = false;
1357 CalculateHandleOffsetAndShowOverlay();
1358 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, false, false, false);
1359 }
1360 auto layoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
1361 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
1362 }
1363 }
1364
OnDragMove(const RefPtr<Ace::DragEvent> & event)1365 void TextPattern::OnDragMove(const RefPtr<Ace::DragEvent>& event)
1366 {
1367 auto weakPtr = WeakClaim(this);
1368 auto pattern = weakPtr.Upgrade();
1369 if (pattern->status_ == Status::DRAGGING) {
1370 CloseSelectOverlay();
1371 pattern->showSelect_ = false;
1372 }
1373 }
1374
InitDragEvent()1375 void TextPattern::InitDragEvent()
1376 {
1377 auto host = GetHost();
1378 CHECK_NULL_VOID(host);
1379 auto eventHub = host->GetEventHub<EventHub>();
1380 CHECK_NULL_VOID(eventHub);
1381 auto gestureHub = host->GetOrCreateGestureEventHub();
1382 CHECK_NULL_VOID(gestureHub);
1383 gestureHub->InitDragDropEvent();
1384 gestureHub->SetTextDraggable(true);
1385 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
1386 auto onDragStart = [weakPtr = WeakClaim(this)](
1387 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
1388 NG::DragDropInfo itemInfo;
1389 auto pattern = weakPtr.Upgrade();
1390 CHECK_NULL_RETURN(pattern, itemInfo);
1391 auto eventHub = pattern->GetEventHub<EventHub>();
1392 CHECK_NULL_RETURN(eventHub, itemInfo);
1393 if (pattern->spans_.empty()) {
1394 return pattern->OnDragStartNoChild(event, extraParams);
1395 } else {
1396 return pattern->OnDragStart(event, extraParams);
1397 }
1398 };
1399 if (!eventHub->HasOnDragStart()) {
1400 eventHub->SetOnDragStart(std::move(onDragStart));
1401 }
1402 auto onDragMove = [weakPtr = WeakClaim(this)](
1403 const RefPtr<OHOS::Ace::DragEvent>& event, const std::string& extraParams) {
1404 auto pattern = weakPtr.Upgrade();
1405 CHECK_NULL_VOID(pattern);
1406 pattern->OnDragMove(event);
1407 };
1408 eventHub->SetOnDragMove(std::move(onDragMove));
1409 auto onDragEnd = [weakPtr = WeakClaim(this), scopeId = Container::CurrentId()](
1410 const RefPtr<OHOS::Ace::DragEvent>& event) {
1411 ContainerScope scope(scopeId);
1412 auto pattern = weakPtr.Upgrade();
1413 CHECK_NULL_VOID(pattern);
1414 if (pattern->spans_.empty()) {
1415 pattern->OnDragEndNoChild(event);
1416 } else {
1417 pattern->OnDragEnd(event);
1418 }
1419 };
1420 eventHub->SetOnDragEnd(std::move(onDragEnd));
1421 }
1422
GetThumbnailCallback()1423 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
1424 {
1425 return [wk = WeakClaim(this)](const Offset& point) {
1426 auto pattern = wk.Upgrade();
1427 CHECK_NULL_VOID(pattern);
1428 if (pattern->BetweenSelectedPosition(point)) {
1429 auto children = pattern->GetAllChildren();
1430 std::list<RefPtr<FrameNode>> imageChildren;
1431 for (const auto& child : children) {
1432 auto node = DynamicCast<FrameNode>(child);
1433 if (!node) {
1434 continue;
1435 }
1436 auto image = node->GetPattern<ImagePattern>();
1437 if (image) {
1438 imageChildren.emplace_back(node);
1439 }
1440 }
1441 pattern->dragNode_ = RichEditorDragPattern::CreateDragNode(pattern->GetHost(), imageChildren);
1442 FrameNode::ProcessOffscreenNode(pattern->dragNode_);
1443 }
1444 };
1445 }
1446
GetAllChildren() const1447 const std::list<RefPtr<UINode>>& TextPattern::GetAllChildren() const
1448 {
1449 childNodes_.clear();
1450 auto host = GetHost();
1451 CHECK_NULL_RETURN(host, childNodes_);
1452 const auto& children = host->GetChildren();
1453 for (const auto& child : children) {
1454 if (child->GetTag() == V2::CONTAINER_SPAN_ETS_TAG) {
1455 auto spanChildren = child->GetChildren();
1456 childNodes_.insert(childNodes_.end(), spanChildren.begin(), spanChildren.end());
1457 } else {
1458 childNodes_.push_back(child);
1459 }
1460 }
1461 return childNodes_;
1462 }
1463
GetSelectedSpanText(std::wstring value,int32_t start,int32_t end) const1464 std::string TextPattern::GetSelectedSpanText(std::wstring value, int32_t start, int32_t end) const
1465 {
1466 if (start < 0 || end > static_cast<int32_t>(value.length()) || start >= end) {
1467 return "";
1468 }
1469 auto min = std::min(start, end);
1470 auto max = std::max(start, end);
1471
1472 return StringUtils::ToString(value.substr(min, max - min));
1473 }
1474
GetTextStyleObject(const RefPtr<SpanNode> & node)1475 TextStyleResult TextPattern::GetTextStyleObject(const RefPtr<SpanNode>& node)
1476 {
1477 TextStyleResult textStyle;
1478 textStyle.fontColor = node->GetTextColorValue(Color::BLACK).ColorToString();
1479 textStyle.fontSize = node->GetFontSizeValue(Dimension(16.0f, DimensionUnit::VP)).ConvertToVp();
1480 textStyle.fontStyle = static_cast<int32_t>(node->GetItalicFontStyleValue(OHOS::Ace::FontStyle::NORMAL));
1481 textStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
1482 std::string fontFamilyValue;
1483 const std::vector<std::string> defaultFontFamily = { "HarmonyOS Sans" };
1484 auto fontFamily = node->GetFontFamilyValue(defaultFontFamily);
1485 for (const auto& str : fontFamily) {
1486 fontFamilyValue += str;
1487 fontFamilyValue += ",";
1488 }
1489 fontFamilyValue = fontFamilyValue.substr(0, fontFamilyValue.size() - 1);
1490 textStyle.fontFamily = !fontFamilyValue.empty() ? fontFamilyValue : defaultFontFamily.front();
1491 textStyle.decorationType = static_cast<int32_t>(node->GetTextDecorationValue(TextDecoration::NONE));
1492 textStyle.decorationColor = node->GetTextDecorationColorValue(Color::BLACK).ColorToString();
1493 textStyle.textAlign = static_cast<int32_t>(node->GetTextAlignValue(TextAlign::START));
1494 auto lm = node->GetLeadingMarginValue({});
1495 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_START] = Dimension(lm.size.Width()).ConvertToVp();
1496 textStyle.leadingMarginSize[RichEditorLeadingRange::LEADING_END] = Dimension(lm.size.Height()).ConvertToVp();
1497 return textStyle;
1498 }
1499
GetChildByIndex(int32_t index) const1500 RefPtr<UINode> TextPattern::GetChildByIndex(int32_t index) const
1501 {
1502 const auto& children = GetAllChildren();
1503 int32_t size = static_cast<int32_t>(children.size());
1504 if (index < 0 || index >= size) {
1505 return nullptr;
1506 }
1507 auto pos = children.begin();
1508 std::advance(pos, index);
1509 return *pos;
1510 }
1511
GetSpanItemByIndex(int32_t index) const1512 RefPtr<SpanItem> TextPattern::GetSpanItemByIndex(int32_t index) const
1513 {
1514 int32_t size = static_cast<int32_t>(spans_.size());
1515 if (index < 0 || index >= size) {
1516 return nullptr;
1517 }
1518 auto pos = spans_.begin();
1519 std::advance(pos, index);
1520 return *pos;
1521 }
1522
GetTextResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)1523 ResultObject TextPattern::GetTextResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
1524 {
1525 bool selectFlag = false;
1526 ResultObject resultObject;
1527 if (!DynamicCast<SpanNode>(uinode)) {
1528 return resultObject;
1529 }
1530 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
1531 int32_t itemLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
1532 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
1533 int32_t startPosition = endPosition - itemLength;
1534
1535 if (startPosition >= start && endPosition <= end) {
1536 selectFlag = true;
1537 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1538 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1539 } else if (startPosition < start && endPosition <= end && endPosition > start) {
1540 selectFlag = true;
1541 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1542 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1543 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
1544 selectFlag = true;
1545 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1546 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1547 } else if (startPosition <= start && endPosition >= end) {
1548 selectFlag = true;
1549 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1550 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1551 }
1552 if (selectFlag) {
1553 resultObject.spanPosition.spanIndex = index;
1554 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
1555 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
1556 resultObject.type = SelectSpanType::TYPESPAN;
1557 resultObject.valueString = spanItem->content;
1558 auto spanNode = DynamicCast<SpanNode>(uinode);
1559 resultObject.textStyle = GetTextStyleObject(spanNode);
1560 }
1561 return resultObject;
1562 }
1563
GetSymbolSpanResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)1564 ResultObject TextPattern::GetSymbolSpanResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
1565 {
1566 bool selectFlag = false;
1567 ResultObject resultObject;
1568 resultObject.isDraggable = false;
1569 if (!DynamicCast<SpanNode>(uinode)) {
1570 return resultObject;
1571 }
1572 auto spanItem = DynamicCast<SpanNode>(uinode)->GetSpanItem();
1573 int32_t itemLength = static_cast<int32_t>(StringUtils::ToWstring(spanItem->content).length());
1574 int32_t endPosition = std::min(GetTextContentLength(), spanItem->position);
1575 int32_t startPosition = endPosition - itemLength;
1576
1577 if (startPosition >= start && endPosition <= end) {
1578 selectFlag = true;
1579 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1580 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1581 } else if (startPosition < start && endPosition <= end && endPosition > start) {
1582 selectFlag = true;
1583 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1584 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1585 } else if (startPosition >= start && startPosition < end && endPosition >= end) {
1586 selectFlag = true;
1587 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1588 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1589 } else if (startPosition <= start && endPosition >= end) {
1590 selectFlag = true;
1591 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = start - startPosition;
1592 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = end - startPosition;
1593 }
1594 if (selectFlag) {
1595 resultObject.valueResource = spanItem->GetResourceObject();
1596 resultObject.spanPosition.spanIndex = index;
1597 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
1598 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
1599 resultObject.type = SelectSpanType::TYPESYMBOLSPAN;
1600 resultObject.valueString = std::to_string(spanItem->unicode);
1601 auto spanNode = DynamicCast<SpanNode>(uinode);
1602 resultObject.symbolSpanStyle = GetSymbolSpanStyleObject(spanNode);
1603 }
1604 return resultObject;
1605 }
1606
GetSymbolSpanStyleObject(const RefPtr<SpanNode> & node)1607 SymbolSpanStyle TextPattern::GetSymbolSpanStyleObject(const RefPtr<SpanNode>& node)
1608 {
1609 SymbolSpanStyle symbolSpanStyle;
1610 std::string symbolColorValue;
1611 auto symbolColors = node->GetSymbolColorList();
1612 for (const auto& color : *symbolColors) {
1613 symbolColorValue += color.ColorToString() + ",";
1614 }
1615 symbolColorValue = symbolColorValue.substr(0, symbolColorValue.size() - 1);
1616 symbolSpanStyle.symbolColor = !symbolColorValue.empty() ? symbolColorValue : SYMBOL_COLOR;
1617 symbolSpanStyle.fontSize = node->GetFontSizeValue(Dimension(DIMENSION_VALUE, DimensionUnit::VP)).ConvertToVp();
1618 symbolSpanStyle.fontWeight = static_cast<int32_t>(node->GetFontWeightValue(FontWeight::NORMAL));
1619 symbolSpanStyle.renderingStrategy = node->GetSymbolRenderingStrategyValue(0);
1620 symbolSpanStyle.effectStrategy = node->GetSymbolEffectStrategyValue(0);
1621 return symbolSpanStyle;
1622 }
1623
GetImageResultObject(RefPtr<UINode> uinode,int32_t index,int32_t start,int32_t end)1624 ResultObject TextPattern::GetImageResultObject(RefPtr<UINode> uinode, int32_t index, int32_t start, int32_t end)
1625 {
1626 int32_t itemLength = 1;
1627 ResultObject resultObject;
1628 if (!DynamicCast<FrameNode>(uinode) || !GetSpanItemByIndex(index)) {
1629 return resultObject;
1630 }
1631 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
1632 int32_t startPosition = endPosition - itemLength;
1633 if ((start <= startPosition) && (end >= endPosition)) {
1634 auto imageNode = DynamicCast<FrameNode>(uinode);
1635 auto imageLayoutProperty = DynamicCast<ImageLayoutProperty>(imageNode->GetLayoutProperty());
1636 resultObject.spanPosition.spanIndex = index;
1637 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
1638 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
1639 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
1640 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
1641 resultObject.type = SelectSpanType::TYPEIMAGE;
1642 if (!imageLayoutProperty->GetImageSourceInfo()->GetPixmap()) {
1643 resultObject.valueString = imageLayoutProperty->GetImageSourceInfo()->GetSrc();
1644 } else {
1645 resultObject.valuePixelMap = imageLayoutProperty->GetImageSourceInfo()->GetPixmap();
1646 }
1647 auto geometryNode = imageNode->GetGeometryNode();
1648 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
1649 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
1650 if (imageLayoutProperty->HasImageFit()) {
1651 resultObject.imageStyle.verticalAlign = static_cast<int32_t>(imageLayoutProperty->GetImageFitValue());
1652 }
1653 if (imageLayoutProperty->HasVerticalAlign()) {
1654 resultObject.imageStyle.objectFit = static_cast<int32_t>(imageLayoutProperty->GetVerticalAlignValue());
1655 }
1656 if (geometryNode->GetMargin()) {
1657 resultObject.imageStyle.margin = geometryNode->GetMargin()->ToJsonString();
1658 }
1659 auto imageRenderCtx = imageNode->GetRenderContext();
1660 if (imageRenderCtx->GetBorderRadius()) {
1661 BorderRadiusProperty brp;
1662 auto jsonObject = JsonUtil::Create(true);
1663 auto jsonBorder = JsonUtil::Create(true);
1664 imageRenderCtx->GetBorderRadiusValue(brp).ToJsonValue(jsonObject, jsonBorder);
1665 resultObject.imageStyle.borderRadius = jsonObject->ToString();
1666 }
1667 }
1668 return resultObject;
1669 }
1670
1671 // ===========================================================
1672 // TextDragBase implementations
GetLineHeight() const1673 float TextPattern::GetLineHeight() const
1674 {
1675 std::vector<RectF> selectedRects;
1676 paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
1677 CHECK_NULL_RETURN(selectedRects.size(), {});
1678 return selectedRects.front().Height();
1679 }
1680
GetTextBoxes()1681 std::vector<RectF> TextPattern::GetTextBoxes()
1682 {
1683 std::vector<RectF> selectedRects;
1684 paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
1685 return selectedRects;
1686 }
1687
GetParentGlobalOffset() const1688 OffsetF TextPattern::GetParentGlobalOffset() const
1689 {
1690 auto host = GetHost();
1691 CHECK_NULL_RETURN(host, {});
1692 auto pipeline = PipelineContext::GetCurrentContext();
1693 CHECK_NULL_RETURN(pipeline, {});
1694 auto rootOffset = pipeline->GetRootRect().GetOffset();
1695 return host->GetPaintRectOffset() - rootOffset;
1696 }
1697
CreateHandles()1698 void TextPattern::CreateHandles()
1699 {
1700 if (IsDragging()) {
1701 TAG_LOGI(AceLogTag::ACE_TEXT, "do not show handles when dragging");
1702 return;
1703 }
1704 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1705 }
1706
BetweenSelectedPosition(const Offset & globalOffset)1707 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
1708 {
1709 auto host = GetHost();
1710 CHECK_NULL_RETURN(host, false);
1711 auto offset = host->GetPaintRectOffset();
1712 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
1713 return IsDraggable(localOffset);
1714 }
1715
1716 // end of TextDragBase implementations
1717 // ===========================================================
1718
OnModifyDone()1719 void TextPattern::OnModifyDone()
1720 {
1721 #if (defined(__aarch64__) || defined(__x86_64__))
1722 FrameNode::PostTask(
1723 [weak = WeakClaim(this)]() {
1724 auto pattern = weak.Upgrade();
1725 CHECK_NULL_VOID(pattern);
1726 pattern->OnAfterModifyDone();
1727 },
1728 TaskExecutor::TaskType::UI);
1729 #endif
1730 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1731 CHECK_NULL_VOID(textLayoutProperty);
1732 auto host = GetHost();
1733 CHECK_NULL_VOID(host);
1734 auto renderContext = host->GetRenderContext();
1735 CHECK_NULL_VOID(renderContext);
1736
1737 if (CheckNeedMeasure(textLayoutProperty->GetPropertyChangeFlag())) {
1738 // measure flag changed, reset paragraph.
1739 paragraph_.Reset();
1740 }
1741
1742 if (!(PipelineContext::GetCurrentContext() &&
1743 PipelineContext::GetCurrentContext()->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
1744 bool shouldClipToContent =
1745 textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
1746 host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
1747 }
1748
1749 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE) {
1750 CloseSelectOverlay();
1751 ResetSelection();
1752 copyOption_ = CopyOptions::None;
1753 } else {
1754 copyOption_ = textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
1755 }
1756 if (GetAllChildren().empty()) {
1757 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
1758 bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
1759 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
1760 if (ifHaveObscured) {
1761 CloseSelectOverlay();
1762 ResetSelection();
1763 copyOption_ = CopyOptions::None;
1764 }
1765
1766 std::string textCache = textForDisplay_;
1767 textForDisplay_ = textLayoutProperty->GetContent().value_or("");
1768 if (textCache != textForDisplay_) {
1769 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
1770 dataDetectorAdapter_->aiDetectInitialized_ = false;
1771 CloseSelectOverlay();
1772 ResetSelection();
1773 }
1774 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
1775 dataDetectorAdapter_->textForAI_ = textForDisplay_;
1776 dataDetectorAdapter_->StartAITask();
1777 }
1778 }
1779
1780 auto gestureEventHub = host->GetOrCreateGestureEventHub();
1781 CHECK_NULL_VOID(gestureEventHub);
1782 if (copyOption_ != CopyOptions::None) {
1783 auto context = PipelineContext::GetCurrentContext();
1784 CHECK_NULL_VOID(context);
1785 if (!clipboard_ && context) {
1786 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
1787 }
1788 InitLongPressEvent(gestureEventHub);
1789 if (host->IsDraggable()) {
1790 InitDragEvent();
1791 }
1792 InitMouseEvent();
1793 InitTouchEvent();
1794 SetAccessibilityAction();
1795 } else {
1796 if (host->IsDraggable() || gestureEventHub->GetTextDraggable()) {
1797 gestureEventHub->SetTextDraggable(false);
1798 }
1799 }
1800 if (onClick_ || copyOption_ != CopyOptions::None) {
1801 InitClickEvent(gestureEventHub);
1802 }
1803 auto eventHub = host->GetEventHub<EventHub>();
1804 CHECK_NULL_VOID(eventHub);
1805 bool enabledCache = eventHub->IsEnabled();
1806 if (textDetectEnable_ && enabledCache != enabled_) {
1807 enabled_ = enabledCache;
1808 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE);
1809 }
1810 }
1811
ToJsonValue(std::unique_ptr<JsonValue> & json) const1812 void TextPattern::ToJsonValue(std::unique_ptr<JsonValue>& json) const
1813 {
1814 json->Put("enableDataDetector", textDetectEnable_ ? "true" : "false");
1815 auto jsonValue = JsonUtil::Create(true);
1816 jsonValue->Put("types", "");
1817 json->Put("dataDetectorConfig", jsonValue->ToString().c_str());
1818 }
1819
OnAfterModifyDone()1820 void TextPattern::OnAfterModifyDone()
1821 {
1822 auto host = GetHost();
1823 CHECK_NULL_VOID(host);
1824 auto inspectorId = host->GetInspectorId().value_or("");
1825 if (!inspectorId.empty()) {
1826 auto prop = host->GetAccessibilityProperty<NG::AccessibilityProperty>();
1827 Recorder::NodeDataCache::Get().PutString(inspectorId, prop->GetText());
1828 }
1829 }
1830
ActSetSelection(int32_t start,int32_t end)1831 void TextPattern::ActSetSelection(int32_t start, int32_t end)
1832 {
1833 if (start == -1 && end == -1) {
1834 ResetSelection();
1835 CloseSelectOverlay();
1836 return;
1837 }
1838 int32_t min = 0;
1839 int32_t textSize = static_cast<int32_t>(GetWideText().length()) + placeholderCount_;
1840 start = start < min ? min : start;
1841 end = end < min ? min : end;
1842 start = start > textSize ? textSize : start;
1843 end = end > textSize ? textSize : end;
1844 if (start >= end) {
1845 FireOnSelectionChange(-1, -1);
1846 return;
1847 }
1848 HandleSelectionChange(start, end);
1849 CalculateHandleOffsetAndShowOverlay();
1850 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1851 auto host = GetHost();
1852 CHECK_NULL_VOID(host);
1853 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
1854 }
1855
UpdateSelectOverlayOrCreate(SelectOverlayInfo & selectInfo,bool animation)1856 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo& selectInfo, bool animation)
1857 {
1858 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
1859 SelectHandleInfo firstHandleInfo;
1860 firstHandleInfo.paintRect = textSelector_.firstHandle;
1861 CheckHandles(firstHandleInfo);
1862
1863 SelectHandleInfo secondHandleInfo;
1864 secondHandleInfo.paintRect = textSelector_.secondHandle;
1865 CheckHandles(secondHandleInfo);
1866
1867 auto start = textSelector_.GetTextStart();
1868 auto end = textSelector_.GetTextEnd();
1869 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
1870 if (selectInfo.isNewAvoid) {
1871 selectOverlayProxy_->UpdateSelectArea(selectInfo.selectArea);
1872 }
1873 selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
1874 selectOverlayProxy_->ShowOrHiddenMenu(!firstHandleInfo.isShow && !secondHandleInfo.isShow);
1875 } else {
1876 auto pipeline = PipelineContext::GetCurrentContext();
1877 CHECK_NULL_VOID(pipeline);
1878 auto host = GetHost();
1879 CHECK_NULL_VOID(host);
1880 pipeline->AddOnAreaChangeNode(host->GetId());
1881 selectInfo.callerFrameNode = GetHost();
1882 selectInfo.hitTestMode = HitTestMode::HTMDEFAULT;
1883 if (!selectInfo.isUsingMouse) {
1884 CheckHandles(selectInfo.firstHandle);
1885 CheckHandles(selectInfo.secondHandle);
1886 }
1887 selectOverlayProxy_ =
1888 pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
1889 CHECK_NULL_VOID(selectOverlayProxy_);
1890 auto start = textSelector_.GetTextStart();
1891 auto end = textSelector_.GetTextEnd();
1892 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
1893 }
1894 }
1895
RedisplaySelectOverlay()1896 void TextPattern::RedisplaySelectOverlay()
1897 {
1898 if (!isShowMenu_) {
1899 TAG_LOGD(AceLogTag::ACE_TEXT, "Do not redisplaySelectOverlay when drag failed");
1900 isShowMenu_ = true;
1901 return;
1902 }
1903 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
1904 CalculateHandleOffsetAndShowOverlay();
1905 if (selectOverlayProxy_->IsMenuShow()) {
1906 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1907 } else {
1908 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
1909 selectOverlayProxy_->ShowOrHiddenMenu(true);
1910 }
1911 }
1912 }
1913
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)1914 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
1915 {
1916 if (config.skipMeasure || dirty->SkipMeasureContent()) {
1917 RedisplaySelectOverlay();
1918 return false;
1919 }
1920
1921 contentRect_ = dirty->GetGeometryNode()->GetContentRect();
1922
1923 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
1924 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
1925 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
1926 CHECK_NULL_RETURN(textLayoutAlgorithm, false);
1927 auto&& paragraph = textLayoutAlgorithm->GetParagraph();
1928 if (!paragraph) {
1929 return false;
1930 }
1931 paragraph_ = paragraph;
1932 // The handle calculation needs to be after the paragraph is assigned.
1933 RedisplaySelectOverlay();
1934 baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
1935 contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
1936 textStyle_ = textLayoutAlgorithm->GetTextStyle();
1937 return true;
1938 }
1939
PreCreateLayoutWrapper()1940 void TextPattern::PreCreateLayoutWrapper()
1941 {
1942 auto host = GetHost();
1943 CHECK_NULL_VOID(host);
1944
1945 // mark content dirty
1946 if (contentMod_) {
1947 contentMod_->ContentChange();
1948 }
1949
1950 auto paintProperty = GetPaintProperty<PaintProperty>();
1951 CHECK_NULL_VOID(paintProperty);
1952 auto flag = paintProperty->GetPropertyChangeFlag();
1953 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1954 CHECK_NULL_VOID(textLayoutProperty);
1955 auto layoutFlag = textLayoutProperty->GetPropertyChangeFlag();
1956 if (!CheckNeedMeasure(flag) && !CheckNeedMeasure(layoutFlag)) {
1957 return;
1958 }
1959
1960 spans_.clear();
1961
1962 // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
1963 const auto& children = host->GetChildren();
1964 if (children.empty()) {
1965 placeholderCount_ = 0;
1966 return;
1967 }
1968
1969 // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
1970 // tree.
1971 std::stack<SpanNodeInfo> nodes;
1972 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
1973 nodes.push({ .node = *iter });
1974 }
1975
1976 InitSpanItem(nodes);
1977 }
1978
InitSpanItem(std::stack<SpanNodeInfo> nodes)1979 void TextPattern::InitSpanItem(std::stack<SpanNodeInfo> nodes)
1980 {
1981 auto host = GetHost();
1982 CHECK_NULL_VOID(host);
1983 std::string textCache;
1984 std::string textForAICache;
1985 int32_t oldPlaceholderCount = placeholderCount_;
1986 placeholderCount_ = 0;
1987 if (!nodes.empty()) {
1988 textCache = textForDisplay_;
1989 textForAICache = dataDetectorAdapter_->textForAI_;
1990 textForDisplay_.clear();
1991 dataDetectorAdapter_->textForAI_.clear();
1992 }
1993
1994 bool isSpanHasClick = false;
1995 CollectSpanNodes(nodes, isSpanHasClick);
1996 if (oldPlaceholderCount != placeholderCount_) {
1997 CloseSelectOverlay();
1998 ResetSelection();
1999 }
2000
2001 if (textCache != textForDisplay_) {
2002 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
2003 dataDetectorAdapter_->aiDetectInitialized_ = false;
2004 OnAfterModifyDone();
2005 for (const auto& item : spans_) {
2006 if (item->inspectId.empty()) {
2007 continue;
2008 }
2009 Recorder::NodeDataCache::Get().PutString(item->inspectId, item->content);
2010 }
2011 CloseSelectOverlay();
2012 ResetSelection();
2013 }
2014 if (isSpanHasClick) {
2015 auto gestureEventHub = host->GetOrCreateGestureEventHub();
2016 InitClickEvent(gestureEventHub);
2017 }
2018 if (CanStartAITask() && !dataDetectorAdapter_->aiDetectInitialized_) {
2019 dataDetectorAdapter_->StartAITask();
2020 }
2021 }
2022
BeforeCreateLayoutWrapper()2023 void TextPattern::BeforeCreateLayoutWrapper()
2024 {
2025 PreCreateLayoutWrapper();
2026 }
2027
CollectSpanNodes(std::stack<SpanNodeInfo> nodes,bool & isSpanHasClick)2028 void TextPattern::CollectSpanNodes(std::stack<SpanNodeInfo> nodes, bool& isSpanHasClick)
2029 {
2030 while (!nodes.empty()) {
2031 auto current = nodes.top();
2032 nodes.pop();
2033 // TODO: Add the judgment of display.
2034 if (!current.node) {
2035 continue;
2036 }
2037 UpdateContainerChildren(current.containerSpanNode, current.node);
2038 auto spanNode = DynamicCast<SpanNode>(current.node);
2039 auto tag = current.node->GetTag();
2040 if (spanNode && tag == V2::SYMBOL_SPAN_ETS_TAG) {
2041 spanNode->CleanSpanItemChildren();
2042 UpdateChildProperty(spanNode);
2043 spanNode->MountToParagraph();
2044 textForDisplay_.append(StringUtils::Str16ToStr8(SYMBOL_TRANS));
2045 } else if (spanNode && tag != V2::PLACEHOLDER_SPAN_ETS_TAG) {
2046 spanNode->CleanSpanItemChildren();
2047 UpdateChildProperty(spanNode);
2048 spanNode->MountToParagraph();
2049 textForDisplay_.append(spanNode->GetSpanItem()->content);
2050 dataDetectorAdapter_->textForAI_.append(spanNode->GetSpanItem()->content);
2051 if (spanNode->GetSpanItem()->onClick) {
2052 isSpanHasClick = true;
2053 }
2054 } else if (tag == V2::IMAGE_ETS_TAG || tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2055 placeholderCount_++;
2056 AddChildSpanItem(current.node);
2057 dataDetectorAdapter_->textForAI_.append("\n");
2058 auto imageNode = DynamicCast<FrameNode>(current.node);
2059 if (!imageNode) {
2060 continue;
2061 }
2062 auto focus_hub = imageNode->GetOrCreateFocusHub();
2063 if (focus_hub && focus_hub->GetOnClickCallback()) {
2064 isSpanHasClick = true;
2065 }
2066 }
2067 if (tag == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2068 continue;
2069 }
2070 auto containerSpanNode = tag == V2::CONTAINER_SPAN_ETS_TAG ? current.node : current.containerSpanNode;
2071 const auto& nextChildren = current.node->GetChildren();
2072 for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
2073 nodes.push({ .node = *iter, .containerSpanNode = containerSpanNode });
2074 }
2075 }
2076 }
2077
UpdateContainerChildren(const RefPtr<UINode> & parentNode,const RefPtr<UINode> & child)2078 void TextPattern::UpdateContainerChildren(const RefPtr<UINode>& parentNode, const RefPtr<UINode>& child)
2079 {
2080 CHECK_NULL_VOID(child);
2081 auto parent = DynamicCast<ContainerSpanNode>(parentNode);
2082 CHECK_NULL_VOID(parent);
2083 auto baseSpan = DynamicCast<BaseSpan>(child);
2084 if (baseSpan) {
2085 if (baseSpan->HasTextBackgroundStyle()) {
2086 return;
2087 }
2088 baseSpan->UpdateTextBackgroundFromParent(parent->GetTextBackgroundStyle());
2089 return;
2090 }
2091 if (child->GetTag() == V2::IMAGE_ETS_TAG) {
2092 auto imageNode = DynamicCast<FrameNode>(child);
2093 CHECK_NULL_VOID(imageNode);
2094 auto imageLayoutProperty = imageNode->GetLayoutProperty<ImageLayoutProperty>();
2095 CHECK_NULL_VOID(imageLayoutProperty);
2096 if (imageLayoutProperty->GetHasPlaceHolderStyleValue(false)) {
2097 return;
2098 }
2099 if (parent->GetTextBackgroundStyle().has_value()) {
2100 imageLayoutProperty->UpdatePlaceHolderStyle(parent->GetTextBackgroundStyle().value());
2101 }
2102 }
2103 }
2104
GetGlobalOffset(Offset & offset)2105 void TextPattern::GetGlobalOffset(Offset& offset)
2106 {
2107 auto host = GetHost();
2108 CHECK_NULL_VOID(host);
2109 auto pipeline = PipelineContext::GetCurrentContext();
2110 CHECK_NULL_VOID(pipeline);
2111 auto rootOffset = pipeline->GetRootRect().GetOffset();
2112 auto globalOffset = host->GetPaintRectOffset() - rootOffset;
2113 offset = Offset(globalOffset.GetX(), globalOffset.GetY());
2114 }
2115
OnVisibleChange(bool isVisible)2116 void TextPattern::OnVisibleChange(bool isVisible)
2117 {
2118 if (!isVisible) {
2119 if (textSelector_.IsValid()) {
2120 CloseSelectOverlay();
2121 ResetSelection();
2122 }
2123 if (textDetectEnable_) {
2124 dataDetectorAdapter_->aiDetectDelayTask_.Cancel();
2125 }
2126 } else {
2127 if (CanStartAITask()) {
2128 dataDetectorAdapter_->StartAITask();
2129 }
2130 }
2131 }
2132
InitSurfaceChangedCallback()2133 void TextPattern::InitSurfaceChangedCallback()
2134 {
2135 auto pipeline = PipelineContext::GetCurrentContext();
2136 CHECK_NULL_VOID(pipeline);
2137 if (!HasSurfaceChangedCallback()) {
2138 auto callbackId = pipeline->RegisterSurfaceChangedCallback(
2139 [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
2140 WindowSizeChangeReason type) {
2141 auto pattern = weak.Upgrade();
2142 if (pattern) {
2143 pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
2144 }
2145 });
2146 UpdateSurfaceChangedCallbackId(callbackId);
2147 }
2148 }
2149
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight)2150 void TextPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
2151 {
2152 if (newWidth == prevWidth && newHeight == prevHeight) {
2153 return;
2154 }
2155 CHECK_NULL_VOID(selectOverlayProxy_);
2156 selectOverlayProxy_->ShowOrHiddenMenu(true);
2157 }
2158
InitSurfacePositionChangedCallback()2159 void TextPattern::InitSurfacePositionChangedCallback()
2160 {
2161 auto pipeline = PipelineContext::GetCurrentContext();
2162 CHECK_NULL_VOID(pipeline);
2163 if (!HasSurfacePositionChangedCallback()) {
2164 auto callbackId =
2165 pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
2166 auto pattern = weak.Upgrade();
2167 if (pattern) {
2168 pattern->HandleSurfacePositionChanged(posX, posY);
2169 }
2170 });
2171 UpdateSurfacePositionChangedCallbackId(callbackId);
2172 }
2173 }
2174
AddChildSpanItem(const RefPtr<UINode> & child)2175 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
2176 {
2177 CHECK_NULL_VOID(child);
2178 auto chidNode = DynamicCast<FrameNode>(child);
2179 if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
2180 return;
2181 }
2182
2183 if (child->GetTag() == V2::SPAN_ETS_TAG || child->GetTag() == V2::SYMBOL_SPAN_ETS_TAG) {
2184 auto spanNode = DynamicCast<SpanNode>(child);
2185 if (spanNode) {
2186 spans_.emplace_back(spanNode->GetSpanItem());
2187 }
2188 } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
2189 auto imageSpanNode = DynamicCast<ImageSpanNode>(child);
2190 if (imageSpanNode) {
2191 spans_.emplace_back(imageSpanNode->GetSpanItem());
2192 spans_.back()->imageNodeId = imageSpanNode->GetId();
2193 return;
2194 }
2195 auto imageNode = DynamicCast<FrameNode>(child);
2196 if (imageNode) {
2197 auto imageSpanItem = MakeRefPtr<ImageSpanItem>();
2198 imageSpanItem->imageNodeId = imageNode->GetId();
2199 imageSpanItem->UpdatePlaceholderBackgroundStyle(imageNode);
2200 auto focus_hub = imageNode->GetOrCreateFocusHub();
2201 CHECK_NULL_VOID(focus_hub);
2202 auto clickCall = focus_hub->GetOnClickCallback();
2203 if (clickCall) {
2204 imageSpanItem->SetOnClickEvent(std::move(clickCall));
2205 }
2206 spans_.emplace_back(imageSpanItem);
2207 auto gesture = imageNode->GetOrCreateGestureEventHub();
2208 CHECK_NULL_VOID(gesture);
2209 gesture->SetHitTestMode(HitTestMode::HTMNONE);
2210
2211 return;
2212 }
2213 } else if (child->GetTag() == V2::PLACEHOLDER_SPAN_ETS_TAG) {
2214 auto placeholderSpanNode = DynamicCast<PlaceholderSpanNode>(child);
2215 if (placeholderSpanNode) {
2216 auto placeholderSpan = placeholderSpanNode->GetSpanItem();
2217 placeholderSpan->placeholderSpanNodeId = placeholderSpanNode->GetId();
2218 spans_.emplace_back(placeholderSpan);
2219 }
2220 }
2221 }
2222
DumpAdvanceInfo()2223 void TextPattern::DumpAdvanceInfo()
2224 {
2225 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
2226 CHECK_NULL_VOID(textLayoutProp);
2227 DumpLog::GetInstance().AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
2228 DumpLog::GetInstance().AddDesc(
2229 std::string("FontColor: ").append(textLayoutProp->GetTextColor().value_or(Color::BLACK).ColorToString()));
2230 DumpLog::GetInstance().AddDesc(
2231 std::string("FontSize: ")
2232 .append(
2233 (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(16.0, DimensionUnit::FP)).ToString()));
2234 DumpLog::GetInstance().AddDesc(std::string("contentRect-->x:")
2235 .append(std::to_string(contentRect_.GetX()))
2236 .append(" y:")
2237 .append(std::to_string(contentRect_.GetY())));
2238 DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
2239 if (SystemProperties::GetDebugEnabled() && paragraph_) {
2240 DumpLog::GetInstance().AddDesc(std::string("from TextEngine paragraph_ info :"));
2241 DumpLog::GetInstance().AddDesc(
2242 std::string("DidExceedMaxLinesx:").append(std::to_string(paragraph_->DidExceedMaxLines())));
2243
2244 DumpLog::GetInstance().AddDesc(std::string("GetTextWidth:")
2245 .append(std::to_string(paragraph_->GetTextWidth()))
2246 .append(" GetHeight:")
2247 .append(std::to_string(paragraph_->GetHeight()))
2248 .append(" GetMaxWidth:")
2249 .append(std::to_string(paragraph_->GetMaxWidth()))
2250 .append(" GetMaxIntrinsicWidth:")
2251 .append(std::to_string(paragraph_->GetMaxIntrinsicWidth())));
2252 DumpLog::GetInstance().AddDesc(std::string("GetLineCount:")
2253 .append(std::to_string(paragraph_->GetLineCount()))
2254 .append(" GetLongestLine:")
2255 .append(std::to_string(paragraph_->GetLongestLine()))
2256 .append(" GetAlphabeticBaseline:")
2257 .append(std::to_string(paragraph_->GetAlphabeticBaseline())));
2258 }
2259
2260 DumpLog::GetInstance().AddDesc(
2261 std::string("BindSelectionMenu: ").append(std::to_string(selectionMenuMap_.empty())));
2262 }
2263
DumpInfo()2264 void TextPattern::DumpInfo()
2265 {
2266 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
2267 CHECK_NULL_VOID(textLayoutProp);
2268 DumpLog::GetInstance().AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
2269 DumpLog::GetInstance().AddDesc(
2270 std::string("FontColor: ").append(textLayoutProp->GetTextColor().value_or(Color::BLACK).ColorToString()));
2271 DumpLog::GetInstance().AddDesc(
2272 std::string("FontSize: ")
2273 .append(
2274 (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(16.0, DimensionUnit::FP)).ToString()));
2275 DumpLog::GetInstance().AddDesc(std::string("Selection: ").append("(").append(textSelector_.ToString()).append(")"));
2276 }
2277
UpdateChildProperty(const RefPtr<SpanNode> & child) const2278 void TextPattern::UpdateChildProperty(const RefPtr<SpanNode>& child) const
2279 {
2280 CHECK_NULL_VOID(child);
2281 auto host = GetHost();
2282 CHECK_NULL_VOID(host);
2283 auto textLayoutProp = host->GetLayoutProperty<TextLayoutProperty>();
2284 CHECK_NULL_VOID(textLayoutProp);
2285
2286 auto inheritPropertyInfo = child->CalculateInheritPropertyInfo();
2287 for (const PropertyInfo& info : inheritPropertyInfo) {
2288 switch (info) {
2289 case PropertyInfo::FONTSIZE:
2290 if (textLayoutProp->HasFontSize()) {
2291 child->UpdateFontSizeWithoutFlushDirty(textLayoutProp->GetFontSize().value());
2292 }
2293 break;
2294 case PropertyInfo::FONTCOLOR:
2295 if (textLayoutProp->HasTextColor()) {
2296 child->UpdateTextColorWithoutFlushDirty(textLayoutProp->GetTextColor().value());
2297 }
2298 break;
2299 case PropertyInfo::FONTSTYLE:
2300 if (textLayoutProp->HasItalicFontStyle()) {
2301 child->UpdateItalicFontStyleWithoutFlushDirty(textLayoutProp->GetItalicFontStyle().value());
2302 }
2303 break;
2304 case PropertyInfo::FONTWEIGHT:
2305 if (textLayoutProp->HasFontWeight()) {
2306 child->UpdateFontWeightWithoutFlushDirty(textLayoutProp->GetFontWeight().value());
2307 }
2308 break;
2309 case PropertyInfo::FONTFAMILY:
2310 if (textLayoutProp->HasFontFamily()) {
2311 child->UpdateFontFamilyWithoutFlushDirty(textLayoutProp->GetFontFamily().value());
2312 }
2313 break;
2314 case PropertyInfo::TEXTDECORATION:
2315 if (textLayoutProp->HasTextDecoration()) {
2316 child->UpdateTextDecorationWithoutFlushDirty(textLayoutProp->GetTextDecoration().value());
2317 if (textLayoutProp->HasTextDecorationColor()) {
2318 child->UpdateTextDecorationColorWithoutFlushDirty(
2319 textLayoutProp->GetTextDecorationColor().value());
2320 }
2321 if (textLayoutProp->HasTextDecorationStyle()) {
2322 child->UpdateTextDecorationStyleWithoutFlushDirty(
2323 textLayoutProp->GetTextDecorationStyle().value());
2324 }
2325 }
2326 break;
2327 case PropertyInfo::TEXTCASE:
2328 if (textLayoutProp->HasTextCase()) {
2329 child->UpdateTextCaseWithoutFlushDirty(textLayoutProp->GetTextCase().value());
2330 }
2331 break;
2332 case PropertyInfo::LETTERSPACE:
2333 if (textLayoutProp->HasLetterSpacing()) {
2334 child->UpdateLetterSpacingWithoutFlushDirty(textLayoutProp->GetLetterSpacing().value());
2335 }
2336 break;
2337 case PropertyInfo::LINEHEIGHT:
2338 if (textLayoutProp->HasLineHeight()) {
2339 child->UpdateLineHeightWithoutFlushDirty(textLayoutProp->GetLineHeight().value());
2340 }
2341 break;
2342 case PropertyInfo::TEXTSHADOW:
2343 if (textLayoutProp->HasTextShadow()) {
2344 child->UpdateTextShadowWithoutFlushDirty(textLayoutProp->GetTextShadow().value());
2345 }
2346 break;
2347 default:
2348 break;
2349 }
2350 }
2351 }
2352
SetAccessibilityAction()2353 void TextPattern::SetAccessibilityAction()
2354 {
2355 auto host = GetHost();
2356 CHECK_NULL_VOID(host);
2357 auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
2358 CHECK_NULL_VOID(textAccessibilityProperty);
2359 textAccessibilityProperty->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end) {
2360 const auto& pattern = weakPtr.Upgrade();
2361 CHECK_NULL_VOID(pattern);
2362 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
2363 CHECK_NULL_VOID(textLayoutProperty);
2364 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
2365 pattern->ActSetSelection(start, end);
2366 }
2367 });
2368
2369 textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
2370 const auto& pattern = weakPtr.Upgrade();
2371 CHECK_NULL_VOID(pattern);
2372 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
2373 CHECK_NULL_VOID(textLayoutProperty);
2374 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
2375 pattern->CloseSelectOverlay(true);
2376 pattern->ResetSelection();
2377 }
2378 });
2379
2380 textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
2381 const auto& pattern = weakPtr.Upgrade();
2382 CHECK_NULL_VOID(pattern);
2383 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
2384 CHECK_NULL_VOID(textLayoutProperty);
2385 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
2386 pattern->HandleOnCopy();
2387 pattern->CloseSelectOverlay(true);
2388 pattern->ResetSelection();
2389 }
2390 });
2391 }
2392
OnColorConfigurationUpdate()2393 void TextPattern::OnColorConfigurationUpdate()
2394 {
2395 auto context = PipelineContext::GetCurrentContext();
2396 CHECK_NULL_VOID(context);
2397 auto theme = context->GetTheme<TextTheme>();
2398 CHECK_NULL_VOID(theme);
2399 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
2400 CHECK_NULL_VOID(textLayoutProperty);
2401 textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
2402 }
2403
GetDragUpperLeftCoordinates()2404 OffsetF TextPattern::GetDragUpperLeftCoordinates()
2405 {
2406 if (dragBoxes_.empty()) {
2407 return { 0.0f, 0.0f };
2408 }
2409 auto startY = dragBoxes_.front().Top();
2410 auto startX = dragBoxes_.front().Left();
2411
2412 auto endY = dragBoxes_.back().Top();
2413 OffsetF offset;
2414 if (NearEqual(startY, endY)) {
2415 offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
2416 } else {
2417 offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
2418 }
2419
2420 return GetParentGlobalOffset() + offset;
2421 }
2422
ProcessBoundRectByTextShadow(RectF & rect)2423 void TextPattern::ProcessBoundRectByTextShadow(RectF& rect)
2424 {
2425 auto property = GetHost()->GetLayoutProperty<TextLayoutProperty>();
2426 auto shadows = property->GetTextShadow();
2427 if (!shadows.has_value()) {
2428 return;
2429 }
2430 float leftOffsetX = 0.0f;
2431 float rightOffsetX = 0.0f;
2432 float upOffsetY = 0.0f;
2433 float downOffsetY = 0.0f;
2434 for (const auto& shadow : shadows.value()) {
2435 auto shadowBlurRadius = shadow.GetBlurRadius() * 2.0f;
2436 if (LessNotEqual(shadow.GetOffset().GetX() - shadowBlurRadius, leftOffsetX)) {
2437 leftOffsetX = shadow.GetOffset().GetX() - shadowBlurRadius;
2438 }
2439
2440 if (GreatNotEqual(shadow.GetOffset().GetX() + shadowBlurRadius, rightOffsetX)) {
2441 rightOffsetX = shadow.GetOffset().GetX() + shadowBlurRadius;
2442 }
2443
2444 if (LessNotEqual(shadow.GetOffset().GetY() - shadowBlurRadius, upOffsetY)) {
2445 upOffsetY = shadow.GetOffset().GetY() - shadowBlurRadius;
2446 }
2447
2448 if (GreatNotEqual(shadow.GetOffset().GetY() + shadowBlurRadius, downOffsetY)) {
2449 downOffsetY = shadow.GetOffset().GetY() + shadowBlurRadius;
2450 }
2451 }
2452 rect.SetRect(
2453 leftOffsetX, upOffsetY, rect.Width() + rightOffsetX - leftOffsetX, rect.Height() + downOffsetY - upOffsetY);
2454 }
2455
ProcessBoundRectByTextMarquee(RectF & rect)2456 void TextPattern::ProcessBoundRectByTextMarquee(RectF& rect)
2457 {
2458 auto host = GetHost();
2459 CHECK_NULL_VOID(host);
2460 auto textLayoutProperty = host->GetLayoutProperty<TextLayoutProperty>();
2461 CHECK_NULL_VOID(textLayoutProperty);
2462 if (!(textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE)) {
2463 return;
2464 }
2465 auto geometryNode = host->GetGeometryNode();
2466 CHECK_NULL_VOID(geometryNode);
2467 auto frameSize = geometryNode->GetFrameSize();
2468 CHECK_NULL_VOID(paragraph_);
2469 if (paragraph_->GetTextWidth() < frameSize.Width()) {
2470 return;
2471 }
2472 auto relativeSelfLeftOffsetX =
2473 std::max(-1 * host->GetOffsetRelativeToWindow().GetX(), rect.GetOffset().GetX() - paragraph_->GetTextWidth());
2474 rect.SetLeft(relativeSelfLeftOffsetX);
2475 rect.SetWidth(frameSize.Width() + paragraph_->GetTextWidth() - relativeSelfLeftOffsetX);
2476 }
2477
CreateNodePaintMethod()2478 RefPtr<NodePaintMethod> TextPattern::CreateNodePaintMethod()
2479 {
2480 if (!contentMod_) {
2481 contentMod_ = MakeRefPtr<TextContentModifier>(textStyle_);
2482 }
2483 if (!overlayMod_) {
2484 overlayMod_ = MakeRefPtr<TextOverlayModifier>();
2485 }
2486 if (isCustomFont_) {
2487 contentMod_->SetIsCustomFont(true);
2488 }
2489 auto paintMethod = MakeRefPtr<TextPaintMethod>(WeakClaim(this), baselineOffset_, contentMod_, overlayMod_);
2490 auto host = GetHost();
2491 CHECK_NULL_RETURN(host, paintMethod);
2492 auto context = host->GetRenderContext();
2493 CHECK_NULL_RETURN(context, paintMethod);
2494 if (context->GetClipEdge().has_value()) {
2495 auto geometryNode = host->GetGeometryNode();
2496 auto frameSize = geometryNode->GetFrameSize();
2497 CHECK_NULL_RETURN(paragraph_, paintMethod);
2498 RectF boundsRect = overlayMod_->GetBoundsRect();
2499 auto boundsWidth = contentRect_.GetX() + static_cast<float>(paragraph_->GetLongestLine());
2500 auto boundsHeight =
2501 contentRect_.GetY() + static_cast<float>(paragraph_->GetHeight() + std::fabs(baselineOffset_));
2502 boundsRect.SetWidth(boundsWidth);
2503 boundsRect.SetHeight(boundsHeight);
2504 ProcessBoundRectByTextShadow(boundsRect);
2505 ProcessBoundRectByTextMarquee(boundsRect);
2506 if (!context->GetClipEdge().value() && (LessNotEqual(frameSize.Width(), boundsRect.Width()) ||
2507 LessNotEqual(frameSize.Height(), boundsRect.Height()))) {
2508 boundsWidth = std::max(frameSize.Width(), boundsRect.Width());
2509 boundsHeight = std::max(frameSize.Height(), boundsRect.Height());
2510 boundsRect.SetWidth(boundsWidth);
2511 boundsRect.SetHeight(boundsHeight);
2512 overlayMod_->SetBoundsRect(boundsRect);
2513
2514 auto gestureHub = host->GetOrCreateGestureEventHub();
2515 CHECK_NULL_RETURN(gestureHub, paintMethod);
2516 std::vector<DimensionRect> hotZoneRegions;
2517 DimensionRect hotZoneRegion;
2518 hotZoneRegion.SetSize(DimensionSize(Dimension(boundsWidth), Dimension(boundsHeight)));
2519 hotZoneRegion.SetOffset(
2520 DimensionOffset(Dimension(contentOffset_.GetX()), Dimension(contentOffset_.GetY())));
2521 hotZoneRegions.emplace_back(hotZoneRegion);
2522 gestureHub->SetResponseRegion(hotZoneRegions);
2523 }
2524 }
2525 return paintMethod;
2526 }
2527
GetHandleIndex(const Offset & offset) const2528 int32_t TextPattern::GetHandleIndex(const Offset& offset) const
2529 {
2530 return paragraph_->GetGlyphIndexByCoordinate(offset);
2531 }
2532
OnAreaChangedInner()2533 void TextPattern::OnAreaChangedInner()
2534 {
2535 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
2536 auto parentGlobalOffset = GetParentGlobalOffset();
2537 if (parentGlobalOffset != parentGlobalOffset_) {
2538 parentGlobalOffset_ = parentGlobalOffset;
2539 CalculateHandleOffsetAndShowOverlay();
2540 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
2541 }
2542 }
2543 }
2544
RemoveAreaChangeInner()2545 void TextPattern::RemoveAreaChangeInner()
2546 {
2547 auto pipeline = PipelineContext::GetCurrentContext();
2548 CHECK_NULL_VOID(pipeline);
2549 auto host = GetHost();
2550 CHECK_NULL_VOID(host);
2551 auto eventHub = host->GetEventHub<TextEventHub>();
2552 CHECK_NULL_VOID(eventHub);
2553 if (eventHub->HasOnAreaChanged()) {
2554 return;
2555 }
2556 pipeline->RemoveOnAreaChangeNode(host->GetId());
2557 }
2558
NeedShowAIDetect()2559 bool TextPattern::NeedShowAIDetect()
2560 {
2561 return textDetectEnable_ && copyOption_ != CopyOptions::None && enabled_ &&
2562 !dataDetectorAdapter_->aiSpanMap_.empty();
2563 }
2564
BindSelectionMenu(TextSpanType spanType,TextResponseType responseType,std::function<void ()> & menuBuilder,std::function<void (int32_t,int32_t)> & onAppear,std::function<void ()> & onDisappear)2565 void TextPattern::BindSelectionMenu(TextSpanType spanType, TextResponseType responseType,
2566 std::function<void()>& menuBuilder, std::function<void(int32_t, int32_t)>& onAppear,
2567 std::function<void()>& onDisappear)
2568 {
2569 auto key = std::make_pair(spanType, responseType);
2570 auto it = selectionMenuMap_.find(key);
2571 if (it != selectionMenuMap_.end()) {
2572 if (menuBuilder == nullptr) {
2573 selectionMenuMap_.erase(it);
2574 return;
2575 }
2576 it->second->buildFunc = menuBuilder;
2577 it->second->onAppear = onAppear;
2578 it->second->onDisappear = onDisappear;
2579 return;
2580 }
2581
2582 auto selectionMenuParams =
2583 std::make_shared<SelectionMenuParams>(spanType, menuBuilder, onAppear, onDisappear, responseType);
2584 selectionMenuMap_[key] = selectionMenuParams;
2585 auto host = GetHost();
2586 CHECK_NULL_VOID(host);
2587 host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF);
2588 }
2589
CloseSelectionMenu()2590 void TextPattern::CloseSelectionMenu()
2591 {
2592 textResponseType_ = TextResponseType::NONE;
2593 CloseSelectOverlay(true);
2594 }
2595
GetMenuParams(TextSpanType spanType,TextResponseType responseType)2596 std::shared_ptr<SelectionMenuParams> TextPattern::GetMenuParams(TextSpanType spanType, TextResponseType responseType)
2597 {
2598 auto key = std::make_pair(spanType, responseType);
2599 auto it = selectionMenuMap_.find(key);
2600 if (it != selectionMenuMap_.end()) {
2601 return it->second;
2602 }
2603
2604 TAG_LOGD(AceLogTag::ACE_TEXT, "The key not in selectionMenuMap_");
2605 return nullptr;
2606 }
2607
CopySelectionMenuParams(SelectOverlayInfo & selectInfo,TextResponseType responseType)2608 void TextPattern::CopySelectionMenuParams(SelectOverlayInfo& selectInfo, TextResponseType responseType)
2609 {
2610 auto currentSpanType = selectedType_.value_or(TextSpanType::NONE);
2611 std::shared_ptr<SelectionMenuParams> menuParams = nullptr;
2612 menuParams = GetMenuParams(currentSpanType, responseType);
2613 if (menuParams == nullptr) {
2614 return;
2615 }
2616
2617 selectInfo.menuInfo.menuIsShow = true;
2618 CopyBindSelectionMenuParams(selectInfo, menuParams);
2619 }
2620
CopyBindSelectionMenuParams(SelectOverlayInfo & selectInfo,std::shared_ptr<SelectionMenuParams> menuParams)2621 void TextPattern::CopyBindSelectionMenuParams(
2622 SelectOverlayInfo& selectInfo, std::shared_ptr<SelectionMenuParams> menuParams)
2623 {
2624 selectInfo.menuInfo.menuBuilder = menuParams->buildFunc;
2625 if (menuParams->onAppear) {
2626 auto weak = AceType::WeakClaim(this);
2627 auto callback = [weak, menuParams]() {
2628 auto pattern = weak.Upgrade();
2629 CHECK_NULL_VOID(pattern);
2630 CHECK_NULL_VOID(menuParams->onAppear);
2631
2632 auto& textSelector = pattern->textSelector_;
2633 auto selectStart = std::min(textSelector.baseOffset, textSelector.destinationOffset);
2634 auto selectEnd = std::max(textSelector.baseOffset, textSelector.destinationOffset);
2635 menuParams->onAppear(selectStart, selectEnd);
2636 };
2637 selectInfo.menuCallback.onAppear = std::move(callback);
2638 }
2639 selectInfo.menuCallback.onDisappear = menuParams->onDisappear;
2640 }
2641
FireOnSelectionChange(int32_t start,int32_t end)2642 void TextPattern::FireOnSelectionChange(int32_t start, int32_t end)
2643 {
2644 auto host = GetHost();
2645 CHECK_NULL_VOID(host);
2646 auto eventHub = host->GetEventHub<TextEventHub>();
2647 CHECK_NULL_VOID(eventHub);
2648 eventHub->FireOnSelectionChange(start, end);
2649 }
2650
HandleSelectionChange(int32_t start,int32_t end)2651 void TextPattern::HandleSelectionChange(int32_t start, int32_t end)
2652 {
2653 if (textSelector_.GetStart() == start && textSelector_.GetEnd() == end) {
2654 return;
2655 }
2656
2657 UpdateSelectionSpanType(std::min(start, end), std::max(start, end));
2658 textSelector_.Update(start, end);
2659 FireOnSelectionChange(std::min(start, end), std::max(start, end));
2660 }
2661
IsSelectedBindSelectionMenu()2662 bool TextPattern::IsSelectedBindSelectionMenu()
2663 {
2664 auto currentSpanType = selectedType_.value_or(TextSpanType::TEXT);
2665 return GetMenuParams(currentSpanType, TextResponseType::SELECTED_BY_MOUSE) != nullptr;
2666 }
2667
UpdateSelectionSpanType(int32_t selectStart,int32_t selectEnd)2668 void TextPattern::UpdateSelectionSpanType(int32_t selectStart, int32_t selectEnd)
2669 {
2670 UpdateSelectionType(GetSpansInfo(selectStart, selectEnd, GetSpansMethod::ONSELECT));
2671 if ((selectedType_ == TextSpanType::NONE && !textSelector_.StartEqualToDest()) ||
2672 textSelector_.StartEqualToDest()) {
2673 selectedType_ = TextSpanType::TEXT;
2674 }
2675 }
2676
UpdateSelectionType(const SelectionInfo & selection)2677 void TextPattern::UpdateSelectionType(const SelectionInfo& selection)
2678 {
2679 selectedType_ = TextSpanType::NONE;
2680 auto list = selection.GetSelection().resultObjects;
2681 bool imageSelected = false;
2682 bool textSelected = false;
2683 for (const auto& obj : list) {
2684 if (obj.type == SelectSpanType::TYPEIMAGE) {
2685 imageSelected = true;
2686 } else if (obj.type == SelectSpanType::TYPESPAN) {
2687 textSelected = true;
2688 }
2689 if (imageSelected && textSelected) {
2690 selectedType_ = TextSpanType::MIXED;
2691 return;
2692 }
2693 }
2694 if (imageSelected) {
2695 selectedType_ = TextSpanType::IMAGE;
2696 } else if (textSelected) {
2697 selectedType_ = TextSpanType::TEXT;
2698 }
2699
2700 TAG_LOGD(AceLogTag::ACE_TEXT, "UpdateSelectionSpanType, selectedType_: %{public}d", selectedType_.value());
2701 }
2702
GetSelectionSpanItemIndex(const MouseInfo & info)2703 int32_t TextPattern::GetSelectionSpanItemIndex(const MouseInfo& info)
2704 {
2705 RectF textContentRect = contentRect_;
2706 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
2707 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
2708 PointF textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
2709 info.GetLocalLocation().GetY() - textContentRect.GetY() };
2710 if (!textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) ||
2711 spans_.empty() || !paragraph_) {
2712 return -1;
2713 }
2714 int32_t start = 0;
2715 bool isFind = false;
2716 int32_t index = -1;
2717 for (const auto& item : spans_) {
2718 index++;
2719 if (!item) {
2720 continue;
2721 }
2722 std::vector<RectF> selectedRects;
2723 paragraph_->GetRectsForRange(start, item->position, selectedRects);
2724 start = item->position;
2725 for (auto&& rect : selectedRects) {
2726 if (rect.IsInRegion(textOffset)) {
2727 isFind = true;
2728 break;
2729 }
2730 }
2731 if (isFind) {
2732 TAG_LOGD(AceLogTag::ACE_TEXT, "GetSelectionSpanItemIndex index: %{public}d", index);
2733 return index;
2734 }
2735 }
2736 return -1;
2737 }
2738
GetBuilderResultObject(RefPtr<UINode> uiNode,int32_t index,int32_t start,int32_t end)2739 ResultObject TextPattern::GetBuilderResultObject(RefPtr<UINode> uiNode, int32_t index, int32_t start, int32_t end)
2740 {
2741 int32_t itemLength = 1;
2742 ResultObject resultObject;
2743 resultObject.isDraggable = false;
2744 if (!DynamicCast<FrameNode>(uiNode) || !GetSpanItemByIndex(index)) {
2745 return resultObject;
2746 }
2747 int32_t endPosition = std::min(GetTextContentLength(), GetSpanItemByIndex(index)->position);
2748 int32_t startPosition = endPosition - itemLength;
2749 if ((start <= startPosition) && (end >= endPosition)) {
2750 auto builderNode = DynamicCast<FrameNode>(uiNode);
2751 CHECK_NULL_RETURN(builderNode, resultObject);
2752 resultObject.spanPosition.spanIndex = index;
2753 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGESTART] = startPosition;
2754 resultObject.spanPosition.spanRange[RichEditorSpanRange::RANGEEND] = endPosition;
2755 resultObject.offsetInSpan[RichEditorSpanRange::RANGESTART] = 0;
2756 resultObject.offsetInSpan[RichEditorSpanRange::RANGEEND] = itemLength;
2757 resultObject.type = SelectSpanType::TYPEIMAGE;
2758 auto geometryNode = builderNode->GetGeometryNode();
2759 CHECK_NULL_RETURN(geometryNode, resultObject);
2760 resultObject.imageStyle.size[RichEditorImageSize::SIZEWIDTH] = geometryNode->GetMarginFrameSize().Width();
2761 resultObject.imageStyle.size[RichEditorImageSize::SIZEHEIGHT] = geometryNode->GetMarginFrameSize().Height();
2762 resultObject.valueString = " ";
2763 }
2764 return resultObject;
2765 }
2766 } // namespace OHOS::Ace::NG
2767