Skip to content

Commit

Permalink
It is now possible to hear object coordinates when using object navig…
Browse files Browse the repository at this point in the history
…ation commands via keyboard or mouse and moving via touch gestures. re #2559

Audio coordinate announcement function now lives in screen explorer. Essentially the new play location coordinates function is same as old mouse handler version.
In screen explorer, added a new function to play coordinates for a given NVDA object. This function in turn calls location coordinate function in screen explorer.
In object presentation settings, there is now a new option to toggle announcement of object location coordinates when using object navigation commands. By default this checkbox is not checked.
  • Loading branch information
josephsl committed Dec 2, 2014
1 parent 6a507e2 commit 9e5eb65
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 22 deletions.
1 change: 1 addition & 0 deletions source/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ def validateConfig(configObj,validator,validationResult=None,keyList=None):
reportKeyboardShortcuts = boolean(default=true)
reportObjectPositionInformation = boolean(default=true)
guessObjectPositionInformationWhenUnavailable = boolean(default=false)
playObjectCoordinates= boolean(default=false)
reportTooltips = boolean(default=false)
reportHelpBalloons = boolean(default=true)
reportObjectDescriptions = boolean(default=True)
Expand Down
15 changes: 14 additions & 1 deletion source/globalCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#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) 2006-2012 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista
#Copyright (C) 2006-2014 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista, Joseph Lee

