Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python Console: Jump to previous/next result (#9784) #9785

Merged
merged 19 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
36b559c
Python Console: Jump to previous/next result (#9784)
JulienCochuyt Jun 21, 2019
3056561
Merge branch 'master' into i9784
JulienCochuyt Sep 23, 2019
8742f3b
Merge branch 'master' into i9784
JulienCochuyt Oct 6, 2019
4c9deba
Python Console: Result navigation: Refactor using NVDA AppModule (#9784)
JulienCochuyt Oct 7, 2019
f79164a
Merge branch 'master' into i9784
JulienCochuyt Nov 18, 2019
8bc556e
Merge branch 'master' into i9784
JulienCochuyt May 3, 2020
cd4f4eb
Python Console: Result navigation: Add missing translators comments (…
JulienCochuyt May 3, 2020
08d7104
Merge branch 'master' into i9784
JulienCochuyt May 11, 2020
297e0ed
Update copyright notice header lines and split instead of disabling E501
JulienCochuyt May 11, 2020
ee5d961
Merge branch 'master' into i9784
JulienCochuyt Jul 3, 2020
ce92a02
Merge remote-tracking branch 'upstream/master' into i9784
JulienCochuyt Apr 23, 2021
6551b0b
Merge remote-tracking branch 'upstream/master' into i9784
JulienCochuyt Apr 23, 2021
9ada707
Update Developer Guide (#9784)
JulienCochuyt Apr 23, 2021
8d92327
Merge remote-tracking branch 'origin/master' into i9784
feerrenrut Apr 30, 2021
cb86c17
Merge remote-tracking branch 'upstream/master' into i9784
JulienCochuyt Apr 30, 2021
fff2ee3
Add comments regarding the use of `isPrevFocusOnNvdaPopup`
JulienCochuyt Apr 30, 2021
f681675
Fix linting errors
JulienCochuyt Apr 30, 2021
a63bbff
Merge remote-tracking branch 'origin/master' into i9784
feerrenrut May 6, 2021
d5e3810
update changes file for PR #9785
feerrenrut May 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 143 additions & 5 deletions source/appModules/nvda.py
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
#appModules/nvda.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2008-2017 NV Access Limited
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2008-2020 NV Access Limited, James Teh, Michael Curran, Leonard de Ruijter, Reef Turner,
# Julien Cochuyt
# This file may be used under the terms of the GNU General Public License, version 2 or later.
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html


import typing

import appModuleHandler
import api
import controlTypes
import versionInfo
from NVDAObjects.IAccessible import IAccessible
from baseObject import ScriptableObject
import gui
from scriptHandler import script
import speech
import textInfos
import braille
import config
from logHandler import log

if typing.TYPE_CHECKING:
import inputCore


nvdaMenuIaIdentity = None

class NvdaDialog(IAccessible):
Expand Down Expand Up @@ -44,6 +54,116 @@ def _get_description(self):
"""
return ""


# Translators: The name of a category of NVDA commands.
SCRCAT_PYTHON_CONSOLE = _("Python Console")


class NvdaPythonConsoleUIOutputClear(ScriptableObject):

isPrevFocusOnNvdaPopup = True

@script(
gesture="kb:control+l",
# Translators: Description of a command to clear the Python Console output pane
description=_("Clear the output pane"),
category=SCRCAT_PYTHON_CONSOLE,
)
def script_clearOutput(self, gesture: "inputCore.InputGesture"):
from pythonConsole import consoleUI
consoleUI.clear()


class NvdaPythonConsoleUIOutputCtrl(ScriptableObject):

isPrevFocusOnNvdaPopup = True

@script(
gesture="kb:alt+downArrow",
# Translators: Description of a command to move to the next result in the Python Console output pane
description=_("Move to the next result"),
category=SCRCAT_PYTHON_CONSOLE
)
def script_moveToNextResult(self, gesture: "inputCore.InputGesture"):
self._resultNavHelper(direction="next", select=False)

@script(
gesture="kb:alt+upArrow",
# Translators: Description of a command to move to the previous result
# in the Python Console output pane
description=_("Move to the previous result"),
category=SCRCAT_PYTHON_CONSOLE
)
def script_moveToPrevResult(self, gesture: "inputCore.InputGesture"):
self._resultNavHelper(direction="previous", select=False)

@script(
gesture="kb:alt+downArrow+shift",
# Translators: Description of a command to select from the current caret position to the end
# of the current result in the Python Console output pane
description=_("Select until the end of the current result"),
category=SCRCAT_PYTHON_CONSOLE
)
def script_selectToResultEnd(self, gesture: "inputCore.InputGesture"):
self._resultNavHelper(direction="next", select=True)

@script(
gesture="kb:alt+shift+upArrow",
# Translators: Description of a command to select from the current caret position to the start
# of the current result in the Python Console output pane
description=_("Select until the start of the current result"),
category=SCRCAT_PYTHON_CONSOLE
)
def script_selectToResultStart(self, gesture: "inputCore.InputGesture"):
self._resultNavHelper(direction="previous", select=True)

def _resultNavHelper(self, direction: str = "next", select: bool = False):
from pythonConsole import consoleUI
startPos, endPos = consoleUI.outputCtrl.GetSelection()
if self.isTextSelectionAnchoredAtStart:
curPos = endPos
else:
curPos = startPos
if direction == "previous":
for pos in reversed(consoleUI.outputPositions):
if pos < curPos:
break
else:
# Translators: Reported when attempting to move to the previous result in the Python Console
# output pane while there is no previous result.
speech.speakMessage(_("Top"))
return
elif direction == "next":
for pos in consoleUI.outputPositions:
if pos > curPos:
break
else:
# Translators: Reported when attempting to move to the next result in the Python Console
# output pane while there is no next result.
speech.speakMessage(_("Bottom"))
return
else:
raise ValueError(u"Unexpected direction: {!r}".format(direction))
if select:
consoleUI.outputCtrl.Freeze()
anchorPos = startPos if self.isTextSelectionAnchoredAtStart else endPos
consoleUI.outputCtrl.SetSelection(anchorPos, pos)
consoleUI.outputCtrl.Thaw()
self.detectPossibleSelectionChange()
self.isTextSelectionAnchoredAtStart = anchorPos < pos
else:
consoleUI.outputCtrl.SetSelection(pos, pos)
info = self.makeTextInfo(textInfos.POSITION_CARET)
copy = info.copy()
info.expand(textInfos.UNIT_LINE)
if (
copy.move(textInfos.UNIT_CHARACTER, 4, endPoint="end") == 4
and copy.text == ">>> "
):
info.move(textInfos.UNIT_CHARACTER, 4, endPoint="start")
speech.speakTextInfo(info, reason=controlTypes.REASON_CARET)
JulienCochuyt marked this conversation as resolved.
Show resolved Hide resolved


class AppModule(appModuleHandler.AppModule):
# The configuration profile that has been previously edited.
# This ought to be a class property.
Expand Down Expand Up @@ -108,8 +228,26 @@ def isNvdaSettingsDialog(self, obj):
return True
return False

def isNvdaPythonConsoleUIInputCtrl(self, obj):
from pythonConsole import consoleUI
if not consoleUI:
return
return obj.windowHandle == consoleUI.inputCtrl.GetHandle()

def isNvdaPythonConsoleUIOutputCtrl(self, obj):
from pythonConsole import consoleUI
if not consoleUI:
return
return obj.windowHandle == consoleUI.outputCtrl.GetHandle()

def chooseNVDAObjectOverlayClasses(self, obj, clsList):
if obj.windowClassName == "#32770" and obj.role == controlTypes.ROLE_DIALOG:
clsList.insert(0, NvdaDialog)
if self.isNvdaSettingsDialog(obj):
clsList.insert(0, NvdaDialogEmptyDescription)
return
if self.isNvdaPythonConsoleUIInputCtrl(obj):
clsList.insert(0, NvdaPythonConsoleUIOutputClear)
elif self.isNvdaPythonConsoleUIOutputCtrl(obj):
clsList.insert(0, NvdaPythonConsoleUIOutputClear)
clsList.insert(0, NvdaPythonConsoleUIOutputCtrl)
13 changes: 7 additions & 6 deletions source/gui/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# -*- coding: UTF-8 -*-
#gui/__init__.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2006-2018 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Mesar Hameed, Joseph Lee, Thomas Stivers, Babbage B.V.
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
# gui/__init__.py
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2006-2020 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Mesar Hameed, Joseph Lee,
# Thomas Stivers, Babbage B.V., Julien Cochuyt
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.

import time
import os
Expand Down Expand Up @@ -128,7 +129,7 @@ def prePopup(self):
"""
nvdaPid = os.getpid()
focus = api.getFocusObject()
if focus.processID != nvdaPid:
if focus.processID != nvdaPid or getattr(focus, "isPrevFocusOnNvdaPopup", False):
JulienCochuyt marked this conversation as resolved.
Show resolved Hide resolved
self.prevFocus = focus
self.prevFocusAncestors = api.getFocusAncestors()
if winUser.getWindowThreadProcessID(winUser.getForegroundWindow())[0] != nvdaPid:
Expand Down
20 changes: 15 additions & 5 deletions source/pythonConsole.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pythonConsole.py
#A part of NonVisual Desktop Access (NVDA)
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
#Copyright (C) 2008-2019 NV Access Limited, Leonard de Ruijter
# pythonConsole.py
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2008-2020 NV Access Limited, Leonard de Ruijter, Julien Cochuyt

import watchdog

Expand All @@ -12,6 +12,7 @@

import builtins
import os
from typing import Sequence
import code
import codeop
import sys
Expand Down Expand Up @@ -246,6 +247,7 @@ def __init__(self, parent):
# Even the most recent line has a position in the history, so initialise with one blank line.
self.inputHistory = [""]
self.inputHistoryPos = 0
self.outputPositions: Sequence[int] = [0]

def onActivate(self, evt):
if evt.GetActive():
Expand All @@ -261,6 +263,12 @@ def output(self, data):
if data and not data.isspace():
queueHandler.queueFunction(queueHandler.eventQueue, speech.speakText, data)

def clear(self):
"""Clear the output.
"""
self.outputCtrl.Clear()
self.outputPositions[:] = [0]

def echo(self, data):
self.outputCtrl.write(data)

Expand All @@ -285,6 +293,8 @@ def execute(self):
self.inputHistory.append("")
self.inputHistoryPos = len(self.inputHistory) - 1
self.inputCtrl.ChangeValue("")
if self.console.prompt != "...":
self.outputPositions.append(self.outputCtrl.GetInsertionPoint())

def historyMove(self, movement):
newIndex = self.inputHistoryPos + movement
Expand Down