diff --git a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.cpp b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.cpp index 916476c0ef285c0cc8bc45cc9d9df12a7eaf502a..cecef3074146ba7f79ff903743ae757122a81ebe 100644 --- a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.cpp +++ b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.cpp @@ -32,8 +32,10 @@ RichEditorOverlayModifier::RichEditorOverlayModifier(const WeakPtr(false); AttachProperty(caretVisible_); - caretOffset_ = AceType::MakeRefPtr(OffsetF()); + caretOffset_ = AceType::MakeRefPtr(OffsetF()); AttachProperty(caretOffset_); + doubleCaretOffset_ = AceType::MakeRefPtr(OffsetF()); + AttachProperty(doubleCaretOffset_); caretHeight_ = AceType::MakeRefPtr(0.0f); AttachProperty(caretHeight_); caretWidth_ = AceType::MakeRefPtr(0.0f); @@ -86,6 +88,21 @@ void RichEditorOverlayModifier::SetCaretOffsetAndHeight(const OffsetF& cursorOff } } +void RichEditorOverlayModifier::SetDoubleCaretOffset(const OffsetF& doubleCursorOffset) +{ + doubleCaretOffset_->Set(doubleCursorOffset); +} + +void RichEditorOverlayModifier::SetNeedDoubleCaret(const bool& needDoubleCaret) +{ + needDoubleCaret_ = needDoubleCaret; +} + +bool RichEditorOverlayModifier::GetNeedDoubleCaret() +{ + return needDoubleCaret_; +} + void RichEditorOverlayModifier::SetCaretColor(uint32_t caretColor) { CHECK_NULL_VOID(caretColor_); @@ -146,6 +163,11 @@ OffsetF RichEditorOverlayModifier::GetCaretOffset() const return caretOffset_->Get(); } +OffsetF RichEditorOverlayModifier::GetDoubleCaretOffset() const +{ + return doubleCaretOffset_->Get(); +} + void RichEditorOverlayModifier::PaintPreviewTextDecoration(DrawingContext& drawingContext) const { CHECK_NULL_VOID(showPreviewTextDecoration_->Get()); @@ -197,6 +219,30 @@ void RichEditorOverlayModifier::PaintCaret(DrawingContext& drawingContext) const drawingContext.canvas.Restore(); } +void RichEditorOverlayModifier::PaintDoubleCaret(DrawingContext& drawingContext) const +{ + if (!caretVisible_->Get()) { + return; + } + auto offset = doubleCaretOffset_->Get(); + drawingContext.canvas.Save(); + RSBrush brush; + brush.SetAntiAlias(true); + brush.SetColor(Color(0xffb3b3b3).GetValue()); + drawingContext.canvas.AttachBrush(brush); + + if (GreatOrEqual(offset.GetX() + caretWidth_->Get(), contentRect_.value().Right())) { + drawingContext.canvas.DrawRect(RSRect( + offset.GetX() - caretWidth_->Get(), offset.GetY(), offset.GetX(), offset.GetY() + caretHeight_->Get())); + } else { + drawingContext.canvas.DrawRect(RSRect( + offset.GetX(), offset.GetY(), offset.GetX() + caretWidth_->Get(), offset.GetY() + caretHeight_->Get())); + } + + drawingContext.canvas.DetachBrush(); + drawingContext.canvas.Restore(); +} + void RichEditorOverlayModifier::PaintScrollBar(DrawingContext& context) { auto scrollBarOverlayModifier = scrollBarOverlayModifier_.Upgrade(); @@ -226,6 +272,10 @@ void RichEditorOverlayModifier::onDraw(DrawingContext& drawingContext) drawingContext.canvas.ClipRect(ToRSRect(contentRect_.value()), RSClipOp::INTERSECT); } PaintCaret(drawingContext); + if(needDoubleCaret_) + { + PaintDoubleCaret(drawingContext); + } PaintPreviewTextDecoration(drawingContext); SetSelectedColor(selectedBackgroundColor_->Get()); TextOverlayModifier::onDraw(drawingContext); diff --git a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h index e3705e16210999c83a5a216a033d380faa42e316..b173f4c66b80c510cf0d161246f04682de05efd8 100644 --- a/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h +++ b/frameworks/core/components_ng/pattern/rich_editor/rich_editor_overlay_modifier.h @@ -46,16 +46,23 @@ public: float GetCaretHeight() const; float GetCaretWidth() const; OffsetF GetCaretOffset() const; + OffsetF GetDoubleCaretOffset() const; void onDraw(DrawingContext& drawingContext) override; void UpdateScrollBar(PaintWrapper* paintWrapper); + void SetDoubleCaretOffset(const OffsetF& doubleCursorOffset); + void SetNeedDoubleCaret(const bool& needDoubleCaret); + bool GetNeedDoubleCaret(); private: void PaintPreviewTextDecoration(DrawingContext& drawingContext) const; void PaintCaret(DrawingContext& drawingContext) const; + void PaintDoubleCaret(DrawingContext& drawingContext) const; void PaintScrollBar(DrawingContext& context); void PaintEdgeEffect(const SizeF& frameSize, RSCanvas& canvas); + bool needDoubleCaret_; + RefPtr doubleCaretOffset_; RefPtr caretVisible_; - RefPtr caretOffset_; + RefPtr caretOffset_; RefPtr caretHeight_; RefPtr caretWidth_; RefPtr caretColor_; 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..97a7906d3fd5e7a322ef191e9324e9db1125405e 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,8 +88,10 @@ void RichEditorPaintMethod::SetCaretOffsetAndHeight(PaintWrapper* paintWrapper) CHECK_NULL_VOID(richEditorPattern); auto overlayMod = DynamicCast(GetOverlayModifier(paintWrapper)); CHECK_NULL_VOID(overlayMod); - auto [caretOffset, caretHeight] = richEditorPattern->CalculateCaretOffsetAndHeight(); - overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight); + if(!richEditorPattern->GetIsTouchCaret()) { + auto [caretOffset, caretHeight] = richEditorPattern->CalculateCaretOffsetAndHeight(); + overlayMod->SetCaretOffsetAndHeight(caretOffset, caretHeight); + } } void RichEditorPaintMethod::UpdateContentModifier(PaintWrapper* paintWrapper) 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 32a9cfb26edad76a13f32be5bbe6ab8f220e930b..b8df390368253b9542c20dcdb45a06ea7c1a1736 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 @@ -97,6 +97,7 @@ constexpr int32_t SYMBOL_SPAN_LENGTH = 2; constexpr int32_t RICH_EDITOR_TWINKLING_INTERVAL_MS = 500; constexpr float DEFAULT_TEXT_SIZE = 16.0f; constexpr int32_t AUTO_SCROLL_INTERVAL = 15; +constexpr Dimension DEFAULT_CHAR_WIDTH = 16.0_vp; constexpr Dimension CARET_BOTTOM_DISTANCE = 16.0_vp; constexpr Dimension AUTO_SCROLL_EDGE_DISTANCE = 15.0_vp; constexpr Dimension AUTO_SCROLL_DRAG_EDGE_DISTANCE = 58.0_vp; @@ -2664,6 +2665,21 @@ 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()) { + auto position = paragraphs_.GetIndex(textOffset); + SetCaretPosition(position); + isTouchCaret_ = true; + + StopTwinkling(); + caretVisible_ = true; + GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER); + + CHECK_NULL_VOID(overlayMod_); + auto [lastClickOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset); + auto localOffset = OffsetF(textOffset.GetX(), lastClickOffset.GetY()); + DynamicCast(overlayMod_)->SetCaretOffsetAndHeight(localOffset, 0.0f); + return; + } InitSelection(textOffset); auto selectEnd = std::max(textSelector_.baseOffset, textSelector_.destinationOffset); auto selectStart = std::min(textSelector_.baseOffset, textSelector_.destinationOffset); @@ -5040,6 +5056,46 @@ void RichEditorPattern::HandleTouchDown(const Offset& offset) OffsetF caretOffset = DynamicCast(overlayMod_)->GetCaretOffset(); RectF lastCaretRect = RectF{ caretOffset.GetX() - caretHeight / 2, caretOffset.GetY(), caretHeight, caretHeight }; isTouchCaret_ = RepeatClickCaret(offset, caretPosition_, lastCaretRect); + if(isTouchCaret_) { + StopTwinkling(); + caretVisible_ = true; + GetHost()->MarkDirtyNode(PROPERTY_UPDATE_RENDER); + } +} + +void RichEditorPattern::StartAnimation() +{ + OffsetF caretOffset = DynamicCast(overlayMod_)->GetCaretOffset(); + Offset offset = Offset(caretOffset.GetX(), caretOffset.GetY()); + Offset textOffset = ConvertTouchOffsetToTextOffset(offset); + + AnimationOption backgroundOption; + backgroundOption.SetDuration(200); + backgroundOption.SetCurve(Curves::SMOOTH); + backgroundOption.SetDelay(0); + auto caretOffsetPropertyCallback = [weakPattern = WeakClaim(this), textOffset]() { + auto pattern = weakPattern.Upgrade(); + CHECK_NULL_VOID(pattern); + if (DynamicCast(pattern->overlayMod_)->GetNeedDoubleCaret()) { + OffsetF doubleCaretOffset = DynamicCast(pattern->overlayMod_)->GetDoubleCaretOffset(); + DynamicCast(pattern->overlayMod_)->SetCaretOffsetAndHeight(doubleCaretOffset, 0.0f); + } + else { + // 不需要双光标,确定实际应该停放的位置。 + auto [caretOffset2, caretHeight] = pattern->CalcAndRecordLastClickCaretInfo(textOffset); + DynamicCast(pattern->overlayMod_)->SetCaretOffsetAndHeight(caretOffset2, 0.0f); + auto position = pattern->paragraphs_.GetIndex(Offset(caretOffset2.GetX(), caretOffset2.GetY())); + pattern->SetCaretPosition(position); + } + }; + auto FinishCallback = [weakPattern = WeakClaim(this), textOffset]() { + auto pattern = weakPattern.Upgrade(); + CHECK_NULL_VOID(pattern); + pattern->StartTwinkling(); + pattern->isTouchCaret_ = false; + DynamicCast(pattern->overlayMod_)->SetNeedDoubleCaret(false); + }; + AnimationUtils::Animate(backgroundOption, caretOffsetPropertyCallback, FinishCallback); } void RichEditorPattern::HandleTouchUp() @@ -5047,10 +5103,13 @@ void RichEditorPattern::HandleTouchUp() if (isTouchCaret_ && selectOverlay_->IsSingleHandleShow()) { selectOverlay_->ShowMenu(); } - isTouchCaret_ = false; if (magnifierController_->GetShowMagnifier()) { magnifierController_->UpdateShowMagnifier(); } + if(isTouchCaret_) + { + StartAnimation(); + } #if defined(OHOS_STANDARD_SYSTEM) && !defined(PREVIEW) if (isLongPress_) { isLongPress_ = false; @@ -5064,7 +5123,26 @@ void RichEditorPattern::HandleTouchMove(const Offset& offset) Offset textOffset = ConvertTouchOffsetToTextOffset(offset); auto position = paragraphs_.GetIndex(textOffset); SetCaretPosition(position); - CalcAndRecordLastClickCaretInfo(textOffset); + // 计算光标实际需要停放的位置 + auto [caretOffset, caretHeight] = CalcAndRecordLastClickCaretInfo(textOffset); + CHECK_NULL_VOID(overlayMod_); + // 光标跟手X随手变化,但是Y值需要稳定在一行。 + auto localOffset = OffsetF(textOffset.GetX(), caretOffset.GetY()); + DynamicCast(overlayMod_)->SetCaretOffsetAndHeight(localOffset, 0.0f); + if (!NearEqual(caretOffset.GetX(), textOffset.GetX(), 2 * DEFAULT_CHAR_WIDTH.ConvertToPx())) { + if (magnifierController_->GetShowMagnifier()) { + magnifierController_->UpdateShowMagnifier(); + } + DynamicCast(overlayMod_)->SetNeedDoubleCaret(true); + DynamicCast(overlayMod_)->SetDoubleCaretOffset(caretOffset); + } else { + DynamicCast(overlayMod_)->SetNeedDoubleCaret(false); + DynamicCast(overlayMod_)->SetDoubleCaretOffset(caretOffset); + if (magnifierController_) { + // 放大镜跟随光标移动 + magnifierController_->SetLocalOffset(localOffset); + } + } if (selectOverlay_->IsSingleHandleShow()) { textSelector_.Update(caretPosition_); CalculateHandleOffsetAndShowOverlay(); 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 deb04400820d001ba4ecbe2d465415e4f89327cb..65c3e2f4a20b8fb1eb1128f92b24a2ea67cc3918 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 @@ -723,6 +723,11 @@ public: contentChange_ = onChange; } + bool GetIsTouchCaret() const + { + return isTouchCaret_; + } + protected: bool CanStartAITask() override; @@ -782,6 +787,7 @@ private: void HandleMouseEvent(const MouseInfo& info); void HandleTouchEvent(const TouchEventInfo& info); void HandleTouchDown(const Offset& offset); + void StartAnimation(); void HandleTouchUp(); void HandleTouchMove(const Offset& offset); void InitLongPressEvent(const RefPtr& gestureHub);