import time
import itertools
Expand All @@ -12,6 +12,7 @@
import keyboardHandler
import mouseHandler
import eventHandler
import screenExplorer
import review
import controlTypes
import api
Expand Down Expand Up @@ -467,6 +468,8 @@ def script_navigatorObject_parent(self,gesture):
if curObject is not None:
api.setNavigatorObject(curObject)
speech.speakObject(curObject,reason=controlTypes.REASON_FOCUS)
if config.conf["presentation"]["playObjectCoordinates"]:
screenExplorer.playObjectCoordinates(curObject)
else:
# Translators: Reported when there is no containing (parent) object such as when focused on desktop.
speech.speakMessage(_("No containing object"))
Expand All @@ -484,6 +487,8 @@ def script_navigatorObject_next(self,gesture):
if curObject is not None:
api.setNavigatorObject(curObject)
speech.speakObject(curObject,reason=controlTypes.REASON_FOCUS)
if config.conf["presentation"]["playObjectCoordinates"]:
screenExplorer.playObjectCoordinates(curObject)
else:
# Translators: Reported when there is no next object (current object is the last object).
speech.speakMessage(_("No next"))
Expand All @@ -501,6 +506,8 @@ def script_navigatorObject_previous(self,gesture):
if curObject is not None:
api.setNavigatorObject(curObject)
speech.speakObject(curObject,reason=controlTypes.REASON_FOCUS)
if config.conf["presentation"]["playObjectCoordinates"]:
screenExplorer.playObjectCoordinates(curObject)
else:
# Translators: Reported when there is no previous object (current object is the first object).
speech.speakMessage(_("No previous"))
Expand All @@ -518,6 +525,8 @@ def script_navigatorObject_firstChild(self,gesture):
if curObject is not None:
api.setNavigatorObject(curObject)
speech.speakObject(curObject,reason=controlTypes.REASON_FOCUS)
if config.conf["presentation"]["playObjectCoordinates"]:
screenExplorer.playObjectCoordinates(curObject)
else:
# Translators: Reported when there is no contained (first child) object such as inside a document.
speech.speakMessage(_("No objects inside"))
Expand Down Expand Up @@ -1309,6 +1318,8 @@ def script_navigatorObject_nextInFlow(self,gesture):
if newObject:
api.setNavigatorObject(newObject)
speech.speakObject(newObject,reason=controlTypes.REASON_FOCUS)
if config.conf["presentation"]["playObjectCoordinates"]:
screenExplorer.playObjectCoordinates(newObject)
else:
# Translators: a message when there is no next object when navigating
ui.message(_("no next"))
Expand All @@ -1327,6 +1338,8 @@ def script_navigatorObject_previousInFlow(self,gesture):
if newObject:
api.setNavigatorObject(newObject)
speech.speakObject(newObject,reason=controlTypes.REASON_FOCUS)
if config.conf["presentation"]["playObjectCoordinates"]:
screenExplorer.playObjectCoordinates(newObject)
else:
# Translators: a message when there is no next object when navigating
ui.message(_("no next"))
Expand Down
6 changes: 6 additions & 0 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -882,6 +882,11 @@ def makeSettings(self, settingsSizer):
settingsSizer.Add(self.guessPositionInfoCheckBox,border=10,flag=wx.BOTTOM)
# Translators: This is the label for a checkbox in the
# object presentation settings dialog.
self.playObjectCoordinatesCheckBox=wx.CheckBox(self,wx.NewId(),label=_("&Play object coordinates"))
self.playObjectCoordinatesCheckBox.SetValue(config.conf["presentation"]["playObjectCoordinates"])
settingsSizer.Add(self.playObjectCoordinatesCheckBox,border=10,flag=wx.BOTTOM)
# Translators: This is the label for a checkbox in the
# object presentation settings dialog.
self.descriptionCheckBox=wx.CheckBox(self,wx.NewId(),label=_("Report object &descriptions"))
self.descriptionCheckBox.SetValue(config.conf["presentation"]["reportObjectDescriptions"])
settingsSizer.Add(self.descriptionCheckBox,border=10,flag=wx.BOTTOM)
Expand Down Expand Up @@ -922,6 +927,7 @@ def onOk(self,evt):
config.conf["presentation"]["reportKeyboardShortcuts"]=self.shortcutCheckBox.IsChecked()
config.conf["presentation"]["reportObjectPositionInformation"]=self.positionInfoCheckBox.IsChecked()
config.conf["presentation"]["guessObjectPositionInformationWhenUnavailable"]=self.guessPositionInfoCheckBox.IsChecked()
config.conf["presentation"]["playObjectCoordinates"]=self.playObjectCoordinatesCheckBox.IsChecked()
config.conf["presentation"]["reportObjectDescriptions"]=self.descriptionCheckBox.IsChecked()
config.conf["presentation"]["progressBarUpdates"]["progressBarOutputMode"]=self.progressLabels[self.progressList.GetSelection()][0]
config.conf["presentation"]["progressBarUpdates"]["reportBackgroundProgressBars"]=self.reportBackgroundProgressBarsCheckBox.IsChecked()
Expand Down
23 changes: 2 additions & 21 deletions source/mouseHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import config
import winInputHook
import core
import screenExplorer

WM_MOUSEMOVE=0x0200
WM_LBUTTONDOWN=0x0201
Expand All @@ -43,26 +44,6 @@ def updateMouseShape(name):
curMouseShape=name
mouseShapeChanged=1

def playAudioCoordinates(x, y, screenWidth, screenHeight, detectBrightness=True,blurFactor=0):
minPitch=config.conf['mouse']['audioCoordinates_minPitch']
maxPitch=config.conf['mouse']['audioCoordinates_maxPitch']
curPitch=minPitch+((maxPitch-minPitch)*((screenHeight-y)/float(screenHeight)))
if detectBrightness:
startX=min(max(x-blurFactor,0),screenWidth)
width=min((x+blurFactor+1)-startX,screenWidth)
startY=min(max(y-blurFactor,0),screenHeight)
height=min((y+blurFactor+1)-startY,screenHeight)
grey=screenBitmap.rgbPixelBrightness(scrBmpObj.captureImage(startX,startY,width,height)[0][0])
brightness=grey/255.0
minBrightness=config.conf['mouse']['audioCoordinates_minVolume']
maxBrightness=config.conf['mouse']['audioCoordinates_maxVolume']
brightness=(brightness*(maxBrightness-minBrightness))+minBrightness
else:
brightness=config.conf['mouse']['audioCoordinates_maxVolume']
leftVolume=int((85*((screenWidth-float(x))/screenWidth))*brightness)
rightVolume=int((85*(float(x)/screenWidth))*brightness)
tones.beep(curPitch,40,left=leftVolume,right=rightVolume)

