@@ -47,10 +47,10 @@ class EditableText(TextContainerObject,ScriptableObject):
4747 announceEntireNewLine = False
4848
4949 #: The minimum amount of time that should elapse before checking if the word under the caret has changed
50- _hasCaretMoved_minWordTimeoutMs = 30
50+ _hasCaretMoved_minWordTimeoutSec = 0.03
5151
5252 #: The maximum amount of time that may elapse before we no longer rely on caret events to detect movement.
53- _useEvents_maxTimeoutMs = 10
53+ _useEvents_maxTimeoutSec = 0.01
5454
5555 _caretMovementTimeoutMultiplier = 1
5656
@@ -71,22 +71,18 @@ def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=None, origWord=No
7171 @rtype: tuple
7272 """
7373 if timeout is None :
74- timeoutMs = config .conf ["editableText" ]["caretMoveTimeoutMs" ]
75- else :
76- # This function's arguments are in seconds, but we want ms.
77- timeoutMs = timeout * 1000
78- timeoutMs *= self ._caretMovementTimeoutMultiplier
79- # time.sleep accepts seconds, so retryInterval is in seconds.
80- # Convert to integer ms to avoid floating point precision errors when adding to elapsed.
81- retryMs = int (retryInterval * 1000 )
74+ timeout = config .conf ["editableText" ]["caretMoveTimeoutMs" ] / 1000
75+ timeout *= self ._caretMovementTimeoutMultiplier
76+ start = time .time ()
8277 elapsed = 0
8378 newInfo = None
79+ retries = 0
8480 while True :
8581 if isScriptWaiting ():
8682 return (False ,None )
8783 api .processPendingEvents (processEventQueue = False )
8884 if eventHandler .isPendingEvents ("gainFocus" ):
89- log .debug ("Focus event. Elapsed: %d ms " % elapsed )
85+ log .debug ("Focus event. Elapsed %g sec " % elapsed )
9086 return (True ,None )
9187 # If the focus changes after this point, fetching the caret may fail,
9288 # but we still want to stay in this loop.
@@ -97,14 +93,17 @@ def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=None, origWord=No
9793 else :
9894 # Caret events are unreliable in some controls.
9995 # Only use them if we consider them safe to rely on for a particular control,
100- # and only if they arrive within C{_useEvents_maxTimeoutMs} mili seconds
96+ # and only if they arrive within C{_useEvents_maxTimeoutSec} seconds
10197 # after causing the event to occur.
10298 if (
103- elapsed <= self ._useEvents_maxTimeoutMs and
99+ elapsed <= self ._useEvents_maxTimeoutSec and
104100 self .caretMovementDetectionUsesEvents and
105101 (eventHandler .isPendingEvents ("caret" ) or eventHandler .isPendingEvents ("textChange" ))
106102 ):
107- log .debug ("Caret move detected using event. Elapsed: %d ms" % elapsed )
103+ log .debug (
104+ "Caret move detected using event. Elapsed %g sec, retries %d"
105+ % (elapsed , retries )
106+ )
108107 return (True ,newInfo )
109108 # Try to detect with bookmarks.
110109 newBookmark = None
@@ -114,9 +113,12 @@ def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=None, origWord=No
114113 except (RuntimeError ,NotImplementedError ):
115114 pass
116115 if newBookmark and newBookmark != bookmark :
117- log .debug ("Caret move detected using bookmarks. Elapsed: %d ms" % elapsed )
116+ log .debug (
117+ "Caret move detected using bookmarks. Elapsed %g sec, retries %d"
118+ % (elapsed , retries )
119+ )
118120 return (True , newInfo )
119- if origWord is not None and newInfo and elapsed >= self ._hasCaretMoved_minWordTimeoutMs :
121+ if origWord is not None and newInfo and elapsed >= self ._hasCaretMoved_minWordTimeoutSec :
120122 # When pressing delete, bookmarks might not be enough to detect caret movement.
121123 # Therefore try detecting if the word under the caret has changed, such as when pressing delete.
122124 # some editors such as Mozilla Gecko can have text and units that get out of sync with eachother while a character is being deleted.
@@ -125,13 +127,21 @@ def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=None, origWord=No
125127 wordInfo .expand (textInfos .UNIT_WORD )
126128 word = wordInfo .text
127129 if word != origWord :
128- log .debug ("Word at caret changed. Elapsed: %d ms " % elapsed )
130+ log .debug ("Word at caret changed. Elapsed: %g sec " % elapsed )
129131 return (True , newInfo )
130- if elapsed >= timeoutMs :
132+ elapsed = time .time () - start
133+ if elapsed >= timeout :
131134 break
132- time .sleep (retryInterval )
133- elapsed += retryMs
134- log .debug ("Caret didn't move before timeout. Elapsed: %d ms" % elapsed )
135+ # We spin the first few tries, as sleep is not accurate for tiny periods
136+ # and we might end up sleeping longer than we need to. Spinning improves
137+ # responsiveness in the case that the app responds fairly quickly.
138+ if retries > 2 :
139+ # Don't spin too long, though. If we get to this point, the app is
140+ # probably taking a while to respond, so super fast response is
141+ # already lost.
142+ time .sleep (retryInterval )
143+ retries += 1
144+ log .debug ("Caret didn't move before timeout. Elapsed: %g sec" % elapsed )
135145 return (False ,newInfo )
136146
137147 def _caretScriptPostMovedHelper (self , speakUnit , gesture , info = None ):
0 commit comments