editeng/source/editeng/impedit.hxx | 3 +- editeng/source/editeng/impedit2.cxx | 44 ++++++++++++++++++++++++++++++++++-- editeng/source/editeng/impedit5.cxx | 7 ----- 3 files changed, 45 insertions(+), 9 deletions(-)
New commits: commit 1d70690e304cd3321d6c5a14e28df717e3d315d9 Author: Jonathan Clark <jonat...@libreoffice.org> AuthorDate: Thu May 15 06:40:07 2025 -0600 Commit: Jonathan Clark <jonat...@libreoffice.org> CommitDate: Fri May 16 11:15:32 2025 +0200 tdf#151336 editeng: Fix bad cursor pos when moving to RTL paragraph This change fixes a bug causing incorrect visual cursor positioning when advancing the cursor from an LTR paragraph to an RTL paragraph that starts with an LTR run. When advancing the cursor past the end of a paragraph, the cursor should be placed at the conceptual start of the following paragraph. Previously, Edit Engine did this by placing the cursor immediately before the character at index 0. The result looks correct in many situations, but it fails in the case of bidi text: the conceptual start of an RTL paragraph is the right edge of the text area, but when the paragraph begins with LTR characters, the character at position 0 may start arbitrarily far to the left of the expected cursor position. Edit Engine already contains logic to correctly position the cursor for the home/end keys. This change extends that logic to include cases where the cursor is moved between paragraphs. Change-Id: I0d23b87beacd53d7508b74445d25f9024dbaf89c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/185367 Tested-by: Jenkins Reviewed-by: Jonathan Clark <jonat...@libreoffice.org> diff --git a/editeng/source/editeng/impedit.hxx b/editeng/source/editeng/impedit.hxx index 36804880cd23..b8a48c195154 100644 --- a/editeng/source/editeng/impedit.hxx +++ b/editeng/source/editeng/impedit.hxx @@ -1059,7 +1059,8 @@ public: void SetText( const EditTextObject& rTextObject ); EditSelection InsertText( const EditTextObject& rTextObject, EditSelection aSel ); - EditSelection const & MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView ); + EditSelection const& MoveCursor(const KeyEvent& rKeyEvent, EditView* pEditView, + CursorFlags* pOutCursorFlags = nullptr); EditSelection MoveParagraphs( Range aParagraphs, sal_Int32 nNewPos, EditView* pCurView ); diff --git a/editeng/source/editeng/impedit2.cxx b/editeng/source/editeng/impedit2.cxx index cb68da749b71..c9b9143b7b8f 100644 --- a/editeng/source/editeng/impedit2.cxx +++ b/editeng/source/editeng/impedit2.cxx @@ -806,8 +806,8 @@ void ImpEditEngine::ParaAttribsChanged( ContentNode const * pNode, bool bIgnoreU // Cursor movements - -EditSelection const & ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, EditView* pEditView ) +EditSelection const& ImpEditEngine::MoveCursor(const KeyEvent& rKeyEvent, EditView* pEditView, + CursorFlags* pOutCursorFlags) { // Actually, only necessary for up/down, but whatever. CheckIdleFormatter(); @@ -816,6 +816,40 @@ EditSelection const & ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, Edit EditPaM aOldPaM( aPaM ); + // tdf#151336: Moving the cursor may advance to another paragraph. When that happens, + // special handling is needed for correct visual cursor placement in RTL text. + auto fnSetStartOfLineFlag = [&] + { + if (pOutCursorFlags) + { + pOutCursorFlags->bStartOfLine = true; + } + }; + + auto fnSetParaChangeStartOfLineFlag = [&] + { + if (pOutCursorFlags && aPaM.GetNode() != aOldPaM.GetNode()) + { + pOutCursorFlags->bStartOfLine = true; + } + }; + + auto fnSetEndOfLineFlag = [&] + { + if (pOutCursorFlags) + { + pOutCursorFlags->bEndOfLine = true; + } + }; + + auto fnSetParaChangeEndOfLineFlag = [&] + { + if (pOutCursorFlags && aPaM.GetNode() != aOldPaM.GetNode()) + { + pOutCursorFlags->bEndOfLine = true; + } + }; + TextDirectionality eTextDirection = TextDirectionality::LeftToRight_TopToBottom; if (IsEffectivelyVertical() && IsTopToBottom()) eTextDirection = TextDirectionality::TopToBottom_RightToLeft; @@ -847,12 +881,16 @@ EditSelection const & ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, Edit case KEY_DOWN: aPaM = CursorDown( aPaM, pEditView ); break; case KEY_LEFT: aPaM = bCtrl ? WordLeft( aPaM ) : CursorLeft( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL ); + fnSetParaChangeEndOfLineFlag(); break; case KEY_RIGHT: aPaM = bCtrl ? WordRight( aPaM ) : CursorRight( aPaM, aTranslatedKeyEvent.GetKeyCode().IsMod2() ? i18n::CharacterIteratorMode::SKIPCHARACTER : i18n::CharacterIteratorMode::SKIPCELL ); + fnSetParaChangeStartOfLineFlag(); break; case KEY_HOME: aPaM = bCtrl ? CursorStartOfDoc() : CursorStartOfLine( aPaM ); + fnSetStartOfLineFlag(); break; case KEY_END: aPaM = bCtrl ? CursorEndOfDoc() : CursorEndOfLine( aPaM ); + fnSetEndOfLineFlag(); break; case KEY_PAGEUP: aPaM = bCtrl ? CursorStartOfDoc() : PageUp( aPaM, pEditView ); break; @@ -860,10 +898,12 @@ EditSelection const & ImpEditEngine::MoveCursor( const KeyEvent& rKeyEvent, Edit break; case css::awt::Key::MOVE_TO_BEGIN_OF_LINE: aPaM = CursorStartOfLine( aPaM ); + fnSetStartOfLineFlag(); bKeyModifySelection = false; break; case css::awt::Key::MOVE_TO_END_OF_LINE: aPaM = CursorEndOfLine( aPaM ); + fnSetEndOfLineFlag(); bKeyModifySelection = false; break; case css::awt::Key::MOVE_WORD_BACKWARD: diff --git a/editeng/source/editeng/impedit5.cxx b/editeng/source/editeng/impedit5.cxx index 84668f0fa942..5a356281ac76 100644 --- a/editeng/source/editeng/impedit5.cxx +++ b/editeng/source/editeng/impedit5.cxx @@ -1030,7 +1030,7 @@ bool ImpEditEngine::PostKeyEvent( const KeyEvent& rKeyEvent, EditView* pEditView if ( ImpEditEngine::DoVisualCursorTraveling() && ( ( nCode == KEY_LEFT ) || ( nCode == KEY_RIGHT ) /* || ( nCode == KEY_HOME ) || ( nCode == KEY_END ) */ ) ) bSetCursorFlags = false; // Will be manipulated within visual cursor move - aCurSel = MoveCursor( rKeyEvent, pEditView ); + aCurSel = MoveCursor(rKeyEvent, pEditView, &aNewCursorFlags); if ( aCurSel.HasRange() ) { css::uno::Reference<css::datatransfer::clipboard::XClipboard> aSelection(GetSystemPrimarySelection()); @@ -1038,11 +1038,6 @@ bool ImpEditEngine::PostKeyEvent( const KeyEvent& rKeyEvent, EditView* pEditView } bMoved = true; - if ( nCode == KEY_HOME ) - aNewCursorFlags.bStartOfLine = true; - else if ( nCode == KEY_END ) - aNewCursorFlags.bEndOfLine = true; - } #if OSL_DEBUG_LEVEL > 1 GetLanguage(getImpl().GetEditDoc().GetPos( aCurSel.Max().GetNode() ), aCurSel.Max().GetIndex());