#Internal mouse event

def internal_mouseEvent(msg,x,y,injected):
Expand Down Expand Up @@ -90,7 +71,7 @@ def executeMouseMoveEvent(x,y):
x=min(max(screenLeft,x),(screenLeft+screenWidth)-1)
y=min(max(screenTop,y),(screenTop+screenHeight)-1)
if config.conf["mouse"]["audioCoordinatesOnMouseMove"]:
playAudioCoordinates(x,y,screenWidth,screenHeight,config.conf['mouse']['audioCoordinates_detectBrightness'],config.conf['mouse']['audioCoordinates_blurFactor'])
screenExplorer.playLocationCoordinates(x,y,screenWidth,screenHeight,config.conf['mouse']['audioCoordinates_detectBrightness'],config.conf['mouse']['audioCoordinates_blurFactor'])
oldMouseObject=api.getMouseObject()
mouseObject=desktopObject.objectFromPoint(x,y)
while mouseObject and mouseObject.beTransparentToMouse:
Expand Down
51 changes: 51 additions & 0 deletions source/screenExplorer.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,59 @@
#screenExplorer.py
#A part of NonVisual Desktop Access (NVDA)
#Copyright (C) 2012-2014 NVDA Contributors
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

"""Module which implements various utilities for screen exploration.
This includes touchscreen movement as well as handling coordinate announcement for mouse and objects.
"""

import api
import tones
import controlTypes
import textInfos
import speech
import screenBitmap
import config
import mouseHandler

def playLocationCoordinates(x, y, screenWidth, screenHeight, detectBrightness=True,blurFactor=0):
"""Plays a tone indicating the XY-coordinate for the given location. This works for both mouse movement and during touch hover gesture.
@param obj: the coordinate for the location and the screen resolution (typically desktopObject.location)
@type obj: int
"""
if x > screenWidth or y > screenHeight:
return
minPitch=config.conf['mouse']['audioCoordinates_minPitch']
maxPitch=config.conf['mouse']['audioCoordinates_maxPitch']
curPitch=minPitch+((maxPitch-minPitch)*((screenHeight-y)/float(screenHeight)))
if detectBrightness:
startX=min(max(x-blurFactor,0),screenWidth)
width=min((x+blurFactor+1)-startX,screenWidth)
startY=min(max(y-blurFactor,0),screenHeight)
height=min((y+blurFactor+1)-startY,screenHeight)
grey=screenBitmap.rgbPixelBrightness(mouseHandler.scrBmpObj.captureImage(startX,startY,width,height)[0][0])
brightness=grey/255.0
minBrightness=config.conf['mouse']['audioCoordinates_minVolume']
maxBrightness=config.conf['mouse']['audioCoordinates_maxVolume']
brightness=(brightness*(maxBrightness-minBrightness))+minBrightness
else:
brightness=config.conf['mouse']['audioCoordinates_maxVolume']
leftVolume=int((85*((screenWidth-float(x))/screenWidth))*brightness)
rightVolume=int((85*(float(x)/screenWidth))*brightness)
tones.beep(curPitch,40,left=leftVolume,right=rightVolume)

def playObjectCoordinates(obj):
"""Plays the audio coordinate for the object.
@param obj: the object for which the coordinate announced should be played
@type obj: NVDAObjects.NvDAObject
"""
l,t,w,h=obj.location
x = l+(w/2)
y = t+(h/2)
screenWidth, screenHeight = api.getDesktopObject().location[2], api.getDesktopObject().location[3]
playLocationCoordinates(x, y, screenWidth, screenHeight)


class ScreenExplorer(object):

Expand Down

0 comments on commit 9e5eb65

Please sign in to comment.