diff --git a/source/globalCommands.py b/source/globalCommands.py index 6fffe1ae1a2..035ce83eefa 100755 --- a/source/globalCommands.py +++ b/source/globalCommands.py @@ -1324,18 +1324,23 @@ def script_review_currentLine(self,gesture): category=SCRCAT_TEXTREVIEW, gestures=("kb:numpad9", "kb(laptop):NVDA+downArrow", "ts(text):flickDown") ) - def script_review_nextLine(self,gesture): - info=api.getReviewPosition().copy() - info.expand(textInfos.UNIT_LINE) - info.collapse() - res=info.move(textInfos.UNIT_LINE,1) - if res==0: + def script_review_nextLine(self, gesture): + origInfo = api.getReviewPosition().copy() + origInfo.collapse() + info = origInfo.copy() + res = info.move(textInfos.UNIT_LINE, 1) + newLine = info.copy() + newLine.expand(textInfos.UNIT_LINE) + # #12808: Some implementations of move forward by one line may succeed one more time than expected, + # landing on the exclusive end of the document. + # Therefore, verify that expanding after the move does result in being on a new line, + # i.e. the new line starts after the original review cursor position. + if res == 0 or newLine.start <= origInfo.start: # Translators: a message reported when review cursor is at the bottom line of the current navigator object. ui.reviewMessage(_("Bottom")) else: api.setReviewPosition(info) - info.expand(textInfos.UNIT_LINE) - speech.speakTextInfo(info, unit=textInfos.UNIT_LINE, reason=controlTypes.OutputReason.CARET) + speech.speakTextInfo(newLine, unit=textInfos.UNIT_LINE, reason=controlTypes.OutputReason.CARET) @script( # Translators: Input help mode message for move review cursor to bottom line command. @@ -1395,18 +1400,23 @@ def script_review_currentWord(self,gesture): category=SCRCAT_TEXTREVIEW, gestures=("kb:numpad6", "kb(laptop):NVDA+control+rightArrow", "ts(text):2finger_flickRight") ) - def script_review_nextWord(self,gesture): - info=api.getReviewPosition().copy() - info.expand(textInfos.UNIT_WORD) - info.collapse() - res=info.move(textInfos.UNIT_WORD,1) - if res==0: + def script_review_nextWord(self, gesture): + origInfo = api.getReviewPosition().copy() + origInfo.collapse() + info = origInfo.copy() + res = info.move(textInfos.UNIT_WORD, 1) + newWord = info.copy() + newWord.expand(textInfos.UNIT_WORD) + # #12808: Some implementations of move forward by one word may succeed one more time than expected, + # landing on the exclusive end of the document. + # Therefore, verify that expanding after the move does result in being on a new word, + # i.e. the new word starts after the original review cursor position. + if res == 0 or newWord.start <= origInfo.start: # Translators: a message reported when review cursor is at the bottom line of the current navigator object. ui.reviewMessage(_("Bottom")) else: api.setReviewPosition(info) - info.expand(textInfos.UNIT_WORD) - speech.speakTextInfo(info, reason=controlTypes.OutputReason.CARET, unit=textInfos.UNIT_WORD) + speech.speakTextInfo(newWord, unit=textInfos.UNIT_WORD, reason=controlTypes.OutputReason.CARET) @script( description=_( diff --git a/user_docs/en/changes.t2t b/user_docs/en/changes.t2t index 92b28a75b65..445cf86425a 100644 --- a/user_docs/en/changes.t2t +++ b/user_docs/en/changes.t2t @@ -18,6 +18,7 @@ What's New in NVDA == Bug Fixes == - Clipboard manager pane should no longer incorrectly steal focus when opening some Office programs. (#12736) - On a system where the user has chosen to swap the primary mouse button from the left to the right, NVDA will no longer accidentally bring up a context menu instead of activating an item, in applications such as web browsers. (#12642) +- When moving the review cursor past the end of text controls, such as in Microsoft Word with UI Automation, "bottom" is correctly reported in more situations. (#12808) - == Changes for Developers ==