-
-
Notifications
You must be signed in to change notification settings - Fork 651
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
Increase default caret move timeout from 30 to 100 ms and make it configurable #7201
Conversation
Ug. Accidentally clicked submit before I was finished. I've updated the initial comment. |
…figurable. - 30 ms was chosen to ensure maximum responsiveness, but this just isn't enough in the wild; e.g. in complex web editors (particularly in Chrome) or when connecting to remote terminals with connection latency. - This is configurable via config.conf["editableText"]["caretMoveTimeout"]. It is not exposed in the GUI, as this should only be changed by advanced users. The value is in ms, not seconds, as that's a better unit for this purpose. However, the function continues to use seconds for its arguments for backwards compatibility. - Previously, _hasCaretMoved slept at the end of the loop but didn't check for caret movement again. This meant that it could return False even if the caret did actually move during the last sleep. Now, the timeout check happens after the caret movement check, ensuring the correct result. - _hasCaretMoved now works with ms rather than seconds to avoid floating point precision errors. These precision errors could result in an extraneous additional retry. - Added some debug logging to help diagnose caret tracking problems in future.
963c3c8
to
a65c5b9
Compare
@jcsteh Thank you very much! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Another PR with a great description!
source/config/configSpec.py
Outdated
@@ -192,6 +192,9 @@ | |||
|
|||
[upgrade] | |||
newLaptopKeyboardLayout = boolean(default=false) | |||
|
|||
[editableText] | |||
caretMoveTimeout = integer(min=0, max=2000, default=100) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be tempted to mention the units as part of the name for this config option, something like caretMoveTimeoutMS
or caretMoveTimeoutMilliSec
source/editableText.py
Outdated
timeout = config.conf["editableText"]["caretMoveTimeout"] | ||
else: | ||
# This function's arguments are in seconds, but we want ms. | ||
timeout *= 1000 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since timeout is documented on the function to be in seconds, I think it could be easy to miss that its converted into milliseconds. Perhaps store the milliseconds in a new var, timeoutMs
source/editableText.py
Outdated
@@ -42,21 +43,30 @@ class EditableText(ScriptableObject): | |||
#: Whether or not to announce text found before the caret on a new line (e.g. auto numbering) | |||
announceNewLineText=True | |||
|
|||
def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=0.03): | |||
def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=None): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the description of this PR you mention backwards compat for this function I think? Changing timeout to None
will risk breaking that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's certainly a valid point, but I think it's a negligible/acceptable risk.
- It's most likely that a caller would call this function either accepting the defaults or passing its own timeout, both of which will still work as expected.
- This doesn't call super, so there's no chance that some method will receive
None
that wasn't expecting it. - The only risk I can think of is that a subclass might have hard-coded the old 0.03 default, in which case they won't get the user's configured value. However, at worst, this means some subclass won't behave consistently with the rest of NVDA, and this would be pretty clear from the debug log if caret movement started timing out. I also think the likelihood of someone having done this is unlikely.
- Since it's a private method, maintaining backwards compat is a courtesy anyway, not an obligation. :)
I am a little uncertain here. Am I correct in assuming that this pull
request and its corresponding code is part of the latest Next snapshot
of NVDA?
…On 5/25/17, Reef Turner ***@***.***> wrote:
feerrenrut requested changes on this pull request.
Looks good. Another PR with a great description!
> @@ -192,6 +192,9 @@
[upgrade]
newLaptopKeyboardLayout = boolean(default=false)
+
+[editableText]
+ caretMoveTimeout = integer(min=0, max=2000, default=100)
I would be tempted to mention the units as part of the name for this config
option, something like `caretMoveTimeoutMS` or `caretMoveTimeoutMilliSec`
> @type timeout: float
@return: a tuple containing a boolean denoting whether this method timed
out, and a TextInfo representing the old or updated caret position or None
if interupted by a script or focus event.
@rtype: tuple
- """
+ """
+ if timeout is None:
+ timeout = config.conf["editableText"]["caretMoveTimeout"]
+ else:
+ # This function's arguments are in seconds, but we want ms.
+ timeout *= 1000
Since timeout is documented on the function to be in seconds, I think it
could be easy to miss that its converted into milliseconds. Perhaps store
the milliseconds in a new var, `timeoutMs`
> @@ -42,21 +43,30 @@ class EditableText(ScriptableObject):
#: Whether or not to announce text found before the caret on a new line
(e.g. auto numbering)
announceNewLineText=True
- def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=0.03):
+ def _hasCaretMoved(self, bookmark, retryInterval=0.01, timeout=None):
In the description of this PR you mention backwards compat for this function
I think? Changing timeout to `None` will risk breaking that.
--
You are receiving this because you are subscribed to this thread.
Reply to this email directly or view it on GitHub:
#7201 (review)
--
Best Regards
Bhavya Shah
Blogger at Hiking Across Horizons: https://bhavyashah125.wordpress.com/
Contacting Me
E-mail Address: bhavya.shah125@gmail.com
Follow me on Twitter @BhavyaShah125 or www.twitter.com/BhavyaShah125
Mobile Number: +91 7506221750
|
@bhavyashah No, it's not in next. It's still undergoing code review. Once
review is approved, it will be merged to next. For your information, a pull
request that is in next will have the "incubating" label applied.
|
@feerrenrut, this is ready for review round 2. Thanks! :) |
…g valueChange events as caret moves. Also, treat caret events as caret moves as well, as this should be more reliable than comparing bookmarks in some cases.
@feerrenrut, would you remind reviewing the latest commit? @tspivey reported that this change was making the delete key feel laggy, which I've now fixed for most cases. |
Arrrg. This latest change breaks Chrome again. Looks like caret events get fired even though Chrome hasn't updated the caret yet. I'm guessing they're MSAA system caret events, rather than IA2 caret events. |
… uses the system caret, but this moves before IA2 is updated. Ignore system caret events for IA2 implementations and rely only on IA2 caret events.
@feerrenrut, it's not my week. Review latest commit pretty please? :) |
…sed by c967b21. Firefox fires caret events even when you use caret movement commands when already at the edge of the control. That means we don't fire caretMovementFailed events in these cases. Furthermore, it seems quite a few things (e.g. Wordpad, Notepad++) don't fire valueChange or caret events when you press delete. To work around this, drop the checks for caret and valueChange events (and revert the shouldAllowSystemCaretEvent stuff). Instead, when pressing delete, compare the word at the caret (in addition to bookmarks) to detect movement.
@feerrenrut, yet another regression fix for you to review when you have a chance. Thanks. |
…if the word at the caret has changed once _hasCaretMoved_minWordTimeoutMs has elapsed. I.e. 30 ms must elapse before starting to check the word. This fixes a regression introduced by #7201 where deleting characters in Mozilla Gecko editable controls would cause the character being deleted to be spoken rather than the character that followed.
…if the word at the caret has changed once _hasCaretMoved_minWordTimeoutMs has elapsed. I.e. 30 ms must elapse before starting to check the word. (#7496) This fixes a regression introduced by #7201 where deleting characters in Mozilla Gecko editable controls would cause the character being deleted to be spoken rather than the character that followed.
Dear |
hello and pardon me for very late reply. |
Link to issue number:
Summary of the issue:
NVDA has a very conservative cursor movement timeout of 30 ms. That is, when the user presses a cursor key, backspace, etc., we check the caret position, send the key press and then wait about 30 ms for the caret to move. 30 ms was chosen to ensure maximum responsiveness, but this just isn't enough in the wild; e.g. in complex web editors (particularly in Chrome) or when connecting to remote terminals with connection latency.
Description of how this pull request fixes the issue:
editableText.EditableText._hasCaretMoved
._hasCaretMoved
now works with ms rather than seconds to avoid floating point precision errors. These precision errors could result in an extraneous additional retry.Testing performed:
Known issues with pull request:
None known.
Change log entry:
In Bug Fixes: