diff --git a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_paint_method.cpp b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_paint_method.cpp index c216e96cc05dcc9d39bf0ce80732c313f4f4f56a..c60cf61a5af89bb790b564312d7cc8ef9f0d5e2d 100644 --- a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_paint_method.cpp +++ b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_paint_method.cpp @@ -88,6 +88,7 @@ void RichEditorPaintMethod::SetCaretOffsetAndHeight(PaintWrapper* paintWrapper) CHECK_NULL_VOID(richEditorPattern); auto overlayMod = DynamicCast(GetOverlayModifier(paintWrapper)); CHECK_NULL_VOID(overlayMod); + CHECK_NULL_VOID(!richEditorPattern->IsMoveCaretAnywhere()); // Avoid to reset caret offset auto [caretOffset, caretHeight] = richEditorPattern->CalculateCaretOffsetAndHeight(); overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight); } diff --git a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.cpp b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.cpp index 144e5fa2ab0c3db35d5ccb6e17584bbd8b66af97..ab841b1679458ef909483afea94c4f033164c500 100644 --- a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.cpp +++ b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.cpp @@ -2518,8 +2518,8 @@ void RichEditorPattern::HandleFocusEvent() if (!usingMouseRightButton_ && !isLongPress_ && !isDragging_) { TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "Handle Focus Event, Request keyboard."); RequestKeyboard(false, true, true); + HandleOnEditChanged(true); } - HandleOnEditChanged(true); } void RichEditorPattern::UseHostToUpdateTextFieldManager() @@ -2740,9 +2740,14 @@ void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr< } auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f)); Offset textOffset = { localOffset.GetX() - textPaintOffset.GetX(), localOffset.GetY() - textPaintOffset.GetY() }; + if ((caretUpdateType_ == CaretUpdateType::LONG_PRESSED) && IsEditing()) { + ShowCaretNoTwinkling(textOffset); + return; + } InitSelection(textOffset); auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset); auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset); + initSelectStart_ = selectStart; if (IsSelected()) { showSelect_ = true; } @@ -2756,13 +2761,16 @@ void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr< selectionMenuOffset_ = info.GetGlobalLocation(); host->MarkDirtyNode(PROPERTY_UPDATE_RENDER); focusHub->RequestFocusImmediately(); - if (overlayMod_) { + if (overlayMod_ && (IsEditing() == IsSelected())) { RequestKeyboard(false, true, true); + HandleOnEditChanged(true); } if (info.GetSourceDevice() != SourceType::MOUSE || caretUpdateType_ != CaretUpdateType::DOUBLE_CLICK) { int32_t requestCode = (selectOverlay_->SelectOverlayIsOn() && caretUpdateType_ == CaretUpdateType::LONG_PRESSED) ? REQUEST_RECREATE : 0; - selectOverlay_->ProcessOverlay({.animation = true, .requestCode = requestCode}); + // preview + longpress shall hide menu; edit + longpress shall show menu. + selectOverlay_->ProcessOverlay({.menuIsShow = IsEditing(), .animation = IsEditing(), + .requestCode = requestCode}); FireOnSelectionChange(selectStart, selectEnd); if (selectOverlay_->IsSingleHandle()) { StartTwinkling(); @@ -2772,7 +2780,6 @@ void RichEditorPattern::HandleDoubleClickOrLongPress(GestureEvent& info, RefPtr< } else { StopTwinkling(); } - HandleOnEditChanged(true); } bool RichEditorPattern::HandleUserLongPressEvent(GestureEvent& info) @@ -5154,10 +5161,16 @@ void RichEditorPattern::HandleTouchDown(const Offset& offset) void RichEditorPattern::HandleTouchUp() { - if (isTouchCaret_ && selectOverlay_->IsSingleHandleShow()) { + if ((isTouchCaret_ && selectOverlay_->IsSingleHandleShow()) || IsSelected()) { + TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "show menu, IsSelected()=%{public}d", IsSelected()); selectOverlay_->ShowMenu(); } + if (isLongPress_ && IsEditing() && !IsSelectAll()) { + TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "after move caret anywhere, restore caret to twinkling"); + StartTwinkling(); + } isTouchCaret_ = false; + isMoveCaretAnywhere_ = false; if (magnifierController_->GetShowMagnifier()) { magnifierController_->UpdateShowMagnifier(); } @@ -5170,6 +5183,14 @@ void RichEditorPattern::HandleTouchUp() void RichEditorPattern::HandleTouchMove(const Offset& offset) { + if (isLongPress_) { + if (!IsEditing()) { + UpdateSelectionByTouchMove(offset); + } else { + MoveCaretAnywhere(offset); + } + return; + } CHECK_NULL_VOID(isTouchCaret_); Offset textOffset = ConvertTouchOffsetToTextOffset(offset); auto position = paragraphs_.GetIndex(textOffset); @@ -8348,4 +8369,77 @@ void RichEditorPattern::HandleTripleClickEvent(OHOS::Ace::GestureEvent& info) ShowSelectOverlay(textSelector_.firstHandle, textSelector_.secondHandle); } } + +void RichEditorPattern::ShowCaretNoTwinkling(const Offset& textOffset) +{ + auto position = paragraphs_.GetIndex(textOffset); + SetCaretPosition(position); + TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "show caret no twinkling at position=%{public}d", position); + StopTwinkling(); + caretVisible_ = true; + isMoveCaretAnywhere_ = true; + auto host = GetHost(); + CHECK_NULL_VOID(host); + host->MarkDirtyNode(PROPERTY_UPDATE_RENDER); + + CHECK_NULL_VOID(overlayMod_); + auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset); + auto localOffset = OffsetF(textOffset.GetX(), lastClickOffset.GetY()); + DynamicCast(overlayMod_)->SetCaretOffsetAndHeight(localOffset, caretHeight); + + // select + long press, so cancel selection. + if (textSelector_.IsValid()) { + TAG_LOGI(AceLogTag::ACE_RICH_TEXT, "select + long press, so cancel selection"); + CloseSelectOverlay(); + ResetSelection(); + } +} + +void RichEditorPattern::UpdateSelectionByTouchMove(const Offset& touchOffset) +{ + // While previewing + long press and move, then shall select content. + auto host = GetHost(); + CHECK_NULL_VOID(host); + + Offset textOffset = ConvertTouchOffsetToTextOffset(touchOffset); + InitSelection(textOffset); + auto selectStart = textSelector_.GetTextStart(); + auto selectEnd = textSelector_.GetTextEnd(); + + int32_t start = initSelectStart_; + int32_t end = selectEnd; + + if (selectStart < initSelectStart_) { + start = selectStart; + end = initSelectStart_; + } + isShowMenu_ = false; + HandleSelectionChange(start, end); + CalculateHandleOffsetAndShowOverlay(); + selectOverlay_->ProcessOverlay({ .menuIsShow = false, .hideHandle = false, .animation = false }); + selectOverlay_->HideMenu(); // preview + longpress and move, shall also hide menu + host->MarkDirtyNode(PROPERTY_UPDATE_MEASURE_SELF); +} + +void RichEditorPattern::MoveCaretAnywhere(const Offset& offset) +{ + // While editing + long press and move, then shall move caret:caret show anywhere on the fonts. + CHECK_NULL_VOID(isMoveCaretAnywhere_); + Offset textOffset = ConvertTouchOffsetToTextOffset(offset); + auto position = paragraphs_.GetIndex(textOffset); + AdjustCursorPosition(position); + SetCaretPosition(position); + auto [caretOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset); + CHECK_NULL_VOID(overlayMod_); + auto localOffset = OffsetF(offset.GetX(), caretOffset.GetY()); + + auto host = GetHost(); + CHECK_NULL_VOID(host); + auto geometryNode = host->GetGeometryNode(); + CHECK_NULL_VOID(geometryNode); + auto frameSize = geometryNode->GetFrameSize(); + // make sure the caret is display in the range of frame. + localOffset.SetX(std::clamp(localOffset.GetX(), 0.0f, frameSize.Width())); + DynamicCast(overlayMod_)->SetCaretOffsetAndHeight(localOffset, caretHeight); +} } // namespace OHOS::Ace::NG diff --git a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.h b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.h index 12bd9bbeb1e329b9004e4e7c6e3adbb4f42561a9..a18019f424937d7526d9d07a131fb9618e22d94f 100644 --- a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.h +++ b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_pattern.h @@ -728,6 +728,11 @@ public: return contentRect_; } + bool IsMoveCaretAnywhere() const + { + return isMoveCaretAnywhere_; + } + protected: bool CanStartAITask() override; @@ -946,6 +951,9 @@ private: void SetAccessibilityAction(); bool BeforeGestureAndClickOperate(GestureEvent& info, bool isDoubleClick); void HandleTripleClickEvent(OHOS::Ace::GestureEvent& info); + void UpdateSelectionByTouchMove(const Offset& offset); + void MoveCaretAnywhere(const Offset& touchOffset); + void ShowCaretNoTwinkling(const Offset& textOffset); #if defined(ENABLE_STANDARD_INPUT) sptr richEditTextChangeListener_; @@ -1044,6 +1052,8 @@ private: bool isCaretInContentArea_ = false; OffsetF movingHandleOffset_; bool mouseClickRelease_ = false; + int32_t initSelectStart_ = 0; + bool isMoveCaretAnywhere_ = false; }; } // namespace OHOS::Ace::NG