From bccf833a70f1e8687ef015b8ee820c495fb43666 Mon Sep 17 00:00:00 2001 From: xqq879988 Date: Thu, 4 Jul 2024 12:59:31 +0800 Subject: [PATCH] =?UTF-8?q?RichEditor=E5=BF=AB=E6=8D=B7=E9=94=AEBug?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: xqq879988 --- .../rich_editor/rich_editor_pattern.cpp | 193 +++++++++++------- .../pattern/rich_editor/rich_editor_pattern.h | 18 +- .../rich_editor/rich_editor_edit_test_ng.cpp | 18 +- 3 files changed, 147 insertions(+), 82 deletions(-) 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 f6cfec0a07f..09a0428d08e 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 @@ -4996,23 +4996,27 @@ bool RichEditorPattern::CursorMoveUp() CHECK_NULL_RETURN(!SelectOverlayIsOn(), false); ResetSelection(); float caretHeight = 0.0f; - OffsetF caretOffsetUp; - OffsetF caretOffsetDown; + float leadingMarginOffset = 0.0f; + CaretOffsetInfo caretInfo; if (static_cast(GetTextContentLength()) > 1) { - int32_t caretPosition = CalcMoveUpPos(caretOffsetUp, caretOffsetDown); + caretInfo = GetCaretOffsetInfoByPosition(); + int32_t caretPosition = CalcMoveUpPos(leadingMarginOffset); CHECK_NULL_RETURN(overlayMod_, false); auto overlayMod = DynamicCast(overlayMod_); auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset(); auto caretOffsetWidth = overlayMod->GetCaretWidth(); + auto rectLineInfo = CalcLineInfoByPosition(); caretPosition = std::clamp(caretPosition, 0, static_cast(GetTextContentLength())); if (caretPosition_ == caretPosition) { caretPosition = 0; } // at line middle or line end - bool isCaretPosInLineEnd = NearEqual(currentCaretOffsetOverlay.GetX(), caretOffsetUp.GetX(), caretOffsetWidth); + bool cursorNotAtLineStart = + NearEqual(currentCaretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth); + bool isEnter = NearZero(currentCaretOffsetOverlay.GetX() - richTextRect_.GetX(), rectLineInfo.GetX()); SetCaretPosition(caretPosition); MoveCaretToContentRect(); - if (isCaretPosInLineEnd) { + if (cursorNotAtLineStart && !isEnter) { OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false); SetLastClickOffset(caretOffset); } @@ -5029,41 +5033,31 @@ bool RichEditorPattern::CursorMoveDown() CHECK_NULL_RETURN(!SelectOverlayIsOn(), false); ResetSelection(); if (static_cast(GetTextContentLength()) > 1) { - float caretHeightUp = 0.0f; - float caretLineHeight = 0.0f; float caretHeight = 0.0f; - float textOffsetDownY = 0.0f; - Offset textOffset; - int32_t caretPositionStart; + float leadingMarginOffset = 0.0f; + CaretOffsetInfo caretInfo; int32_t caretPositionEnd; - caretPositionStart = caretPosition_; - OffsetF caretOffsetUp = CalcCursorOffsetByPosition(caretPosition_, caretHeightUp, false, false); - OffsetF caretOffsetLine = CalcCursorOffsetByPosition(caretPosition_, caretLineHeight); - auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); + caretInfo = GetCaretOffsetInfoByPosition(); + caretPositionEnd = CalcMoveDownPos(leadingMarginOffset); CHECK_NULL_RETURN(overlayMod_, false); auto overlayMod = DynamicCast(overlayMod_); auto caretOffsetOverlay = overlayMod->GetCaretOffset(); auto caretOffsetWidth = overlayMod->GetCaretWidth(); - float textOffsetX = GetTextRect().GetX(); - float textOffsetY = GetTextRect().GetY() - minDet / 2.0; - bool isCaretPosInLineEnd = NearEqual(caretOffsetOverlay.GetX(), caretOffsetUp.GetX(), caretOffsetWidth); - auto lineHeightDis = CalcLineHeightByPosition(caretPosition_); - // at line middle position or line end position or first line start position - if (isCaretPosInLineEnd || caretPositionStart == 0) { - textOffsetDownY = caretOffsetLine.GetY() + lineHeightDis - textOffsetY; - } else { - // at line start position, caretOffsetLine equ caretOffsetUp, so previous line height add currentLineHeight - textOffsetDownY = caretOffsetLine.GetY() + caretLineHeight + lineHeightDis - textOffsetY; - } - textOffset = Offset(caretOffsetOverlay.GetX() - textOffsetX, textOffsetDownY); - caretPositionEnd = paragraphs_.GetIndex(textOffset); + bool cursorNotAtLineStart = + NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth); + bool isEnter = NearZero(caretOffsetOverlay.GetX() - richTextRect_.GetX(), leadingMarginOffset); caretPositionEnd = std::clamp(caretPositionEnd, 0, static_cast(GetTextContentLength())); - if (caretPosition_ == caretPositionEnd) { - caretPositionEnd = GetTextContentLength(); + if (caretPositionEnd <= caretPosition_) { + float caretHeightEnd = 0.0f; + OffsetF caretOffsetEnd = CalcCursorOffsetByPosition(GetTextContentLength(), caretHeightEnd); + if (NearEqual(caretOffsetEnd.GetY(), caretOffsetOverlay.GetY())) { + caretPositionEnd = GetTextContentLength(); + } else { + caretPositionEnd += 1; + } } SetCaretPosition(caretPositionEnd); - // at line middle or line end, direct to call SetLastClickOffset - if (isCaretPosInLineEnd && caretPositionStart != 0) { + if (cursorNotAtLineStart && caretPosition_ != 0 && !isEnter) { OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false); SetLastClickOffset(caretOffset); } @@ -8847,61 +8841,120 @@ OffsetF RichEditorPattern::GetPaintRectGlobalOffset() const return textPaintOffset - rootOffset; } -float RichEditorPattern::CalcLineHeightByPosition(int32_t position) +CaretOffsetInfo RichEditorPattern::GetCaretOffsetInfoByPosition() +{ + CaretOffsetInfo caretInfo; + + caretInfo.caretOffsetUp = CalcCursorOffsetByPosition(caretPosition_, caretInfo.caretHeightUp, false, false); + caretInfo.caretOffsetDown = CalcCursorOffsetByPosition(caretPosition_, caretInfo.caretHeightDown, true, false); + caretInfo.caretOffsetLine = CalcCursorOffsetByPosition(caretPosition_, caretInfo.caretHeightLine); + return caretInfo; +} + +void RichEditorPattern::CalcLineSidesIndexByPosition(int32_t& startIndex, int32_t& endIndex) { - int32_t lineStartIndex; - int32_t lineEndIndex; Offset textStartOffset; Offset textEndOffset; - CHECK_NULL_RETURN(overlayMod_, 0.0f); + CHECK_NULL_VOID(overlayMod_); auto overlayMod = DynamicCast(overlayMod_); + auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); + float textOffsetY = richTextRect_.GetY() - (minDet / 2.0); auto currentCaretOffsetOverlay = overlayMod->GetCaretOffset(); - textStartOffset = Offset(0, currentCaretOffsetOverlay.GetY() - GetTextRect().GetY()); - textEndOffset = Offset(GetTextRect().Width(), currentCaretOffsetOverlay.GetY() - GetTextRect().GetY()); - lineStartIndex = paragraphs_.GetIndex(textStartOffset); - lineEndIndex = paragraphs_.GetIndex(textEndOffset); - auto selectedRects = paragraphs_.GetRects(lineStartIndex, lineEndIndex); - CHECK_NULL_RETURN(selectedRects.size(), 0.0f); - return selectedRects.front().Height(); + textStartOffset = Offset(0, currentCaretOffsetOverlay.GetY() - textOffsetY); + textEndOffset = Offset(richTextRect_.Width(), currentCaretOffsetOverlay.GetY() - textOffsetY); + startIndex = paragraphs_.GetIndex(textStartOffset); + endIndex = paragraphs_.GetIndex(textEndOffset); } -int32_t RichEditorPattern::CalcMoveUpPos(OffsetF& caretOffsetUp, OffsetF& caretOffsetDown) +RectF RichEditorPattern::CalcLineInfoByPosition() +{ + int32_t startIndex = 0; + int32_t endIndex = 0; + + CalcLineSidesIndexByPosition(startIndex, endIndex); + if (startIndex == endIndex) { + endIndex += 1; + } + auto selectedRects = paragraphs_.GetRects(startIndex, endIndex); + CHECK_NULL_RETURN(selectedRects.size(), {}); + return selectedRects.front(); +} + +int32_t RichEditorPattern::CalcMoveUpPos(float& leadingMarginOffset) { int32_t caretPosition; - float caretHeightUp = 0.0f; - float caretHeightDown = 0.0f; - float lineHeightDis = 0.0f; + CaretOffsetInfo caretInfo; + float textOffsetDownY = 0.0f; + int32_t startIndex = 0; + int32_t endIndex = 0; Offset textOffset; - OffsetF caretOffsetCalcUp = CalcCursorOffsetByPosition(caretPosition_, caretHeightUp, false, false); - OffsetF caretOffsetCalcDown = CalcCursorOffsetByPosition(caretPosition_, caretHeightDown, true, false); + caretInfo = GetCaretOffsetInfoByPosition(); auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); - caretOffsetUp = caretOffsetCalcUp; - caretOffsetDown = caretOffsetCalcDown; CHECK_NULL_RETURN(overlayMod_, 0); auto overlayMod = DynamicCast(overlayMod_); auto caretOffsetOverlay = overlayMod->GetCaretOffset(); - float textOffsetY = GetTextRect().GetY() + minDet / 2.0; // 2.0 Cursor one half at the center position - float lineHeight = CalcLineHeightByPosition(caretPosition_); - if (lineHeight == 0) { - lineHeightDis = caretHeightUp - overlayMod->GetCaretHeight(); + auto caretOffsetWidth = overlayMod->GetCaretWidth(); + bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth); + float textOffsetY = richTextRect_.GetY() + (minDet / 2.0); // 2.0 Cursor one half at the center position + CalcLineSidesIndexByPosition(startIndex, endIndex); + auto rectLineInfo = CalcLineInfoByPosition(); + leadingMarginOffset = rectLineInfo.GetX(); + if (cursorNotAtLineStart) { + textOffsetDownY = caretInfo.caretOffsetLine.GetY() - textOffsetY; + // lm mean leadingMargin abbr + auto lmSizeOffset = (endIndex - startIndex <= 1 && NearEqual(rectLineInfo.Width(), richTextRect_.Width())) + ? rectLineInfo.GetX() + : 0; + textOffset = Offset(caretInfo.caretOffsetLine.GetX() - richTextRect_.GetX() + lmSizeOffset, textOffsetDownY); } else { - lineHeightDis = lineHeight - overlayMod->GetCaretHeight(); + textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY; + textOffset = Offset(caretOffsetOverlay.GetX() - richTextRect_.GetX(), textOffsetDownY); } - float textOffsetDownY = caretOffsetOverlay.GetY() - lineHeightDis - textOffsetY; - textOffset = Offset(caretOffsetOverlay.GetX() - GetTextRect().GetX(), textOffsetDownY); caretPosition = paragraphs_.GetIndex(textOffset); return caretPosition; } +int32_t RichEditorPattern::CalcMoveDownPos(float& leadingMarginOffset) +{ + CaretOffsetInfo caretInfo; + float textOffsetDownY = 0.0f; + Offset textOffset; + int32_t caretPositionEnd; + + caretInfo = GetCaretOffsetInfoByPosition(); + auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); + CHECK_NULL_RETURN(overlayMod_, 0); + auto overlayMod = DynamicCast(overlayMod_); + auto caretOffsetOverlay = overlayMod->GetCaretOffset(); + auto caretOffsetWidth = overlayMod->GetCaretWidth(); + float textOffsetX = richTextRect_.GetX(); + float textOffsetY = richTextRect_.GetY() - (minDet / 2.0); + bool cursorNotAtLineStart = NearEqual(caretOffsetOverlay.GetX(), caretInfo.caretOffsetUp.GetX(), caretOffsetWidth); + // midle or enter + auto rectLineInfo = CalcLineInfoByPosition(); + leadingMarginOffset = rectLineInfo.GetX(); + auto lineHeightDis = rectLineInfo.Height(); + // midle or end, first line start position,end line end position + textOffsetDownY = caretInfo.caretOffsetLine.GetY() + caretInfo.caretHeightLine - textOffsetY; + if (cursorNotAtLineStart || caretPosition_ == 0) { + textOffset = Offset(caretInfo.caretOffsetLine.GetX() - textOffsetX, textOffsetDownY); + } else { + textOffsetDownY += lineHeightDis; + textOffset = Offset(caretOffsetOverlay.GetX() - textOffsetX, textOffsetDownY); + } + caretPositionEnd = paragraphs_.GetIndex(textOffset); + return caretPositionEnd; +} + int32_t RichEditorPattern::CalcLineBeginPosition() { float caretHeight = 0.0f; OffsetF caretOffset = CalcCursorOffsetByPosition(caretPosition_, caretHeight, false, false); auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f)); auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); - auto textOffsetY = caretOffset.GetY() - textPaintOffset.GetY() + minDet / 2.0; + auto textOffsetY = caretOffset.GetY() - textPaintOffset.GetY() + (minDet / 2.0); Offset textOffset = { 0, textOffsetY }; auto newPos = paragraphs_.GetIndex(textOffset); return newPos; @@ -8919,26 +8972,26 @@ float RichEditorPattern::GetTextThemeFontSize() int32_t RichEditorPattern::CalcLineEndPosition() { - float caretHeightUp = 0.0f; - float caretHeightDown = 0.0f; - float caretHeightLine = 0.0f; + CaretOffsetInfo caretInfo; int32_t realCaretOffsetY = 0; int32_t realLastClickOffsetY = 0; - OffsetF caretOffsetUp = CalcCursorOffsetByPosition(caretPosition_, caretHeightUp, false, false); - OffsetF caretOffsetDown = CalcCursorOffsetByPosition(caretPosition_, caretHeightDown, true, false); - OffsetF caretOffsetLine = CalcCursorOffsetByPosition(caretPosition_, caretHeightLine); + + caretInfo = GetCaretOffsetInfoByPosition(); if (NearEqual(richTextRect_.GetY(), contentRect_.GetY())) { realLastClickOffsetY = lastClickOffset_.GetY(); - realCaretOffsetY = caretOffsetDown.GetY(); + realCaretOffsetY = caretInfo.caretOffsetDown.GetY(); } else { - auto scrollOffset = caretOffsetDown.GetY() - caretOffsetUp.GetY() + caretOffsetLine.GetY(); + auto scrollOffset = + caretInfo.caretOffsetDown.GetY() - caretInfo.caretOffsetUp.GetY() + caretInfo.caretOffsetLine.GetY(); realLastClickOffsetY = lastClickOffset_.GetY() + std::abs(richTextRect_.GetY()) + contentRect_.GetY(); realCaretOffsetY = scrollOffset + std::abs(richTextRect_.GetY()) + contentRect_.GetY(); } + auto textPaintOffset = contentRect_.GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f)); auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); Offset textOffset; - float textWidth = richTextRect_.Width(); - float textPaintOffsetY = contentRect_.GetY() - std::min(baselineOffset_, 0.0f) - minDet / 2.0; + auto rectLineInfo = CalcLineInfoByPosition(); + float textWidth = richTextRect_.Width() + rectLineInfo.GetX(); + float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0); float textOffsetClickY = realLastClickOffsetY - textPaintOffsetY; float textOffsetDownY = realCaretOffsetY - textPaintOffsetY; if (lastClickOffset_.NonNegative()) { @@ -8963,7 +9016,7 @@ bool RichEditorPattern::CursorMoveLineBegin() OffsetF caretOffsetDown = CalcCursorOffsetByPosition(caretPosition_, caretHeightDown, true, false); auto textPaintOffset = GetTextRect().GetOffset() - OffsetF(0.0, std::min(baselineOffset_, 0.0f)); auto minDet = paragraphs_.minParagraphFontSize.value_or(GetTextThemeFontSize()); - float textPaintOffsetY = textPaintOffset.GetY() - minDet / 2.0; + float textPaintOffsetY = textPaintOffset.GetY() - (minDet / 2.0); if (lastClickOffset_.NonNegative()) { textOffset = { 0, lastClickOffset_.GetY() - textPaintOffsetY }; } else { @@ -9087,7 +9140,7 @@ int32_t RichEditorPattern::HandleSelectPosition(bool isForward) } else { float selectEndHeight = 0.0f; OffsetF selectEndOffset = CalcCursorOffsetByPosition(textSelector_.GetEnd(), selectEndHeight); - careOffsetY = caretOffset.GetY() - GetTextRect().GetY() + caretHeight + minDet / 2.0; + careOffsetY = caretOffset.GetY() - GetTextRect().GetY() + caretHeight + (minDet / 2.0); newPos = paragraphs_.GetIndex(Offset(caretOffset.GetX() - GetTextRect().GetX(), careOffsetY), true); OffsetF newCaretOffset = CalcCursorOffsetByPosition(newPos, newCaretHeight); if (!textSelector_.SelectNothing() && textSelector_.GetTextStart() == caretPosition_ && 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 01b3605c16a..b2cc13d2954 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 @@ -98,6 +98,17 @@ const std::map, MoveDirection> SELEC {{ HandleType::SECOND, SelectorAdjustPolicy::INCLUDE }, MoveDirection::FORWARD }, {{ HandleType::SECOND, SelectorAdjustPolicy::EXCLUDE }, MoveDirection::BACKWARD } }; +struct CaretOffsetInfo { + // caret front offset info + OffsetF caretOffsetUp; + // caret end offset info + OffsetF caretOffsetDown; + // caret position offset info + OffsetF caretOffsetLine; + float caretHeightUp = 0.0f; + float caretHeightDown = 0.0f; + float caretHeightLine = 0.0f; +}; class RichEditorPattern : public TextPattern, public ScrollablePattern, public TextInputClient, public Magnifier, public SpanWatcher { @@ -378,8 +389,11 @@ public: bool CursorMoveToParagraphEnd(); bool CursorMoveHome(); bool CursorMoveEnd(); - float CalcLineHeightByPosition(int32_t position); - int32_t CalcMoveUpPos(OffsetF& caretOffsetUp, OffsetF& caretOffsetDown); + void CalcLineSidesIndexByPosition(int32_t& startIndex, int32_t& endIndex); + RectF CalcLineInfoByPosition(); + CaretOffsetInfo GetCaretOffsetInfoByPosition(); + int32_t CalcMoveUpPos(float& leadingMarginOffset); + int32_t CalcMoveDownPos(float& leadingMarginOffset); int32_t CalcLineBeginPosition(); float GetTextThemeFontSize(); int32_t CalcLineEndPosition(); diff --git a/test/unittest/core/pattern/rich_editor/rich_editor_edit_test_ng.cpp b/test/unittest/core/pattern/rich_editor/rich_editor_edit_test_ng.cpp index 76ff0196b34..f1dd919f016 100644 --- a/test/unittest/core/pattern/rich_editor/rich_editor_edit_test_ng.cpp +++ b/test/unittest/core/pattern/rich_editor/rich_editor_edit_test_ng.cpp @@ -447,6 +447,7 @@ HWTEST_F(RichEditorEditTestNg, TestRichEditorCalcMoveUpPos001, TestSize.Level1) /** * @tc.steps: step1. declare and init variables. */ + float lmSizeOffset = 0.0f; ASSERT_NE(richEditorNode_, nullptr); auto richEditorPattern = richEditorNode_->GetPattern(); ASSERT_NE(richEditorPattern, nullptr); @@ -456,19 +457,16 @@ HWTEST_F(RichEditorEditTestNg, TestRichEditorCalcMoveUpPos001, TestSize.Level1) /** * @tc.steps: step2. change parameter and call function. */ - OffsetF caretOffsetUp(-5.0f, 5.0f); - OffsetF caretOffsetDown(5.0f, 10.0f); - richEditorPattern->CalcMoveUpPos(caretOffsetUp, caretOffsetDown); - EXPECT_NE(caretOffsetUp.x_, -5.0f); - EXPECT_NE(caretOffsetDown.x_, 5.0f); + richEditorPattern->CalcMoveUpPos(lmSizeOffset); + EXPECT_EQ(lmSizeOffset, 0.0f); } /** - * @tc.name: TestRichEditorCalcLineHeightByPosition001 - * @tc.desc: test CalcLineHeightByPosition + * @tc.name: TestRichEditorCalcLineInfoByPosition001 + * @tc.desc: test CalcLineInfoByPosition * @tc.type: FUNC */ -HWTEST_F(RichEditorEditTestNg, TestRichEditorCalcLineHeightByPosition001, TestSize.Level1) +HWTEST_F(RichEditorEditTestNg, TestRichEditorCalcLineInfoByPosition001, TestSize.Level1) { /** * @tc.steps: step1. declare and init variables. @@ -479,8 +477,8 @@ HWTEST_F(RichEditorEditTestNg, TestRichEditorCalcLineHeightByPosition001, TestSi richEditorPattern->CreateNodePaintMethod(); EXPECT_NE(richEditorPattern->contentMod_, nullptr); EXPECT_NE(richEditorPattern->overlayMod_, nullptr); - auto ret = richEditorPattern->CalcLineHeightByPosition(10); - EXPECT_EQ(ret, 0.0f); + auto ret = richEditorPattern->CalcLineInfoByPosition(); + EXPECT_EQ(ret.Height(), 0.0f); } /**