2121import watchdog
2222from logHandler import log
2323import windowUtils
24- from locationHelper import RectLTRB , RectLTWH
24+ from locationHelper import RectLTRB , RectLTWH , Point
2525import textUtils
2626from typing import Union , List , Tuple
2727
@@ -200,7 +200,15 @@ def getWindowTextInRect(bindingHandle, windowHandle, left, top, right, bottom,mi
200200 characterLocations = []
201201 cpBufIt = iter (cpBuf )
202202 for cp in cpBufIt :
203- characterLocations .append (RectLTRB (wcharToInt (cp ), wcharToInt (next (cpBufIt )), wcharToInt (next (cpBufIt )), wcharToInt (next (cpBufIt ))))
203+ left , top , right , bottom = (
204+ wcharToInt (cp ),
205+ wcharToInt (next (cpBufIt )),
206+ wcharToInt (next (cpBufIt )),
207+ wcharToInt (next (cpBufIt ))
208+ )
209+ if right < left :
210+ left , right = right , left
211+ characterLocations .append (RectLTRB (left , top , right , bottom ))
204212 return text , characterLocations
205213
206214def getFocusRect (obj ):
@@ -292,23 +300,31 @@ def _get__storyFieldsAndRects(self) -> Tuple[
292300 List [int ]
293301 ]:
294302 # All returned coordinates are logical coordinates.
295- if self ._location :
296- left , top , right , bottom = self ._location
297- else :
298- try :
299- left , top , width , height = self .obj .location
300- except TypeError :
301- # No location; nothing we can do.
302- return [], [], [], []
303- right = left + width
304- bottom = top + height
303+ location = self ._location if self ._location else self .obj .location
304+ if location is None or not any (location ):
305+ # No location; nothing we can do.
306+ return [], [], [], []
305307 bindingHandle = self .obj .appModule .helperLocalBindingHandle
306308 if not bindingHandle :
307309 log .debugWarning ("AppModule does not have a binding handle" )
308310 return [], [], [], []
309- left ,top = windowUtils .physicalToLogicalPoint (self .obj .windowHandle ,left ,top )
310- right ,bottom = windowUtils .physicalToLogicalPoint (self .obj .windowHandle ,right ,bottom )
311- text ,rects = getWindowTextInRect (bindingHandle , self .obj .windowHandle , left , top , right , bottom , self .minHorizontalWhitespace , self .minVerticalWhitespace ,self .stripOuterWhitespace ,self .includeDescendantWindows )
311+ try :
312+ location = location .toLogical (self .obj .windowHandle )
313+ except RuntimeError :
314+ log .exception ()
315+ return [], [], [], []
316+ text , rects = getWindowTextInRect (
317+ bindingHandle ,
318+ self .obj .windowHandle ,
319+ location .left ,
320+ location .top ,
321+ location .right ,
322+ location .bottom ,
323+ self .minHorizontalWhitespace ,
324+ self .minVerticalWhitespace ,
325+ self .stripOuterWhitespace ,
326+ self .includeDescendantWindows
327+ )
312328 if not text :
313329 return [], [], [], []
314330 text = "<control>%s</control>" % text
@@ -436,15 +452,29 @@ def _normalizeFormatField(self,field):
436452
437453 def _getOffsetFromPoint (self , x , y ):
438454 # Accepts physical coordinates.
439- x ,y = windowUtils .physicalToLogicalPoint (self .obj .windowHandle ,x ,y )
455+ try :
456+ x , y = windowUtils .physicalToLogicalPoint (
457+ self .obj .windowHandle ,
458+ x ,
459+ y
460+ )
461+ except RuntimeError :
462+ raise LookupError ("physicalToLogicalPoint failed" )
440463 for charOffset , (charLeft , charTop , charRight , charBottom ) in enumerate (self ._storyFieldsAndRects [1 ]):
441464 if charLeft <= x < charRight and charTop <= y < charBottom :
442465 return charOffset
443466 raise LookupError
444467
445468 def _getClosestOffsetFromPoint (self ,x ,y ):
446469 # Accepts physical coordinates.
447- x ,y = windowUtils .physicalToLogicalPoint (self .obj .windowHandle ,x ,y )
470+ try :
471+ x , y = windowUtils .physicalToLogicalPoint (
472+ self .obj .windowHandle ,
473+ x ,
474+ y
475+ )
476+ except RuntimeError :
477+ raise LookupError ("physicalToLogicalPoint failed" )
448478 #Enumerate the character rectangles
449479 a = enumerate (self ._storyFieldsAndRects [1 ])
450480 #Convert calculate center points for all the rectangles
@@ -462,7 +492,15 @@ def _getBoundingRectFromOffset(self, offset):
462492 rects = self ._storyFieldsAndRects [1 ]
463493 if not rects or offset >= len (rects ):
464494 raise LookupError
465- return rects [offset ].toPhysical (self .obj .windowHandle ).toLTWH ()
495+ rect = rects [offset ].toLTWH ()
496+ try :
497+ rect = rect .toPhysical (self .obj .windowHandle )
498+ except RuntimeError :
499+ raise LookupError (
500+ "Couldn't convert character rectangle at offset %d to physical coordinates"
501+ % offset
502+ )
503+ return rect
466504
467505 def _getNVDAObjectFromOffset (self ,offset ):
468506 try :
@@ -554,7 +592,15 @@ def _get_boundingRects(self):
554592 for lineEndOffset in lineEndOffsets :
555593 startOffset = endOffset
556594 endOffset = lineEndOffset
557- rects .append (RectLTWH .fromCollection (* self ._storyFieldsAndRects [1 ][startOffset :endOffset ]).toPhysical (self .obj .windowHandle ))
595+ lineRect = RectLTWH .fromCollection (* self ._storyFieldsAndRects [1 ][startOffset :endOffset ])
596+ try :
597+ lineRect = lineRect .toPhysical (self .obj .windowHandle )
598+ except RuntimeError :
599+ raise LookupError (
600+ f"Couldn't convert line rectangle at offsets { startOffset } to { endOffset } "
601+ "to physical coordinates"
602+ )
603+ rects .append (lineRect )
558604 return rects
559605
560606 def _getFirstVisibleOffset (self ):
@@ -598,11 +644,15 @@ def _findCaretOffsetFromLocation(
598644 def _getCaretOffset (self ):
599645 caretRect = getCaretRect (self .obj )
600646 objLocation = self .obj .location
601- objRect = objLocation .toLTRB ().toLogical (self .obj .windowHandle )
647+ try :
648+ objRect = objLocation .toLTRB ().toLogical (self .obj .windowHandle )
649+ except RuntimeError :
650+ raise RuntimeError (
651+ "Couldn't convert object location to logical coordinates when getting caret offset"
652+ )
602653 caretRect = caretRect .intersection (objRect )
603654 if not any (caretRect ):
604- raise RuntimeError ("The caret rectangle does not overlap with the window" )
605- # Find a character offset where the caret overlaps vertically, overlaps horizontally, overlaps the baseline and is totally within or on the correct side for the reading order
655+ raise RuntimeError ("The caret rectangle does not overlap with the window" ) # Find a character offset where the caret overlaps vertically, overlaps horizontally, overlaps the baseline and is totally within or on the correct side for the reading order
606656 try :
607657 return self ._findCaretOffsetFromLocation (caretRect ,validateBaseline = True ,validateDirection = True )
608658 except LookupError :
@@ -623,11 +673,14 @@ def _setCaretOffset(self,offset):
623673 if offset >= len (rects ):
624674 raise RuntimeError ("offset %d out of range" )
625675 rect = rects [offset ]
626- x = rect .left
627- y = rect .center .y
628- x ,y = windowUtils .logicalToPhysicalPoint (self .obj .windowHandle ,x ,y )
629- oldX ,oldY = winUser .getCursorPos ()
630- winUser .setCursorPos (x ,y )
676+ # Place the cursor at the left coordinate of the character, vertically centered.
677+ point = Point (rect .left , rect .center .y )
678+ try :
679+ point = point .toPhysical (self .obj .windowHandle )
680+ except RuntimeError :
681+ raise RuntimeError ("Conversion to physical coordinates failed when setting caret offset" )
682+ oldX , oldY = winUser .getCursorPos ()
683+ winUser .setCursorPos (* point )
631684 mouseHandler .executeMouseEvent (winUser .MOUSEEVENTF_LEFTDOWN ,0 ,0 )
632685 mouseHandler .executeMouseEvent (winUser .MOUSEEVENTF_LEFTUP ,0 ,0 )
633686 winUser .setCursorPos (oldX ,oldY )
0 commit comments