Skip to content

Commit

Permalink
Provide a locationHelper module for code related to locations, rectan…
Browse files Browse the repository at this point in the history
…gles and points (PR #7537)

Closes #7423

Previously there were many different ways in which a location is presented in NVDA:
- (left, top, width, height)
- (left, top, right, bottom)
- textInfos.Rect, which has left, top, right and bottom properties
- ctypes.wintypes.Rect, which is a struct and also has left, top, right and bottom properties
- (x,y)
- textInfos.Point, which has x and y properties
- ctypes.wintypes.Point, which is a structure and also has x and y properties

On the fly conversion between the different types is not possible. Also, conversion from screen coordinates to client coordinates, physical to logical coordinates, etc. is somewhat cumbersome.
  • Loading branch information
LeonarddeR authored and feerrenrut committed Jul 30, 2018
1 parent e31b8f2 commit 91831b2
Show file tree
Hide file tree
Showing 12 changed files with 652 additions and 24 deletions.
7 changes: 2 additions & 5 deletions source/NVDAObjects/IAccessible/MSHTML.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from .. import InvalidNVDAObject
from ..window import Window
from NVDAObjects.UIA import UIA, UIATextInfo
from locationHelper import RectLTRB

IID_IHTMLElement=comtypes.GUID('{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}')

Expand Down Expand Up @@ -589,11 +590,7 @@ def _get_location(self):
top=int(r.top*yFactor)
right=int(r.right*xFactor)
bottom=int(r.bottom*yFactor)
width=right-left
height=bottom-top
p=ctypes.wintypes.POINT(x=left,y=top)
ctypes.windll.user32.ClientToScreen(self.windowHandle,ctypes.byref(p))
return (p.x,p.y,width,height)
return RectLTRB(left,top,right,bottom).toScreen(self.windowHandle).toLTWH()
return None

def _get_TextInfo(self):
Expand Down
3 changes: 2 additions & 1 deletion source/NVDAObjects/IAccessible/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import NVDAObjects.JAB
import eventHandler
from NVDAObjects.behaviors import ProgressBar, Dialog, EditableTextWithAutoSelectDetection, FocusableUnfocusableContainer, ToolTip, Notification
from locationHelper import RectLTWH

def getNVDAObjectFromEvent(hwnd,objectID,childID):
try:
Expand Down Expand Up @@ -902,7 +903,7 @@ def _get_childCount(self):

def _get_location(self):
try:
return self.IAccessibleObject.accLocation(self.IAccessibleChildID)
return RectLTWH(*self.IAccessibleObject.accLocation(self.IAccessibleChildID))
except COMError:
return None

Expand Down
28 changes: 23 additions & 5 deletions source/NVDAObjects/IAccessible/sysListView32.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
import api
import eventHandler
import winKernel
import winUser
from . import IAccessible, List
from ..window import Window
import watchdog
from NVDAObjects.behaviors import RowWithoutCellObjects, RowWithFakeNavigation
import config
from locationHelper import RectLTRB

#Window messages
LVM_FIRST=0x1000
Expand Down Expand Up @@ -53,6 +53,15 @@
LVIF_GROUPID=0x100
LVIF_COLUMNS=0x200

#GETSUBITEMRECT flags
# Returns the bounding rectangle of the entire item, including the icon and label
LVIR_BOUNDS = 0
# Returns the bounding rectangle of the icon or small icon.
LVIR_ICON = 1
# Returns the bounding rectangle of the entire item, including the icon and label.
# This is identical to LVIR_BOUNDS.
LVIR_LABEL = 2

#Item states
LVIS_FOCUSED=0x01
LVIS_SELECTED=0x02
Expand Down Expand Up @@ -297,18 +306,27 @@ def event_stateChange(self):
class ListItem(RowWithFakeNavigation, RowWithoutCellObjects, ListItemWithoutColumnSupport):

def _getColumnLocationRaw(self,index):
assert index>0, "Invalid index: %d" % index
processHandle=self.processHandle
localRect=RECT(left=2,top=index)
# LVM_GETSUBITEMRECT requires a pointer to a RECT structure that will receive the subitem bounding rectangle information.
localRect=RECT(
left=LVIR_LABEL, # Returns the bounding rectangle of the entire item, including the icon and label
top=index # The one-based index of the subitem
)
internalRect=winKernel.virtualAllocEx(processHandle,None,sizeof(localRect),winKernel.MEM_COMMIT,winKernel.PAGE_READWRITE)
try:
winKernel.writeProcessMemory(processHandle,internalRect,byref(localRect),sizeof(localRect),None)
watchdog.cancellableSendMessage(self.windowHandle,LVM_GETSUBITEMRECT, (self.IAccessibleChildID-1), internalRect)
winKernel.readProcessMemory(processHandle,internalRect,byref(localRect),sizeof(localRect),None)
finally:
winKernel.virtualFreeEx(processHandle,internalRect,0,winKernel.MEM_RELEASE)
windll.user32.ClientToScreen(self.windowHandle,byref(localRect))
windll.user32.ClientToScreen(self.windowHandle,byref(localRect,8))
return (localRect.left,localRect.top,localRect.right-localRect.left,localRect.bottom-localRect.top)
# #8268: this might be a malformed rectangle
# (i.e. with a left coordinate that is greather than the right coordinate).
left = min(localRect.left, localRect.right)
top = min(localRect.top, localRect.bottom)
right = max(localRect.left, localRect.right)
bottom = max(localRect.top, localRect.bottom)
return RectLTRB(left, top, right, bottom).toScreen(self.windowHandle).toLTWH()

def _getColumnLocation(self,column):
return self._getColumnLocationRaw(self.parent._columnOrderArray[column - 1])
Expand Down
3 changes: 2 additions & 1 deletion source/NVDAObjects/JAB/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import textInfos.offsets
from logHandler import log
from .. import InvalidNVDAObject
from locationHelper import RectLTWH

JABRolesToNVDARoles={
"alert":controlTypes.ROLE_DIALOG,
Expand Down Expand Up @@ -301,7 +302,7 @@ def _get_description(self):
return re_simpleXmlTag.sub(" ", self._JABAccContextInfo.description)

def _get_location(self):
return (self._JABAccContextInfo.x,self._JABAccContextInfo.y,self._JABAccContextInfo.width,self._JABAccContextInfo.height)
return RectLTWH(self._JABAccContextInfo.x,self._JABAccContextInfo.y,self._JABAccContextInfo.width,self._JABAccContextInfo.height)

def _get_hasFocus(self):
if controlTypes.STATE_FOCUSED in self.states:
Expand Down
8 changes: 2 additions & 6 deletions source/NVDAObjects/UIA/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
from NVDAObjects.behaviors import ProgressBar, EditableTextWithoutAutoSelectDetection, Dialog, Notification, EditableTextWithSuggestions
import braille
import time
from locationHelper import RectLTWH
import ui

class UIATextInfo(textInfos.TextInfo):
Expand Down Expand Up @@ -1296,12 +1297,7 @@ def _get_location(self):
if r is None:
return
# r is a tuple of floats representing left, top, width and height.
# However, most NVDA code expecs location coordinates to be ints
left=int(r[0])
top=int(r[1])
width=int(r[2])
height=int(r[3])
return left,top,width,height
return RectLTWH.fromFloatCollection(*r)

def _get_value(self):
val=self._getUIACacheablePropertyValue(UIAHandler.UIA_RangeValueValuePropertyId,True)
Expand Down
3 changes: 2 additions & 1 deletion source/NVDAObjects/window/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
from NVDAObjects import NVDAObject
from NVDAObjects.behaviors import EditableText, LiveText
import watchdog
from locationHelper import RectLTWH

re_WindowsForms=re.compile(r'^WindowsForms[0-9]*\.(.*)\.app\..*$')
re_ATL=re.compile(r'^ATL:(.*)$')
Expand Down Expand Up @@ -192,7 +193,7 @@ def _get_windowControlID(self):
def _get_location(self):
r=ctypes.wintypes.RECT()
ctypes.windll.user32.GetWindowRect(self.windowHandle,ctypes.byref(r))
return (r.left,r.top,r.right-r.left,r.bottom-r.top)
return RectLTWH.fromCompatibleType(r)

def _get_displayText(self):
"""The text at this object's location according to the display model for this object's window."""
Expand Down
5 changes: 2 additions & 3 deletions source/appModules/powerpnt.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import controlTypes
from logHandler import log
import scriptHandler
from locationHelper import RectLTRB
from NVDAObjects.window._msOfficeChart import OfficeChart

# Window classes where PowerPoint's object model should be used
Expand Down Expand Up @@ -738,9 +739,7 @@ def _get_location(self):
top=self.documentWindow.ppObjectModel.pointsToScreenPixelsY(pointTop)
right=self.documentWindow.ppObjectModel.pointsToScreenPixelsX(pointLeft+pointWidth)
bottom=self.documentWindow.ppObjectModel.pointsToScreenPixelsY(pointTop+pointHeight)
width=right-left
height=bottom-top
return (left,top,width,height)
return RectLTRB(left,top,right,bottom).toLTWH()

def _get_ppShapeType(self):
"""Fetches and caches the type of this shape."""
Expand Down
3 changes: 2 additions & 1 deletion source/displayModel.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import watchdog
from logHandler import log
import windowUtils
from locationHelper import RectLTRB

def wcharToInt(c):
i=ord(c)
Expand Down Expand Up @@ -178,7 +179,7 @@ def getWindowTextInRect(bindingHandle, windowHandle, left, top, right, bottom,mi
characterLocations = []
cpBufIt = iter(cpBuf)
for cp in cpBufIt:
characterLocations.append((wcharToInt(cp), wcharToInt(next(cpBufIt)), wcharToInt(next(cpBufIt)), wcharToInt(next(cpBufIt))))
characterLocations.append(RectLTRB(wcharToInt(cp), wcharToInt(next(cpBufIt)), wcharToInt(next(cpBufIt)), wcharToInt(next(cpBufIt))))
return text, characterLocations

def getFocusRect(obj):
Expand Down
Loading

0 comments on commit 91831b2

Please sign in to comment.