Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | |
| 5 var RoleType = chrome.automation.RoleType; | |
| 6 | |
| 7 /** | |
| 8 * @constructor | |
|
David Tseng
2016/11/22 19:01:09
Are you planning on compiling this file (at least
dmazzoni
2016/11/28 18:51:12
Yes. I finished adding that to this change now. To
| |
| 9 */ | |
| 10 SelectToSpeak = function() { | |
| 11 this.node_ = null; | |
| 12 this.down_ = false; | |
| 13 | |
| 14 chrome.automation.getDesktop(function(d) { | |
| 15 d.addEventListener('mousePressed', this.onMousePressed_.bind(this), true); | |
|
David Tseng
2016/11/22 19:01:10
chrome.automation.EventType
dmazzoni
2016/11/28 18:51:12
Done.
| |
| 16 d.addEventListener('mouseDragged', this.onMouseDragged_.bind(this), true); | |
| 17 d.addEventListener('mouseMoved', this.onMouseDragged_.bind(this), true); | |
|
David Tseng
2016/11/22 19:01:09
onMouseDraggedOrMoved? (or were these supposed to
dmazzoni
2016/11/28 18:51:13
Actually we don't need to handle mouseMoved. Fixed
| |
| 18 d.addEventListener('mouseReleased', this.onMouseReleased_.bind(this), true); | |
| 19 d.addEventListener('mouseCanceled', this.onMouseCanceled_.bind(this), true); | |
| 20 }.bind(this)); | |
| 21 }; | |
| 22 | |
| 23 SelectToSpeak.prototype = { | |
| 24 /** | |
| 25 * Called when the mouse is pressed and the user is in a mode where | |
| 26 * select-to-speak is capturing mouse events (for example holding down | |
| 27 * Search). | |
| 28 * | |
| 29 * @param {!AutomationEvent} evt | |
| 30 */ | |
| 31 onMousePressed_: function(evt) { | |
| 32 this.down_ = true; | |
| 33 this.mouseStart_ = {x: evt.mouseX, y: evt.mouseY}; | |
| 34 this.startNode_ = evt.target; | |
| 35 this.speechQueue_ = []; | |
| 36 chrome.tts.stop(); | |
| 37 this.onMouseDragged_(evt); | |
| 38 }, | |
| 39 | |
| 40 /** | |
| 41 * Called when the mouse is moved or dragged and the user is in a | |
| 42 * mode where select-to-speak is capturing mouse events (for example | |
|
David Tseng
2016/11/22 19:01:10
Select to speak only captures if search is down, r
dmazzoni
2016/11/28 18:51:13
Currently yes but I wanted to write the comment in
| |
| 43 * holding down Search). | |
| 44 * | |
| 45 * @param {!AutomationEvent} evt | |
| 46 */ | |
| 47 onMouseDragged_: function(evt) { | |
| 48 if (!this.down_) | |
|
David Tseng
2016/11/22 19:01:10
Doesn't a mouse drag already mean a mouse down? I
dmazzoni
2016/11/28 18:51:12
This is still needed to handle a race condition wh
| |
| 49 return; | |
| 50 | |
| 51 var rect = {left: Math.floor(this.mouseStart_.x), | |
| 52 top: Math.floor(this.mouseStart_.y), | |
|
David Tseng
2016/11/22 19:01:10
How does this work if I drag up or to the left? Do
dmazzoni
2016/11/28 18:51:12
Good point. Fixed.
| |
| 53 width: Math.floor(evt.mouseX - this.mouseStart_.x), | |
| 54 height: Math.floor(evt.mouseY - this.mouseStart_.y)}; | |
| 55 chrome.accessibilityPrivate.setFocusRing([rect]); | |
| 56 }, | |
| 57 | |
| 58 /** | |
| 59 * Called when the mouse is moved or dragged and the user is in a | |
| 60 * mode where select-to-speak is capturing mouse events (for example | |
|
David Tseng
2016/11/22 19:01:10
Update the comment
dmazzoni
2016/11/28 18:51:12
Done.
| |
| 61 * holding down Search). | |
| 62 * | |
| 63 * @param {!AutomationEvent} evt | |
| 64 */ | |
| 65 onMouseReleased_: function(evt) { | |
| 66 this.onMouseDragged_(evt); | |
| 67 this.down_ = false; | |
| 68 | |
| 69 chrome.accessibilityPrivate.setFocusRing([]); | |
|
David Tseng
2016/11/22 19:01:10
I would have expected this to happen when s2s goes
dmazzoni
2016/11/28 18:51:12
That's handled in onMouseCanceled.
For onMouseRel
| |
| 70 | |
| 71 // Walk up to the nearest window, web area, or dialog that the | |
| 72 // hit node is contained inside. Only speak objects within that | |
| 73 // container. In the future we might include other container-like | |
| 74 // roles here. | |
| 75 var root = this.startNode_; | |
| 76 while (root.parent && | |
| 77 root.role != RoleType.window && | |
| 78 root.role != RoleType.rootWebArea && | |
| 79 root.role != RoleType.desktop && | |
| 80 root.role != RoleType.dialog) { | |
| 81 root = root.parent; | |
| 82 } | |
| 83 | |
| 84 var rect = {left: Math.floor(this.mouseStart_.x), | |
| 85 top: Math.floor(this.mouseStart_.y), | |
| 86 width: Math.floor(evt.mouseX - this.mouseStart_.x), | |
| 87 height: Math.floor(evt.mouseY - this.mouseStart_.y)}; | |
| 88 var nodes = []; | |
| 89 this.findAllMatching_(root, rect, nodes); | |
| 90 | |
| 91 this.speechQueue_ = nodes; | |
| 92 this.speakNextInQueue_(); | |
| 93 }, | |
| 94 | |
| 95 /** | |
| 96 * Called when the user cancels select-to-speak's capturing of mouse | |
| 97 * events (for example by releasing Search while the mouse is still down). | |
| 98 * | |
| 99 * @param {!AutomationEvent} evt | |
| 100 */ | |
| 101 onMouseCanceled_: function(evt) { | |
| 102 this.down_ = false; | |
| 103 chrome.accessibilityPrivate.setFocusRing([]); | |
| 104 this.speechQueue_ = []; | |
| 105 chrome.tts.stop(); | |
| 106 }, | |
| 107 | |
| 108 /** | |
| 109 * Returns true if |rect1| and |rect2| overlap. The rects must define | |
| 110 * left, top, width, and height. | |
| 111 * @param {Object} rect1 | |
| 112 * @param {Object} rect2 | |
| 113 * @return {boolean} True if the rects overlap. | |
| 114 */ | |
| 115 overlaps_: function(rect1, rect2) { | |
|
David Tseng
2016/11/22 19:01:09
No need to define this function in this class; mak
dmazzoni
2016/11/28 18:51:12
Done.
| |
| 116 var l1 = rect1.left; | |
| 117 var r1 = rect1.left + rect1.width; | |
| 118 var t1 = rect1.top; | |
| 119 var b1 = rect1.top + rect1.height; | |
| 120 var l2 = rect2.left; | |
| 121 var r2 = rect2.left + rect2.width; | |
| 122 var t2 = rect2.top; | |
| 123 var b2 = rect2.top + rect2.height; | |
| 124 return (l1 < r2 && r1 > l2 && t1 < b2 && b1 > t2); | |
|
David Tseng
2016/11/22 19:01:09
Doesn't look right. What happens if the two rects
dmazzoni
2016/11/28 18:51:13
Both of those cases work as long as the rects have
| |
| 125 }, | |
| 126 | |
| 127 /** | |
| 128 * Finds all nodes within the subtree rooted at |node| that overlap | |
| 129 * a given rectangle. | |
| 130 * @param {AutomationNode} The starting node. | |
| 131 * @param {Object} rect The bounding box to search. | |
| 132 * @param {Array<AutomationNode>} nodes The matching node array to be | |
| 133 * populated. | |
| 134 * @return {boolean} True if any matches are found. | |
| 135 */ | |
| 136 findAllMatching_: function(node, rect, nodes) { | |
| 137 var found = false; | |
| 138 for (var c = node.firstChild; c; c = c.nextSibling) { | |
| 139 if (this.findAllMatching_(c, rect, nodes)) | |
| 140 found = true; | |
| 141 } | |
| 142 | |
| 143 if (found) | |
| 144 return true; | |
| 145 | |
| 146 if (!node.name) | |
|
David Tseng
2016/11/22 19:01:10
This is going to give some strange results I think
dmazzoni
2016/11/28 18:51:13
I think it's actually useful that it reads the alt
| |
| 147 return false; | |
| 148 | |
| 149 if (this.overlaps_(node.location, rect)) { | |
| 150 nodes.push(node); | |
| 151 return true; | |
| 152 } | |
| 153 | |
| 154 return false; | |
| 155 }, | |
| 156 | |
| 157 /** | |
| 158 * Pop the next matching node from the queue and speak and highlight it. | |
| 159 */ | |
| 160 speakNextInQueue_: function() { | |
| 161 if (!this.speechQueue_ || this.speechQueue_.length == 0) { | |
|
David Tseng
2016/11/22 19:01:10
Looks like type checking would help here. Set this
dmazzoni
2016/11/28 18:51:12
Done.
| |
| 162 chrome.accessibilityPrivate.setFocusRing([]); | |
| 163 return; | |
| 164 } | |
| 165 | |
| 166 var node = this.speechQueue_.shift(); | |
| 167 chrome.accessibilityPrivate.setFocusRing([node.location]); | |
| 168 chrome.tts.speak(node.name, { | |
| 169 lang: 'en-US', | |
|
David Tseng
2016/11/22 19:01:10
Maybe we should think about starting some code sha
dmazzoni
2016/11/28 18:51:13
Agreed, perhaps some common utility libraries.
| |
| 170 'enqueue': false, | |
|
David Tseng
2016/11/22 19:01:09
Can we let the native tts queue handle things?
dmazzoni
2016/11/28 18:51:12
Done, it just means slightly trickier logic inside
| |
| 171 onEvent: (function(event) { | |
| 172 if (event.type == 'end') { | |
| 173 this.speakNextInQueue_(); | |
| 174 } else if (event.type == 'interrupted' || | |
| 175 event.type == 'cancelled') { | |
| 176 chrome.accessibilityPrivate.setFocusRing([]); | |
| 177 } | |
| 178 }).bind(this) | |
| 179 }); | |
| 180 } | |
| 181 }; | |
| 182 | |
| 183 new SelectToSpeak(); | |
| OLD | NEW |