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 <stack>
19
20 #include "base/geometry/ng/offset_t.h"
21 #include "base/geometry/ng/rect_t.h"
22 #include "base/geometry/offset.h"
23 #include "base/log/dump_log.h"
24 #include "base/utils/utils.h"
25 #include "base/window/drag_window.h"
26 #include "core/common/font_manager.h"
27 #include "core/components_ng/base/ui_node.h"
28 #include "core/components_ng/event/gesture_event_hub.h"
29 #include "core/components_ng/event/long_press_event.h"
30 #include "core/components_ng/manager/select_overlay/select_overlay_manager.h"
31 #include "core/components_ng/pattern/select_overlay/select_overlay_property.h"
32 #include "core/components_ng/pattern/text/text_layout_algorithm.h"
33 #include "core/components_ng/pattern/text/text_layout_property.h"
34 #include "core/components_ng/pattern/text_drag/text_drag_pattern.h"
35 #include "core/components_ng/property/property.h"
36 #include "core/gestures/gesture_info.h"
37
38 #ifdef ENABLE_DRAG_FRAMEWORK
39 #include "core/common/ace_engine_ext.h"
40 #include "core/common/udmf/udmf_client.h"
41 #endif
42
43 namespace OHOS::Ace::NG {
44 namespace {
45 constexpr int32_t API_PROTEXTION_GREATER_NINE = 9;
46 // uncertainty range when comparing selectedTextBox to contentRect
47 constexpr float BOX_EPSILON = 0.5f;
48 }; // namespace
49
OnAttachToFrameNode()50 void TextPattern::OnAttachToFrameNode()
51 {
52 auto pipeline = PipelineContext::GetCurrentContext();
53 CHECK_NULL_VOID_NOLOG(pipeline);
54 auto host = GetHost();
55 CHECK_NULL_VOID_NOLOG(host);
56 if (pipeline->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE) {
57 host->GetRenderContext()->UpdateClipEdge(true);
58 }
59 InitSurfaceChangedCallback();
60 InitSurfacePositionChangedCallback();
61 auto theme = pipeline->GetTheme<TextTheme>();
62 CHECK_NULL_VOID_NOLOG(theme);
63 host->SetDraggable(theme->GetDraggable());
64 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
65 textLayoutProperty->UpdateTextAlign(TextAlign::START);
66 textLayoutProperty->UpdateAlignment(Alignment::CENTER_LEFT);
67 }
68
OnDetachFromFrameNode(FrameNode * node)69 void TextPattern::OnDetachFromFrameNode(FrameNode* node)
70 {
71 CloseSelectOverlay();
72 auto pipeline = PipelineContext::GetCurrentContext();
73 CHECK_NULL_VOID(pipeline);
74 if (HasSurfaceChangedCallback()) {
75 LOGD("Unregister surface change callback with id %{public}d", surfaceChangedCallbackId_.value_or(-1));
76 pipeline->UnregisterSurfaceChangedCallback(surfaceChangedCallbackId_.value_or(-1));
77 }
78 if (HasSurfacePositionChangedCallback()) {
79 LOGD("Unregister surface position change callback with id %{public}d",
80 surfacePositionChangedCallbackId_.value_or(-1));
81 pipeline->UnregisterSurfacePositionChangedCallback(surfacePositionChangedCallbackId_.value_or(-1));
82 }
83 auto frameNode = WeakClaim(node);
84 pipeline->RemoveFontNodeNG(frameNode);
85 auto fontManager = pipeline->GetFontManager();
86 if (fontManager) {
87 fontManager->UnRegisterCallbackNG(frameNode);
88 fontManager->RemoveVariationNodeNG(frameNode);
89 }
90 }
91
CloseSelectOverlay()92 void TextPattern::CloseSelectOverlay()
93 {
94 CloseSelectOverlay(false);
95 }
96
CloseSelectOverlay(bool animation)97 void TextPattern::CloseSelectOverlay(bool animation)
98 {
99 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
100 selectOverlayProxy_->Close(animation);
101 }
102 }
103
ResetSelection()104 void TextPattern::ResetSelection()
105 {
106 if (textSelector_.IsValid()) {
107 textSelector_.Update(-1, -1);
108 auto host = GetHost();
109 CHECK_NULL_VOID(host);
110 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
111 }
112 }
113
GetGraphemeClusterLength(int32_t extend) const114 int32_t TextPattern::GetGraphemeClusterLength(int32_t extend) const
115 {
116 auto text = textForDisplay_;
117 char16_t aroundChar = 0;
118
119 if (static_cast<size_t>(extend) < (text.length())) {
120 aroundChar = text[std::min(static_cast<int32_t>(text.length() - 1), extend)];
121 }
122 return StringUtils::NotInUtf16Bmp(aroundChar) ? 2 : 1;
123 }
124
InitSelection(const Offset & pos)125 void TextPattern::InitSelection(const Offset& pos)
126 {
127 CHECK_NULL_VOID(paragraph_);
128 int32_t extend = paragraph_->GetHandlePositionForClick(pos);
129 int32_t start = 0;
130 int32_t end = 0;
131 if (!paragraph_->GetWordBoundary(extend, start, end)) {
132 start = extend;
133 end = std::min(
134 static_cast<int32_t>(GetWideText().length()) + imageCount_, extend + GetGraphemeClusterLength(extend));
135 }
136 textSelector_.Update(start, end);
137 }
138
CalcCursorOffsetByPosition(int32_t position,float & selectLineHeight)139 OffsetF TextPattern::CalcCursorOffsetByPosition(int32_t position, float& selectLineHeight)
140 {
141 auto host = GetHost();
142 CHECK_NULL_RETURN(host, OffsetF(0.0f, 0.0f));
143 auto rect = host->GetGeometryNode()->GetFrameRect();
144 CHECK_NULL_RETURN(paragraph_, OffsetF(0.0f, 0.0f));
145 CaretMetrics metrics;
146 auto computeSuccess = paragraph_->ComputeOffsetForCaretUpstream(position, metrics) ||
147 paragraph_->ComputeOffsetForCaretDownstream(position, metrics);
148 if (!computeSuccess) {
149 LOGW("Get caret offset failed, set it to text tail");
150 return { rect.Width(), 0.0f };
151 }
152 selectLineHeight = metrics.height;
153 return { static_cast<float>(metrics.offset.GetX()), static_cast<float>(metrics.offset.GetY()) };
154 }
155
CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)156 void TextPattern::CalculateHandleOffsetAndShowOverlay(bool isUsingMouse)
157 {
158 auto host = GetHost();
159 CHECK_NULL_VOID(host);
160 auto pipeline = host->GetContext();
161 CHECK_NULL_VOID(pipeline);
162 auto rootOffset = pipeline->GetRootRect().GetOffset();
163 auto offset = host->GetPaintRectOffset() + contentRect_.GetOffset();
164 auto textPaintOffset = offset - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
165
166 // calculate firstHandleOffset, secondHandleOffset and handlePaintSize
167 float startSelectHeight = 0.0f;
168 float endSelectHeight = 0.0f;
169 auto startOffset = CalcCursorOffsetByPosition(textSelector_.baseOffset, startSelectHeight);
170 auto endOffset = CalcCursorOffsetByPosition(textSelector_.destinationOffset, endSelectHeight);
171 OffsetF firstHandleOffset = startOffset + textPaintOffset - rootOffset;
172 OffsetF secondHandleOffset = endOffset + textPaintOffset - rootOffset;
173
174 textSelector_.selectionBaseOffset = firstHandleOffset;
175 textSelector_.selectionDestinationOffset = secondHandleOffset;
176
177 RectF firstHandle;
178 firstHandle.SetOffset(firstHandleOffset);
179 firstHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), startSelectHeight });
180 textSelector_.firstHandle = firstHandle;
181
182 RectF secondHandle;
183 secondHandle.SetOffset(secondHandleOffset);
184 secondHandle.SetSize({ SelectHandleInfo::GetDefaultLineWidth().ConvertToPx(), endSelectHeight });
185 secondHandle.SetHeight(endSelectHeight);
186 textSelector_.secondHandle = secondHandle;
187 }
188
HandleLongPress(GestureEvent & info)189 void TextPattern::HandleLongPress(GestureEvent& info)
190 {
191 if (copyOption_ == CopyOptions::None || isMousePressed_) {
192 return;
193 }
194 auto host = GetHost();
195 CHECK_NULL_VOID(host);
196 auto hub = host->GetEventHub<EventHub>();
197 CHECK_NULL_VOID(hub);
198 auto gestureHub = hub->GetOrCreateGestureEventHub();
199 CHECK_NULL_VOID(gestureHub);
200 if (IsDraggable(info.GetLocalLocation())) {
201 dragBoxes_ = GetTextBoxes();
202 // prevent long press event from being triggered when dragging
203 #ifdef ENABLE_DRAG_FRAMEWORK
204 gestureHub->SetIsTextDraggable(true);
205 #endif
206 return;
207 }
208 #ifdef ENABLE_DRAG_FRAMEWORK
209 gestureHub->SetIsTextDraggable(false);
210 #endif
211 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
212 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
213 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
214 InitSelection(textOffset);
215 CalculateHandleOffsetAndShowOverlay();
216 CloseSelectOverlay(true);
217 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
218 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
219 }
220
OnHandleMove(const RectF & handleRect,bool isFirstHandle)221 void TextPattern::OnHandleMove(const RectF& handleRect, bool isFirstHandle)
222 {
223 auto host = GetHost();
224 CHECK_NULL_VOID(host);
225 auto pipeline = host->GetContext();
226 CHECK_NULL_VOID(pipeline);
227 auto rootOffset = pipeline->GetRootRect().GetOffset();
228 auto offset = host->GetPaintRectOffset() + contentRect_.GetOffset() - rootOffset;
229 auto textPaintOffset = offset - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
230
231 auto localOffsetX = handleRect.GetX();
232 auto localOffsetY = handleRect.GetY();
233
234 if (localOffsetX < offset.GetX()) {
235 localOffsetX = offset.GetX();
236 } else if (GreatOrEqual(localOffsetX, offset.GetX() + contentRect_.Width())) {
237 localOffsetX = offset.GetX() + contentRect_.Width();
238 }
239
240 if (localOffsetY < offset.GetY()) {
241 localOffsetY = offset.GetY();
242 } else if (GreatNotEqual(localOffsetY, offset.GetY() + contentRect_.Height())) {
243 localOffsetY = offset.GetY() + contentRect_.Height();
244 }
245
246 localOffsetX = localOffsetX - textPaintOffset.GetX();
247 localOffsetY = localOffsetY - textPaintOffset.GetY();
248
249 CHECK_NULL_VOID(paragraph_);
250 // the handle position is calculated based on the middle of the handle height.
251 if (isFirstHandle) {
252 auto start =
253 paragraph_->GetHandlePositionForClick(Offset(localOffsetX, localOffsetY + handleRect.Height() / 2));
254 textSelector_.Update(start, textSelector_.destinationOffset);
255 } else {
256 auto end = paragraph_->GetHandlePositionForClick(Offset(localOffsetX, localOffsetY + handleRect.Height() / 2));
257 textSelector_.Update(textSelector_.baseOffset, end);
258 }
259 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
260
261 CHECK_NULL_VOID_NOLOG(selectOverlayProxy_);
262 auto start = textSelector_.GetTextStart();
263 auto end = textSelector_.GetTextEnd();
264 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
265 }
266
OnHandleMoveDone(const RectF & handleRect,bool isFirstHandle)267 void TextPattern::OnHandleMoveDone(const RectF& handleRect, bool isFirstHandle)
268 {
269 CalculateHandleOffsetAndShowOverlay();
270 if (selectOverlayProxy_) {
271 SelectHandleInfo handleInfo;
272 if (isFirstHandle) {
273 handleInfo.paintRect = textSelector_.firstHandle;
274 selectOverlayProxy_->UpdateFirstSelectHandleInfo(handleInfo);
275 } else {
276 handleInfo.paintRect = textSelector_.secondHandle;
277 selectOverlayProxy_->UpdateSecondSelectHandleInfo(handleInfo);
278 }
279 if (IsSelectAll() && selectMenuInfo_.showCopyAll == true) {
280 selectMenuInfo_.showCopyAll = false;
281 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
282 } else if (!IsSelectAll() && selectMenuInfo_.showCopyAll == false) {
283 selectMenuInfo_.showCopyAll = true;
284 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
285 }
286 return;
287 }
288 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
289 auto host = GetHost();
290 CHECK_NULL_VOID(host);
291 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
292 }
293
IsSelectAll()294 bool TextPattern::IsSelectAll()
295 {
296 return textSelector_.GetTextStart() == 0 &&
297 textSelector_.GetTextEnd() == static_cast<int32_t>(GetWideText().length()) + imageCount_;
298 }
GetWideText() const299 std::wstring TextPattern::GetWideText() const
300 {
301 return StringUtils::ToWstring(textForDisplay_);
302 }
303
GetSelectedText(int32_t start,int32_t end) const304 std::string TextPattern::GetSelectedText(int32_t start, int32_t end) const
305 {
306 auto wideText = GetWideText();
307 auto min = std::clamp(std::max(std::min(start, end), 0), 0, static_cast<int32_t>(wideText.length()));
308 auto max = std::clamp(std::min(std::max(start, end), static_cast<int32_t>(wideText.length())), 0,
309 static_cast<int32_t>(wideText.length()));
310 return StringUtils::ToString(wideText.substr(min, max - min));
311 }
312
HandleOnCopy()313 void TextPattern::HandleOnCopy()
314 {
315 CHECK_NULL_VOID(clipboard_);
316 if (textSelector_.IsValid() && textSelector_.GetTextStart() == textSelector_.GetTextEnd()) {
317 textSelector_.Update(-1, -1);
318 LOGW("Nothing to select");
319 return;
320 }
321 auto value = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
322 if (value.empty()) {
323 textSelector_.Update(-1, -1);
324 LOGW("Copy value is empty");
325 return;
326 }
327 if (copyOption_ != CopyOptions::None) {
328 clipboard_->SetData(value, copyOption_);
329 }
330 ResetSelection();
331 }
332
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle)333 void TextPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle)
334 {
335 ShowSelectOverlay(firstHandle, secondHandle, false);
336 }
ShowSelectOverlay(const RectF & firstHandle,const RectF & secondHandle,bool animation)337 void TextPattern::ShowSelectOverlay(const RectF& firstHandle, const RectF& secondHandle, bool animation)
338 {
339 SelectOverlayInfo selectInfo;
340 selectInfo.firstHandle.paintRect = firstHandle;
341 selectInfo.secondHandle.paintRect = secondHandle;
342 CheckHandles(selectInfo.secondHandle);
343 selectInfo.onHandleMove = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
344 auto pattern = weak.Upgrade();
345 CHECK_NULL_VOID(pattern);
346 pattern->OnHandleMove(handleRect, isFirst);
347 };
348 selectInfo.onHandleMoveDone = [weak = WeakClaim(this)](const RectF& handleRect, bool isFirst) {
349 auto pattern = weak.Upgrade();
350 CHECK_NULL_VOID(pattern);
351 pattern->OnHandleMoveDone(handleRect, isFirst);
352 };
353 selectInfo.menuInfo.menuIsShow = true;
354 selectInfo.menuInfo.showCut = false;
355 selectInfo.menuInfo.showPaste = false;
356 selectInfo.menuCallback.onCopy = [weak = WeakClaim(this)]() {
357 auto pattern = weak.Upgrade();
358 CHECK_NULL_VOID(pattern);
359 pattern->HandleOnCopy();
360 };
361 selectInfo.menuCallback.onSelectAll = [weak = WeakClaim(this)]() {
362 auto pattern = weak.Upgrade();
363 CHECK_NULL_VOID(pattern);
364 pattern->HandleOnSelectAll();
365 };
366 selectInfo.onClose = [weak = WeakClaim(this)](bool closedByGlobalEvent) {
367 auto pattern = weak.Upgrade();
368 CHECK_NULL_VOID(pattern);
369 if (closedByGlobalEvent) {
370 pattern->ResetSelection();
371 }
372 };
373
374 if (!menuOptionItems_.empty()) {
375 selectInfo.menuOptionItems = GetMenuOptionItems();
376 }
377 selectMenuInfo_ = selectInfo.menuInfo;
378 UpdateSelectOverlayOrCreate(selectInfo, animation);
379 }
380
HandleOnSelectAll()381 void TextPattern::HandleOnSelectAll()
382 {
383 auto textSize = GetWideText().length() + imageCount_;
384 textSelector_.Update(0, textSize);
385 CalculateHandleOffsetAndShowOverlay();
386 CloseSelectOverlay(true);
387 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
388 selectMenuInfo_.showCopyAll = false;
389 selectOverlayProxy_->UpdateSelectMenuInfo(selectMenuInfo_);
390 auto host = GetHost();
391 CHECK_NULL_VOID(host);
392 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
393 }
394
CheckHandles(SelectHandleInfo & handleInfo)395 void TextPattern::CheckHandles(SelectHandleInfo& handleInfo)
396 {
397 auto frameNode = GetHost();
398 CHECK_NULL_VOID(frameNode);
399 auto renderContext = frameNode->GetRenderContext();
400 CHECK_NULL_VOID(renderContext);
401 if (renderContext->GetClipEdge().value_or(true) == false) {
402 return;
403 }
404
405 auto host = GetHost();
406 CHECK_NULL_VOID(host);
407 auto pipeline = host->GetContext();
408 CHECK_NULL_VOID(pipeline);
409 auto offset = host->GetPaintRectOffset() + contentRect_.GetOffset();
410 RectF contentGlobalRect(offset, contentRect_.GetSize());
411 auto handleOffset = handleInfo.paintRect.GetOffset();
412 if (!contentGlobalRect.IsInRegion(PointF(handleOffset.GetX(), handleOffset.GetY() + BOX_EPSILON))) {
413 handleInfo.isShow = false;
414 }
415 }
416
InitLongPressEvent(const RefPtr<GestureEventHub> & gestureHub)417 void TextPattern::InitLongPressEvent(const RefPtr<GestureEventHub>& gestureHub)
418 {
419 CHECK_NULL_VOID_NOLOG(!longPressEvent_);
420 auto longPressCallback = [weak = WeakClaim(this)](GestureEvent& info) {
421 auto pattern = weak.Upgrade();
422 CHECK_NULL_VOID(pattern);
423 pattern->HandleLongPress(info);
424 };
425 longPressEvent_ = MakeRefPtr<LongPressEvent>(std::move(longPressCallback));
426
427 constexpr int32_t longPressDelay = 600;
428 // Default time is 500, used by drag event. Drag event would trigger if text is selected, but we want
429 // it to only trigger on the second long press, after selection. Therefore, long press delay of Selection needs to
430 // be slightly longer to ensure that order.
431 gestureHub->SetLongPressEvent(longPressEvent_, false, false, longPressDelay);
432
433 auto onTextSelectorChange = [weak = WeakClaim(this)]() {
434 auto pattern = weak.Upgrade();
435 CHECK_NULL_VOID(pattern);
436 auto frameNode = pattern->GetHost();
437 CHECK_NULL_VOID(frameNode);
438 frameNode->OnAccessibilityEvent(AccessibilityEventType::TEXT_SELECTION_UPDATE);
439 };
440 textSelector_.SetOnAccessibility(std::move(onTextSelectorChange));
441 }
442
OnHandleTouchUp()443 void TextPattern::OnHandleTouchUp()
444 {
445 CloseSelectOverlay();
446 ResetSelection();
447 }
448
HandleClickEvent(GestureEvent & info)449 void TextPattern::HandleClickEvent(GestureEvent& info)
450 {
451 if (textSelector_.IsValid()) {
452 CloseSelectOverlay(true);
453 ResetSelection();
454 }
455
456 RectF textContentRect = contentRect_;
457 textContentRect.SetTop(contentRect_.GetY() - std::min(baselineOffset_, 0.0f));
458 textContentRect.SetHeight(contentRect_.Height() - std::max(baselineOffset_, 0.0f));
459 bool isClickOnSpan = false;
460 if (textContentRect.IsInRegion(PointF(info.GetLocalLocation().GetX(), info.GetLocalLocation().GetY())) &&
461 !spanItemChildren_.empty() && paragraph_) {
462 Offset textOffset = { info.GetLocalLocation().GetX() - textContentRect.GetX(),
463 info.GetLocalLocation().GetY() - textContentRect.GetY() };
464 auto position = paragraph_->GetHandlePositionForClick(textOffset);
465 for (const auto& item : spanItemChildren_) {
466 if (item && position < item->position) {
467 if (!item->onClick) {
468 break;
469 }
470 GestureEvent spanClickinfo = info;
471 EventTarget target = info.GetTarget();
472 target.area.SetWidth(Dimension(0.0f));
473 target.area.SetHeight(Dimension(0.0f));
474 spanClickinfo.SetTarget(target);
475 item->onClick(spanClickinfo);
476 isClickOnSpan = true;
477 break;
478 }
479 }
480 }
481 if (onClick_ && !isClickOnSpan) {
482 onClick_(info);
483 }
484 }
485
InitClickEvent(const RefPtr<GestureEventHub> & gestureHub)486 void TextPattern::InitClickEvent(const RefPtr<GestureEventHub>& gestureHub)
487 {
488 CHECK_NULL_VOID_NOLOG(!clickEventInitialized_);
489 auto clickCallback = [weak = WeakClaim(this)](GestureEvent& info) {
490 auto pattern = weak.Upgrade();
491 CHECK_NULL_VOID(pattern);
492 pattern->HandleClickEvent(info);
493 };
494
495 auto clickListener = MakeRefPtr<ClickEvent>(std::move(clickCallback));
496 gestureHub->AddClickEvent(clickListener);
497 clickEventInitialized_ = true;
498 }
499
InitMouseEvent()500 void TextPattern::InitMouseEvent()
501 {
502 CHECK_NULL_VOID_NOLOG(!mouseEventInitialized_);
503 auto host = GetHost();
504 CHECK_NULL_VOID(host);
505 auto eventHub = host->GetEventHub<EventHub>();
506 CHECK_NULL_VOID(eventHub);
507 auto inputHub = eventHub->GetOrCreateInputEventHub();
508 CHECK_NULL_VOID(inputHub);
509
510 auto mouseTask = [weak = WeakClaim(this)](MouseInfo& info) {
511 auto pattern = weak.Upgrade();
512 CHECK_NULL_VOID_NOLOG(pattern);
513 pattern->HandleMouseEvent(info);
514 };
515 auto mouseEvent = MakeRefPtr<InputEvent>(std::move(mouseTask));
516 inputHub->AddOnMouseEvent(mouseEvent);
517 mouseEventInitialized_ = true;
518 }
519
HandleMouseEvent(const MouseInfo & info)520 void TextPattern::HandleMouseEvent(const MouseInfo& info)
521 {
522 if (copyOption_ == CopyOptions::None) {
523 return;
524 }
525 if (info.GetButton() == MouseButton::RIGHT_BUTTON && info.GetAction() == MouseAction::PRESS) {
526 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
527 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
528 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
529 InitSelection(textOffset);
530 CalculateHandleOffsetAndShowOverlay(true);
531 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle, true);
532 auto host = GetHost();
533 CHECK_NULL_VOID(host);
534 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
535 return;
536 }
537 if (info.GetButton() == MouseButton::LEFT_BUTTON) {
538 if (info.GetAction() == MouseAction::PRESS) {
539 isMousePressed_ = true;
540 if (BetweenSelectedPosition(info.GetGlobalLocation())) {
541 blockPress_ = true;
542 return;
543 }
544 mouseStatus_ = MouseStatus::PRESSED;
545 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
546 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
547 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
548 CHECK_NULL_VOID(paragraph_);
549 auto start = paragraph_->GetHandlePositionForClick(textOffset);
550 textSelector_.Update(start, start);
551 }
552
553 if (info.GetAction() == MouseAction::MOVE) {
554 if (blockPress_) {
555 return;
556 }
557 mouseStatus_ = MouseStatus::MOVE;
558 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
559 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
560 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
561 CHECK_NULL_VOID(paragraph_);
562 auto end = paragraph_->GetHandlePositionForClick(textOffset);
563 textSelector_.Update(textSelector_.baseOffset, end);
564 }
565
566 if (info.GetAction() == MouseAction::RELEASE) {
567 if (blockPress_) {
568 blockPress_ = false;
569 }
570 mouseStatus_ = MouseStatus::RELEASED;
571 auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f));
572 Offset textOffset = { info.GetLocalLocation().GetX() - textPaintOffset.GetX(),
573 info.GetLocalLocation().GetY() - textPaintOffset.GetY() };
574 CHECK_NULL_VOID(paragraph_);
575 auto end = paragraph_->GetHandlePositionForClick(textOffset);
576 textSelector_.Update(textSelector_.baseOffset, end);
577 isMousePressed_ = false;
578 }
579 }
580 auto host = GetHost();
581 CHECK_NULL_VOID(host);
582 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
583 }
584
InitTouchEvent()585 void TextPattern::InitTouchEvent()
586 {
587 CHECK_NULL_VOID_NOLOG(!touchEventInitialized_);
588 auto host = GetHost();
589 CHECK_NULL_VOID(host);
590 auto gesture = host->GetOrCreateGestureEventHub();
591 CHECK_NULL_VOID(gesture);
592
593 auto touchTask = [weak = WeakClaim(this)](const TouchEventInfo& info) {
594 auto pattern = weak.Upgrade();
595 CHECK_NULL_VOID_NOLOG(pattern);
596 pattern->HandleTouchEvent(info);
597 };
598 auto touchListener_ = MakeRefPtr<TouchEventImpl>(std::move(touchTask));
599 gesture->AddTouchEvent(touchListener_);
600 touchEventInitialized_ = true;
601 }
602
HandleTouchEvent(const TouchEventInfo & info)603 void TextPattern::HandleTouchEvent(const TouchEventInfo& info)
604 {
605 return;
606 }
607
InitPanEvent(const RefPtr<GestureEventHub> & gestureHub)608 void TextPattern::InitPanEvent(const RefPtr<GestureEventHub>& gestureHub)
609 {
610 CHECK_NULL_VOID_NOLOG(!panEventInitialized_);
611 auto actionStartTask = [weak = WeakClaim(this)](const GestureEvent& info) {
612 auto pattern = weak.Upgrade();
613 CHECK_NULL_VOID_NOLOG(pattern);
614 pattern->HandlePanStart(info);
615 };
616 auto actionUpdateTask = [weak = WeakClaim(this)](const GestureEvent& info) {
617 auto pattern = weak.Upgrade();
618 CHECK_NULL_VOID_NOLOG(pattern);
619 pattern->HandlePanUpdate(info);
620 };
621 auto actionEndTask = [weak = WeakClaim(this)](const GestureEvent& info) {
622 auto pattern = weak.Upgrade();
623 CHECK_NULL_VOID_NOLOG(pattern);
624 pattern->HandlePanEnd(info);
625 };
626 auto actionCancelTask = [weak = WeakClaim(this)]() {};
627 auto panEvent = MakeRefPtr<PanEvent>(
628 std::move(actionStartTask), std::move(actionUpdateTask), std::move(actionEndTask), std::move(actionCancelTask));
629
630 PanDirection panDirection;
631 panDirection.type = PanDirection::ALL;
632 gestureHub->AddPanEvent(panEvent, panDirection, DEFAULT_PAN_FINGER, DEFAULT_PAN_DISTANCE);
633 panEventInitialized_ = true;
634 }
635
HandlePanStart(const GestureEvent & info)636 void TextPattern::HandlePanStart(const GestureEvent& info)
637 {
638 auto host = GetHost();
639 CHECK_NULL_VOID(host);
640 auto offset = info.GetLocalLocation();
641 if (!IsDraggable(offset)) {
642 return;
643 }
644 auto pipelineContext = host->GetContext();
645 CHECK_NULL_VOID(pipelineContext);
646
647 #if !defined(PREVIEW)
648 if (!dragWindow_) {
649 auto rect = pipelineContext->GetCurrentWindowRect();
650 auto initTextPattern = AceType::Claim(this);
651
652 // create textdrag window
653 dragWindow_ = DragWindow::CreateTextDragWindow("APP_DRAG_WINDOW",
654 static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
655 static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()),
656 static_cast<int32_t>(contentRect_.Width() + contentRect_.GetX()),
657 contentRect_.Height() + contentRect_.GetY());
658 if (dragWindow_) {
659 dragWindow_->SetOffset(static_cast<int32_t>(host->GetPaintRectOffset().GetX() + rect.Left()),
660 static_cast<int32_t>(host->GetPaintRectOffset().GetY() + rect.Top()));
661 // draw select text on drag window
662 dragWindow_->DrawTextNG(paragraph_, initTextPattern);
663 // add select data to clipboard
664 auto manager = pipelineContext->GetDragDropManager();
665 CHECK_NULL_VOID(manager);
666 dragDropProxy_ = manager->CreateTextDragDropProxy();
667 CHECK_NULL_VOID(dragDropProxy_);
668 dragDropProxy_->OnTextDragStart(GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd()));
669 }
670 }
671 #endif
672 }
673
IsDraggable(const Offset & offset)674 bool TextPattern::IsDraggable(const Offset& offset)
675 {
676 auto host = GetHost();
677 CHECK_NULL_RETURN(host, false);
678 auto eventHub = host->GetEventHub<EventHub>();
679 bool draggable = eventHub->HasOnDragStart();
680 if (copyOption_ != CopyOptions::None && draggable &&
681 GreatNotEqual(textSelector_.GetTextEnd(), textSelector_.GetTextStart())) {
682 // Determine if the pan location is in the selected area
683 std::vector<Rect> selectedRects;
684 paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
685 auto panOffset = OffsetF(offset.GetX(), offset.GetY()) - contentRect_.GetOffset() +
686 OffsetF(0.0, std::min(baselineOffset_, 0.0f));
687 for (const auto& selectedRect : selectedRects) {
688 if (selectedRect.IsInRegion(Point(panOffset.GetX(), panOffset.GetY()))) {
689 return true;
690 }
691 }
692 }
693 return false;
694 }
695
HandlePanUpdate(const GestureEvent & info)696 void TextPattern::HandlePanUpdate(const GestureEvent& info)
697 {
698 if (dragWindow_) {
699 if (dragWindow_) {
700 dragWindow_->TextDragWindowMove(info.GetOffsetX(), info.GetOffsetY());
701 }
702 return;
703 }
704 }
705
HandlePanEnd(const GestureEvent & info)706 void TextPattern::HandlePanEnd(const GestureEvent& info)
707 {
708 if (dragWindow_) {
709 dragWindow_->Destroy();
710 dragWindow_ = nullptr;
711 if (dragDropProxy_) {
712 dragDropProxy_->OnDragEnd(info, true);
713 }
714 return;
715 }
716 }
717
718 #ifdef ENABLE_DRAG_FRAMEWORK
OnDragStart(const RefPtr<Ace::DragEvent> & event,const std::string & extraParams)719 DragDropInfo TextPattern::OnDragStart(const RefPtr<Ace::DragEvent>& event, const std::string& extraParams)
720 {
721 auto host = GetHost();
722 CHECK_NULL_RETURN(host, {});
723 CHECK_NULL_RETURN(dragNodeWk_.Upgrade(), {});
724
725 DragDropInfo itemInfo;
726 auto selectedStr = GetSelectedText(textSelector_.GetTextStart(), textSelector_.GetTextEnd());
727 itemInfo.extraInfo = selectedStr;
728 RefPtr<UnifiedData> unifiedData = UdmfClient::GetInstance()->CreateUnifiedData();
729 UdmfClient::GetInstance()->AddPlainTextRecord(unifiedData, selectedStr);
730 event->SetData(unifiedData);
731
732 AceEngineExt::GetInstance().DragStartExt();
733
734 CloseSelectOverlay();
735 ResetSelection();
736 return itemInfo;
737 }
738
InitDragEvent()739 void TextPattern::InitDragEvent()
740 {
741 auto host = GetHost();
742 CHECK_NULL_VOID(host);
743 auto eventHub = host->GetEventHub<EventHub>();
744 CHECK_NULL_VOID(eventHub);
745 if (eventHub->HasOnDragStart()) {
746 LOGD("drag event has already been initialized");
747 return;
748 }
749
750 auto gestureHub = host->GetOrCreateGestureEventHub();
751 gestureHub->InitDragDropEvent();
752 gestureHub->SetTextDraggable(true);
753 gestureHub->SetThumbnailCallback(GetThumbnailCallback());
754 auto onDragStart = [weakPtr = WeakClaim(this)](
755 const RefPtr<Ace::DragEvent>& event, const std::string& extraParams) -> DragDropInfo {
756 auto pattern = weakPtr.Upgrade();
757 CHECK_NULL_RETURN(pattern, {});
758 return pattern->OnDragStart(event, extraParams);
759 };
760 if (!eventHub->HasOnDragStart()) {
761 eventHub->SetOnDragStart(std::move(onDragStart));
762 }
763 }
764
GetThumbnailCallback()765 std::function<void(Offset)> TextPattern::GetThumbnailCallback()
766 {
767 return [wk = WeakClaim(this)](const Offset& point) {
768 auto pattern = wk.Upgrade();
769 CHECK_NULL_VOID(pattern);
770 if (pattern->BetweenSelectedPosition(point)) {
771 pattern->dragNode_ = TextDragPattern::CreateDragNode(pattern->GetHost());
772 pattern->dragNodeWk_ = pattern->dragNode_;
773 FrameNode::ProcessOffscreenNode(pattern->dragNode_);
774 }
775 };
776 }
777 #endif // ENABLE_DRAG_FRAMEWORK
778
779 // ===========================================================
780 // TextDragBase implementations
GetLineHeight() const781 float TextPattern::GetLineHeight() const
782 {
783 std::vector<Rect> selectedRects;
784 paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
785 CHECK_NULL_RETURN(selectedRects.size(), {});
786 return selectedRects.front().Height();
787 }
788
ConvertRect(const Rect & rect)789 RSTypographyProperties::TextBox TextPattern::ConvertRect(const Rect& rect)
790 {
791 return { RSRect(rect.Left(), rect.Top(), rect.Right(), rect.Bottom()), RSTextDirection::LTR };
792 }
793
GetTextBoxes()794 std::vector<RSTypographyProperties::TextBox> TextPattern::GetTextBoxes()
795 {
796 std::vector<Rect> selectedRects;
797 paragraph_->GetRectsForRange(textSelector_.GetTextStart(), textSelector_.GetTextEnd(), selectedRects);
798 std::vector<RSTypographyProperties::TextBox> res;
799 res.reserve(selectedRects.size());
800 for (auto&& rect : selectedRects) {
801 res.emplace_back(ConvertRect(rect));
802 }
803 return res;
804 }
805
GetParentGlobalOffset() const806 OffsetF TextPattern::GetParentGlobalOffset() const
807 {
808 auto host = GetHost();
809 CHECK_NULL_RETURN(host, {});
810 return host->GetPaintRectOffset();
811 }
812
CreateHandles()813 void TextPattern::CreateHandles()
814 {
815 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
816 }
817
BetweenSelectedPosition(const Offset & globalOffset)818 bool TextPattern::BetweenSelectedPosition(const Offset& globalOffset)
819 {
820 auto host = GetHost();
821 CHECK_NULL_RETURN(host, false);
822 auto offset = host->GetPaintRectOffset();
823 auto localOffset = globalOffset - Offset(offset.GetX(), offset.GetY());
824 return IsDraggable(localOffset);
825 }
826
827 // end of TextDragBase implementations
828 // ===========================================================
829
OnModifyDone()830 void TextPattern::OnModifyDone()
831 {
832 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
833 CHECK_NULL_VOID(textLayoutProperty);
834 auto host = GetHost();
835 CHECK_NULL_VOID(host);
836 auto renderContext = host->GetRenderContext();
837 CHECK_NULL_VOID(renderContext);
838
839 if (CheckNeedMeasure(textLayoutProperty->GetPropertyChangeFlag())) {
840 // measure flag changed, reset paragraph.
841 LOGD("reset on modify done!");
842 paragraph_.Reset();
843 }
844
845 if (!(PipelineContext::GetCurrentContext() &&
846 PipelineContext::GetCurrentContext()->GetMinPlatformVersion() > API_PROTEXTION_GREATER_NINE)) {
847 bool shouldClipToContent =
848 textLayoutProperty->GetTextOverflow().value_or(TextOverflow::CLIP) == TextOverflow::CLIP;
849 host->GetRenderContext()->SetClipToFrame(shouldClipToContent);
850 }
851 std::string textCache = textForDisplay_;
852 textForDisplay_ = textLayoutProperty->GetContent().value_or("");
853 if (textCache != textForDisplay_) {
854 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
855 }
856
857 auto obscuredReasons = renderContext->GetObscured().value_or(std::vector<ObscuredReasons>());
858 bool ifHaveObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
859 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
860 if (textLayoutProperty->GetTextOverflowValue(TextOverflow::CLIP) == TextOverflow::MARQUEE || ifHaveObscured) {
861 CloseSelectOverlay();
862 ResetSelection();
863 copyOption_ = CopyOptions::None;
864 return;
865 }
866 copyOption_ = textLayoutProperty->GetCopyOption().value_or(CopyOptions::None);
867 if (ignoreEvent_) {
868 return;
869 }
870 auto gestureEventHub = host->GetOrCreateGestureEventHub();
871 CHECK_NULL_VOID(gestureEventHub);
872 if (copyOption_ != CopyOptions::None) {
873 auto context = host->GetContext();
874 CHECK_NULL_VOID(context);
875 if (!clipboard_ && context) {
876 clipboard_ = ClipboardProxy::GetInstance()->GetClipboard(context->GetTaskExecutor());
877 }
878 InitLongPressEvent(gestureEventHub);
879 if (host->IsDraggable()) {
880 #ifdef ENABLE_DRAG_FRAMEWORK
881 InitDragEvent();
882 #else
883 InitPanEvent(gestureEventHub);
884 #endif
885 }
886 InitMouseEvent();
887 InitTouchEvent();
888 SetAccessibilityAction();
889 }
890 if (onClick_ || copyOption_ != CopyOptions::None) {
891 InitClickEvent(gestureEventHub);
892 }
893 }
894
ActSetSelection(int32_t start,int32_t end)895 void TextPattern::ActSetSelection(int32_t start, int32_t end)
896 {
897 if (start < 0) {
898 start = GetWideText().length();
899 }
900 if (end < 0) {
901 end = GetWideText().length();
902 }
903 textSelector_.Update(start, end);
904 CalculateHandleOffsetAndShowOverlay();
905 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
906 auto host = GetHost();
907 CHECK_NULL_VOID(host);
908 host->MarkDirtyNode(PROPERTY_UPDATE_RENDER);
909 }
910
UpdateSelectOverlayOrCreate(SelectOverlayInfo selectInfo,bool animation)911 void TextPattern::UpdateSelectOverlayOrCreate(SelectOverlayInfo selectInfo, bool animation)
912 {
913 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
914 SelectHandleInfo firstHandleInfo;
915 SelectHandleInfo secondHandleInfo;
916 firstHandleInfo.paintRect = textSelector_.firstHandle;
917 secondHandleInfo.paintRect = textSelector_.secondHandle;
918 auto start = textSelector_.GetTextStart();
919 auto end = textSelector_.GetTextEnd();
920 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
921 selectOverlayProxy_->UpdateFirstAndSecondHandleInfo(firstHandleInfo, secondHandleInfo);
922 } else {
923 auto pipeline = PipelineContext::GetCurrentContext();
924 CHECK_NULL_VOID(pipeline);
925 selectInfo.callerFrameNode = GetHost();
926 selectOverlayProxy_ =
927 pipeline->GetSelectOverlayManager()->CreateAndShowSelectOverlay(selectInfo, WeakClaim(this), animation);
928 CHECK_NULL_VOID_NOLOG(selectOverlayProxy_);
929 auto start = textSelector_.GetTextStart();
930 auto end = textSelector_.GetTextEnd();
931 selectOverlayProxy_->SetSelectInfo(GetSelectedText(start, end));
932 }
933 }
934
OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper> & dirty,const DirtySwapConfig & config)935 bool TextPattern::OnDirtyLayoutWrapperSwap(const RefPtr<LayoutWrapper>& dirty, const DirtySwapConfig& config)
936 {
937 if (selectOverlayProxy_ && !selectOverlayProxy_->IsClosed()) {
938 CalculateHandleOffsetAndShowOverlay();
939 ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle);
940 }
941 if (config.skipMeasure || dirty->SkipMeasureContent()) {
942 return false;
943 }
944 auto layoutAlgorithmWrapper = DynamicCast<LayoutAlgorithmWrapper>(dirty->GetLayoutAlgorithm());
945 CHECK_NULL_RETURN(layoutAlgorithmWrapper, false);
946 auto textLayoutAlgorithm = DynamicCast<TextLayoutAlgorithm>(layoutAlgorithmWrapper->GetLayoutAlgorithm());
947 CHECK_NULL_RETURN(textLayoutAlgorithm, false);
948 auto paragraph = textLayoutAlgorithm->GetParagraph();
949 if (!paragraph) {
950 LOGD("on layout process, just return");
951 return false;
952 }
953 LOGD("on layout process, continue");
954 paragraph_ = textLayoutAlgorithm->GetParagraph();
955 baselineOffset_ = textLayoutAlgorithm->GetBaselineOffset();
956 contentRect_ = dirty->GetGeometryNode()->GetContentRect();
957 contentOffset_ = dirty->GetGeometryNode()->GetContentOffset();
958 textStyle_ = textLayoutAlgorithm->GetTextStyle();
959 return true;
960 }
961
BeforeCreateLayoutWrapper()962 void TextPattern::BeforeCreateLayoutWrapper()
963 {
964 auto host = GetHost();
965 CHECK_NULL_VOID(host);
966 const auto& layoutProperty = host->GetLayoutProperty();
967 auto flag = layoutProperty ? layoutProperty->GetPropertyChangeFlag() : PROPERTY_UPDATE_NORMAL;
968 // When updating the scenario, needs to determine whether the SpanNode node is refreshed.
969 if (paragraph_ && (flag & PROPERTY_UPDATE_BY_CHILD_REQUEST) != PROPERTY_UPDATE_BY_CHILD_REQUEST) {
970 LOGD("no need to refresh span node");
971 return;
972 }
973 imageCount_ = 0;
974 // When dirty areas are marked because of child node changes, the text rendering node tree is reset.
975 const auto& children = host->GetChildren();
976 if (children.empty()) {
977 return;
978 }
979
980 if (paragraph_) {
981 LOGD("reset before create layoutwrapper");
982 paragraph_.Reset();
983 }
984 spanItemChildren_.clear();
985
986 // Depth-first iterates through all host's child nodes to collect the SpanNode object, building a text rendering
987 // tree.
988 std::stack<RefPtr<UINode>> nodes;
989 for (auto iter = children.rbegin(); iter != children.rend(); ++iter) {
990 nodes.push(*iter);
991 }
992
993 std::string textCache;
994 if (!nodes.empty()) {
995 textCache = textForDisplay_;
996 textForDisplay_.clear();
997 }
998
999 bool isSpanHasClick = false;
1000 CollectSpanNodes(nodes, isSpanHasClick);
1001
1002 if (textCache != textForDisplay_) {
1003 host->OnAccessibilityEvent(AccessibilityEventType::TEXT_CHANGE, textCache, textForDisplay_);
1004 }
1005 if (isSpanHasClick) {
1006 auto gestureEventHub = host->GetOrCreateGestureEventHub();
1007 InitClickEvent(gestureEventHub);
1008 }
1009 }
1010
CollectSpanNodes(std::stack<RefPtr<UINode>> nodes,bool & isSpanHasClick)1011 void TextPattern::CollectSpanNodes(std::stack<RefPtr<UINode>> nodes, bool& isSpanHasClick)
1012 {
1013 while (!nodes.empty()) {
1014 auto current = nodes.top();
1015 nodes.pop();
1016 // TODO: Add the judgment of display.
1017 if (!current) {
1018 continue;
1019 }
1020 auto spanNode = DynamicCast<SpanNode>(current);
1021 if (spanNode) {
1022 spanNode->CleanSpanItemChildren();
1023 UpdateChildProperty(spanNode);
1024 spanNode->MountToParagraph();
1025 textForDisplay_.append(spanNode->GetSpanItem()->content);
1026 if (spanNode->GetSpanItem()->onClick) {
1027 isSpanHasClick = true;
1028 }
1029 } else if (current->GetTag() == V2::IMAGE_ETS_TAG) {
1030 imageCount_++;
1031 AddChildSpanItem(current);
1032 }
1033 const auto& nextChildren = current->GetChildren();
1034 for (auto iter = nextChildren.rbegin(); iter != nextChildren.rend(); ++iter) {
1035 nodes.push(*iter);
1036 }
1037 }
1038 }
1039
1040
GetGlobalOffset(Offset & offset)1041 void TextPattern::GetGlobalOffset(Offset& offset)
1042 {
1043 auto host = GetHost();
1044 CHECK_NULL_VOID(host);
1045 auto pipeline = host->GetContext();
1046 CHECK_NULL_VOID(pipeline);
1047 auto rootOffset = pipeline->GetRootRect().GetOffset();
1048 auto globalOffset = host->GetPaintRectOffset() - rootOffset;
1049 offset = Offset(globalOffset.GetX(), globalOffset.GetY());
1050 }
1051
OnVisibleChange(bool isVisible)1052 void TextPattern::OnVisibleChange(bool isVisible)
1053 {
1054 if (!isVisible) {
1055 if (textSelector_.IsValid()) {
1056 CloseSelectOverlay();
1057 ResetSelection();
1058 }
1059 }
1060 }
1061
InitSurfaceChangedCallback()1062 void TextPattern::InitSurfaceChangedCallback()
1063 {
1064 auto host = GetHost();
1065 CHECK_NULL_VOID(host);
1066 auto pipeline = host->GetContext();
1067 CHECK_NULL_VOID(pipeline);
1068 if (!HasSurfaceChangedCallback()) {
1069 auto callbackId = pipeline->RegisterSurfaceChangedCallback(
1070 [weak = WeakClaim(this)](int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight,
1071 WindowSizeChangeReason type) {
1072 auto pattern = weak.Upgrade();
1073 if (pattern) {
1074 pattern->HandleSurfaceChanged(newWidth, newHeight, prevWidth, prevHeight);
1075 }
1076 });
1077 LOGD("Add surface changed callback id %{public}d", callbackId);
1078 UpdateSurfaceChangedCallbackId(callbackId);
1079 }
1080 }
1081
HandleSurfaceChanged(int32_t newWidth,int32_t newHeight,int32_t prevWidth,int32_t prevHeight)1082 void TextPattern::HandleSurfaceChanged(int32_t newWidth, int32_t newHeight, int32_t prevWidth, int32_t prevHeight)
1083 {
1084 LOGD("TextPattern handle surface change, new width %{public}d, new height %{public}d, prev width %{public}d, prev "
1085 "height %{public}d",
1086 newWidth, newHeight, prevWidth, prevHeight);
1087 CloseSelectOverlay();
1088 ResetSelection();
1089 }
1090
InitSurfacePositionChangedCallback()1091 void TextPattern::InitSurfacePositionChangedCallback()
1092 {
1093 auto host = GetHost();
1094 CHECK_NULL_VOID(host);
1095 auto pipeline = host->GetContext();
1096 CHECK_NULL_VOID(pipeline);
1097 if (!HasSurfacePositionChangedCallback()) {
1098 auto callbackId =
1099 pipeline->RegisterSurfacePositionChangedCallback([weak = WeakClaim(this)](int32_t posX, int32_t posY) {
1100 auto pattern = weak.Upgrade();
1101 if (pattern) {
1102 pattern->HandleSurfacePositionChanged(posX, posY);
1103 }
1104 });
1105 LOGI("Add position changed callback id %{public}d", callbackId);
1106 UpdateSurfacePositionChangedCallbackId(callbackId);
1107 }
1108 }
1109
AddChildSpanItem(const RefPtr<UINode> & child)1110 void TextPattern::AddChildSpanItem(const RefPtr<UINode>& child)
1111 {
1112 CHECK_NULL_VOID(child);
1113 auto chidNode = DynamicCast<FrameNode>(child);
1114 if (chidNode && chidNode->GetLayoutProperty() && chidNode->GetLayoutProperty()->IsOverlayNode()) {
1115 return;
1116 }
1117
1118 if (child->GetTag() == V2::SPAN_ETS_TAG) {
1119 auto spanNode = DynamicCast<SpanNode>(child);
1120 if (spanNode) {
1121 spanItemChildren_.emplace_back(spanNode->GetSpanItem());
1122 }
1123 } else if (child->GetTag() == V2::IMAGE_ETS_TAG) {
1124 auto imageNode = DynamicCast<FrameNode>(child);
1125 if (imageNode) {
1126 spanItemChildren_.emplace_back(MakeRefPtr<ImageSpanItem>());
1127 }
1128 }
1129 }
1130
DumpInfo()1131 void TextPattern::DumpInfo()
1132 {
1133 auto textLayoutProp = GetLayoutProperty<TextLayoutProperty>();
1134 CHECK_NULL_VOID(textLayoutProp);
1135 DumpLog::GetInstance().AddDesc(std::string("Content: ").append(textLayoutProp->GetContent().value_or(" ")));
1136 DumpLog::GetInstance().AddDesc(
1137 std::string("FontColor: ").append(textLayoutProp->GetTextColor().value_or(Color::BLACK).ColorToString()));
1138 DumpLog::GetInstance().AddDesc(
1139 std::string("FontSize: ")
1140 .append(
1141 (textStyle_.has_value() ? textStyle_->GetFontSize() : Dimension(16.0, DimensionUnit::FP)).ToString()));
1142 }
1143
UpdateChildProperty(const RefPtr<SpanNode> & child) const1144 void TextPattern::UpdateChildProperty(const RefPtr<SpanNode>& child) const
1145 {
1146 CHECK_NULL_VOID(child);
1147 auto host = GetHost();
1148 CHECK_NULL_VOID(host);
1149 auto textLayoutProp = host->GetLayoutProperty<TextLayoutProperty>();
1150 CHECK_NULL_VOID(textLayoutProp);
1151
1152 auto inheritPropertyInfo = child->CaculateInheritPropertyInfo();
1153 for (const PropertyInfo& info : inheritPropertyInfo) {
1154 switch (info) {
1155 case PropertyInfo::FONTSIZE:
1156 if (textLayoutProp->HasFontSize()) {
1157 child->UpdateFontSizeWithoutFlushDirty(textLayoutProp->GetFontSize().value());
1158 }
1159 break;
1160 case PropertyInfo::FONTCOLOR:
1161 if (textLayoutProp->HasTextColor()) {
1162 child->UpdateTextColorWithoutFlushDirty(textLayoutProp->GetTextColor().value());
1163 }
1164 break;
1165 case PropertyInfo::FONTSTYLE:
1166 if (textLayoutProp->HasItalicFontStyle()) {
1167 child->UpdateItalicFontStyleWithoutFlushDirty(textLayoutProp->GetItalicFontStyle().value());
1168 }
1169 break;
1170 case PropertyInfo::FONTWEIGHT:
1171 if (textLayoutProp->HasFontWeight()) {
1172 child->UpdateFontWeightWithoutFlushDirty(textLayoutProp->GetFontWeight().value());
1173 }
1174 break;
1175 case PropertyInfo::FONTFAMILY:
1176 if (textLayoutProp->HasFontFamily()) {
1177 child->UpdateFontFamilyWithoutFlushDirty(textLayoutProp->GetFontFamily().value());
1178 }
1179 break;
1180 case PropertyInfo::TEXTDECORATION:
1181 if (textLayoutProp->HasTextDecoration()) {
1182 child->UpdateTextDecorationWithoutFlushDirty(textLayoutProp->GetTextDecoration().value());
1183 if (textLayoutProp->HasTextDecorationColor()) {
1184 child->UpdateTextDecorationColorWithoutFlushDirty(
1185 textLayoutProp->GetTextDecorationColor().value());
1186 }
1187 }
1188 break;
1189 case PropertyInfo::TEXTCASE:
1190 if (textLayoutProp->HasTextCase()) {
1191 child->UpdateTextCaseWithoutFlushDirty(textLayoutProp->GetTextCase().value());
1192 }
1193 break;
1194 case PropertyInfo::LETTERSPACE:
1195 if (textLayoutProp->HasLetterSpacing()) {
1196 child->UpdateLetterSpacingWithoutFlushDirty(textLayoutProp->GetLetterSpacing().value());
1197 }
1198 break;
1199 case PropertyInfo::LINEHEIGHT:
1200 if (textLayoutProp->HasLineHeight()) {
1201 child->UpdateLineHeightWithoutFlushDirty(textLayoutProp->GetLineHeight().value());
1202 }
1203 break;
1204 default:
1205 LOGW("Inherited properties are not supported.");
1206 break;
1207 }
1208 }
1209 }
1210
SetAccessibilityAction()1211 void TextPattern::SetAccessibilityAction()
1212 {
1213 auto host = GetHost();
1214 CHECK_NULL_VOID(host);
1215 auto textAccessibilityProperty = host->GetAccessibilityProperty<AccessibilityProperty>();
1216 CHECK_NULL_VOID(textAccessibilityProperty);
1217 textAccessibilityProperty->SetActionSetSelection([weakPtr = WeakClaim(this)](int32_t start, int32_t end) {
1218 const auto& pattern = weakPtr.Upgrade();
1219 CHECK_NULL_VOID(pattern);
1220 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
1221 CHECK_NULL_VOID(textLayoutProperty);
1222 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
1223 pattern->ActSetSelection(start, end);
1224 }
1225 });
1226
1227 textAccessibilityProperty->SetActionClearSelection([weakPtr = WeakClaim(this)]() {
1228 const auto& pattern = weakPtr.Upgrade();
1229 CHECK_NULL_VOID(pattern);
1230 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
1231 CHECK_NULL_VOID(textLayoutProperty);
1232 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
1233 pattern->CloseSelectOverlay(true);
1234 pattern->ResetSelection();
1235 }
1236 });
1237
1238 textAccessibilityProperty->SetActionCopy([weakPtr = WeakClaim(this)]() {
1239 const auto& pattern = weakPtr.Upgrade();
1240 CHECK_NULL_VOID(pattern);
1241 auto textLayoutProperty = pattern->GetLayoutProperty<TextLayoutProperty>();
1242 CHECK_NULL_VOID(textLayoutProperty);
1243 if (textLayoutProperty->GetCopyOptionValue(CopyOptions::None) != CopyOptions::None) {
1244 pattern->HandleOnCopy();
1245 pattern->CloseSelectOverlay(true);
1246 pattern->ResetSelection();
1247 }
1248 });
1249 }
1250
OnColorConfigurationUpdate()1251 void TextPattern::OnColorConfigurationUpdate()
1252 {
1253 auto host = GetHost();
1254 CHECK_NULL_VOID(host);
1255 auto context = host->GetContext();
1256 CHECK_NULL_VOID(context);
1257 auto theme = context->GetTheme<TextTheme>();
1258 CHECK_NULL_VOID(theme);
1259 auto textLayoutProperty = GetLayoutProperty<TextLayoutProperty>();
1260 CHECK_NULL_VOID(textLayoutProperty);
1261 textLayoutProperty->UpdateTextColor(theme->GetTextStyle().GetTextColor());
1262 }
1263
GetDragUpperLeftCoordinates()1264 OffsetF TextPattern::GetDragUpperLeftCoordinates()
1265 {
1266 if (dragBoxes_.empty()) {
1267 return { 0.0f, 0.0f };
1268 }
1269 auto startY = dragBoxes_.front().rect_.GetTop();
1270 auto startX = dragBoxes_.front().rect_.GetLeft();
1271 auto endY = dragBoxes_.back().rect_.GetTop();
1272
1273 OffsetF offset;
1274 if (NearEqual(startY, endY)) {
1275 offset = { contentRect_.GetX() + startX, startY + contentRect_.GetY() };
1276 } else {
1277 offset = { contentRect_.GetX(), startY + contentRect_.GetY() };
1278 }
1279
1280 return GetParentGlobalOffset() + offset;
1281 }
1282 } // namespace OHOS::Ace::NG
1283