OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 | 4 |
5 /** | 5 /** |
6 * @fileoverview Processes events related to editing text and emits the | 6 * @fileoverview Processes events related to editing text and emits the |
7 * appropriate spoken and braille feedback. | 7 * appropriate spoken and braille feedback. |
8 */ | 8 */ |
9 | 9 |
10 goog.provide('editing.TextEditHandler'); | 10 goog.provide('editing.TextEditHandler'); |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 /** | 67 /** |
68 * A |TextEditHandler| suitable for text fields. | 68 * A |TextEditHandler| suitable for text fields. |
69 * @constructor | 69 * @constructor |
70 * @param {!AutomationNode} node A node with the role of |textField| | 70 * @param {!AutomationNode} node A node with the role of |textField| |
71 * @extends {editing.TextEditHandler} | 71 * @extends {editing.TextEditHandler} |
72 */ | 72 */ |
73 function TextFieldTextEditHandler(node) { | 73 function TextFieldTextEditHandler(node) { |
74 editing.TextEditHandler.call(this, node); | 74 editing.TextEditHandler.call(this, node); |
75 | 75 |
76 chrome.automation.getDesktop(function(desktop) { | 76 chrome.automation.getDesktop(function(desktop) { |
77 var useRichText = editing.useRichText && | 77 var useRichText = editing.useRichText && node.state.richlyEditable; |
78 node.state.richlyEditable; | |
79 | 78 |
80 /** @private {!AutomationEditableText} */ | 79 /** @private {!AutomationEditableText} */ |
81 this.editableText_ = useRichText ? | 80 this.editableText_ = useRichText ? new AutomationRichEditableText(node) : |
82 new AutomationRichEditableText(node) : new AutomationEditableText(node); | 81 new AutomationEditableText(node); |
83 }.bind(this)); | 82 }.bind(this)); |
84 } | 83 } |
85 | 84 |
86 TextFieldTextEditHandler.prototype = { | 85 TextFieldTextEditHandler.prototype = { |
87 __proto__: editing.TextEditHandler.prototype, | 86 __proto__: editing.TextEditHandler.prototype, |
88 | 87 |
89 /** @override */ | 88 /** @override */ |
90 onEvent: function(evt) { | 89 onEvent: function(evt) { |
91 if (evt.type !== EventType.TEXT_CHANGED && | 90 if (evt.type !== EventType.TEXT_CHANGED && |
92 evt.type !== EventType.TEXT_SELECTION_CHANGED && | 91 evt.type !== EventType.TEXT_SELECTION_CHANGED && |
93 evt.type !== EventType.VALUE_CHANGED && | 92 evt.type !== EventType.VALUE_CHANGED && evt.type !== EventType.FOCUS) |
94 evt.type !== EventType.FOCUS) | |
95 return; | 93 return; |
96 if (!evt.target.state.focused || | 94 if (!evt.target.state.focused || !evt.target.state.editable || |
97 !evt.target.state.editable || | |
98 evt.target != this.node_) | 95 evt.target != this.node_) |
99 return; | 96 return; |
100 | 97 |
101 this.editableText_.onUpdate(); | 98 this.editableText_.onUpdate(); |
102 }, | 99 }, |
103 }; | 100 }; |
104 | 101 |
105 /** | 102 /** |
106 * A |ChromeVoxEditableTextBase| that implements text editing feedback | 103 * A |ChromeVoxEditableTextBase| that implements text editing feedback |
107 * for automation tree text fields. | 104 * for automation tree text fields. |
108 * @constructor | 105 * @constructor |
109 * @param {!AutomationNode} node | 106 * @param {!AutomationNode} node |
110 * @extends {cvox.ChromeVoxEditableTextBase} | 107 * @extends {cvox.ChromeVoxEditableTextBase} |
111 */ | 108 */ |
112 function AutomationEditableText(node) { | 109 function AutomationEditableText(node) { |
113 if (!node.state.editable) | 110 if (!node.state.editable) |
114 throw Error('Node must have editable state set to true.'); | 111 throw Error('Node must have editable state set to true.'); |
115 var start = node.textSelStart; | 112 var start = node.textSelStart; |
116 var end = node.textSelEnd; | 113 var end = node.textSelEnd; |
117 cvox.ChromeVoxEditableTextBase.call( | 114 cvox.ChromeVoxEditableTextBase.call( |
118 this, | 115 this, node.value || '', Math.min(start, end), Math.max(start, end), |
119 node.value || '', | 116 node.state[StateType.PROTECTED] /**password*/, cvox.ChromeVox.tts); |
120 Math.min(start, end), | |
121 Math.max(start, end), | |
122 node.state[StateType.PROTECTED] /**password*/, | |
123 cvox.ChromeVox.tts); | |
124 /** @override */ | 117 /** @override */ |
125 this.multiline = node.state[StateType.MULTILINE] || false; | 118 this.multiline = node.state[StateType.MULTILINE] || false; |
126 /** @type {!AutomationNode} @private */ | 119 /** @type {!AutomationNode} @private */ |
127 this.node_ = node; | 120 this.node_ = node; |
128 /** @type {Array<number>} @private */ | 121 /** @type {Array<number>} @private */ |
129 this.lineBreaks_ = []; | 122 this.lineBreaks_ = []; |
130 } | 123 } |
131 | 124 |
132 AutomationEditableText.prototype = { | 125 AutomationEditableText.prototype = { |
133 __proto__: cvox.ChromeVoxEditableTextBase.prototype, | 126 __proto__: cvox.ChromeVoxEditableTextBase.prototype, |
134 | 127 |
135 /** | 128 /** |
136 * Called when the text field has been updated. | 129 * Called when the text field has been updated. |
137 */ | 130 */ |
138 onUpdate: function() { | 131 onUpdate: function() { |
139 var newValue = this.node_.value || ''; | 132 var newValue = this.node_.value || ''; |
140 | 133 |
141 if (this.value != newValue) | 134 if (this.value != newValue) |
142 this.lineBreaks_ = []; | 135 this.lineBreaks_ = []; |
143 | 136 |
144 var textChangeEvent = new cvox.TextChangeEvent( | 137 var textChangeEvent = new cvox.TextChangeEvent( |
145 newValue, | 138 newValue, this.node_.textSelStart || 0, this.node_.textSelEnd || 0, |
146 this.node_.textSelStart || 0, | |
147 this.node_.textSelEnd || 0, | |
148 true /* triggered by user */); | 139 true /* triggered by user */); |
149 this.changed(textChangeEvent); | 140 this.changed(textChangeEvent); |
150 this.outputBraille_(); | 141 this.outputBraille_(); |
151 }, | 142 }, |
152 | 143 |
153 /** @override */ | 144 /** @override */ |
154 getLineIndex: function(charIndex) { | 145 getLineIndex: function(charIndex) { |
155 if (!this.multiline) | 146 if (!this.multiline) |
156 return 0; | 147 return 0; |
157 var breaks = this.node_.lineBreaks || []; | 148 var breaks = this.node_.lineBreaks || []; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
234 AutomationRichEditableText.prototype = { | 225 AutomationRichEditableText.prototype = { |
235 __proto__: AutomationEditableText.prototype, | 226 __proto__: AutomationEditableText.prototype, |
236 | 227 |
237 /** @override */ | 228 /** @override */ |
238 onUpdate: function() { | 229 onUpdate: function() { |
239 var root = this.node_.root; | 230 var root = this.node_.root; |
240 if (!root.anchorObject || !root.focusObject) | 231 if (!root.anchorObject || !root.focusObject) |
241 return; | 232 return; |
242 | 233 |
243 var cur = new editing.EditableLine( | 234 var cur = new editing.EditableLine( |
244 root.anchorObject, root.anchorOffset || 0, | 235 root.anchorObject, root.anchorOffset || 0, root.focusObject, |
245 root.focusObject, root.focusOffset || 0); | 236 root.focusOffset || 0); |
246 var prev = this.line_; | 237 var prev = this.line_; |
247 this.line_ = cur; | 238 this.line_ = cur; |
248 | 239 |
249 if (prev.equals(cur)) { | 240 if (prev.equals(cur)) { |
250 // Collapsed cursor. | 241 // Collapsed cursor. |
251 this.changed(new cvox.TextChangeEvent( | 242 this.changed(new cvox.TextChangeEvent( |
252 cur.text || '', | 243 cur.text || '', cur.startOffset || 0, cur.endOffset || 0, true)); |
253 cur.startOffset || 0, | |
254 cur.endOffset || 0, | |
255 true)); | |
256 | 244 |
257 var value = cur.value_; | 245 var value = cur.value_; |
258 value.setSpan(new cvox.ValueSpan(0), 0, cur.value_.length); | 246 value.setSpan(new cvox.ValueSpan(0), 0, cur.value_.length); |
259 value.setSpan( | 247 value.setSpan( |
260 new cvox.ValueSelectionSpan(), cur.startOffset, cur.endOffset); | 248 new cvox.ValueSelectionSpan(), cur.startOffset, cur.endOffset); |
261 cvox.ChromeVox.braille.write(new cvox.NavBraille({text: value, | 249 cvox.ChromeVox.braille.write(new cvox.NavBraille( |
262 startIndex: cur.startOffset, | 250 {text: value, startIndex: cur.startOffset, endIndex: cur.endOffset})); |
263 endIndex: cur.endOffset})); | |
264 | 251 |
265 // Finally, queue up any text markers/styles at bounds. | 252 // Finally, queue up any text markers/styles at bounds. |
266 var container = cur.lineContainer_; | 253 var container = cur.lineContainer_; |
267 if (!container) | 254 if (!container) |
268 return; | 255 return; |
269 | 256 |
270 if (container.markerTypes) { | 257 if (container.markerTypes) { |
271 // Only consider markers that start or end at the selection bounds. | 258 // Only consider markers that start or end at the selection bounds. |
272 var markerStartIndex = -1, markerEndIndex = -1; | 259 var markerStartIndex = -1, markerEndIndex = -1; |
273 var localStartOffset = cur.localStartOffset; | 260 var localStartOffset = cur.localStartOffset; |
(...skipping 30 matching lines...) Expand all Loading... |
304 | 291 |
305 // Just output the current line. | 292 // Just output the current line. |
306 if (!cur.lineStart_ || !cur.lineEnd_) | 293 if (!cur.lineStart_ || !cur.lineEnd_) |
307 return; | 294 return; |
308 var prevRange = null; | 295 var prevRange = null; |
309 if (prev.lineStart_ && prev.lineEnd_) { | 296 if (prev.lineStart_ && prev.lineEnd_) { |
310 prevRange = new Range( | 297 prevRange = new Range( |
311 Cursor.fromNode(prev.lineStart_), Cursor.fromNode(prev.lineEnd_)); | 298 Cursor.fromNode(prev.lineStart_), Cursor.fromNode(prev.lineEnd_)); |
312 } | 299 } |
313 | 300 |
314 new Output().withRichSpeechAndBraille(new Range( | 301 new Output() |
315 Cursor.fromNode(cur.lineStart_), Cursor.fromNode(cur.lineEnd_)), | 302 .withRichSpeechAndBraille( |
316 prevRange, | 303 new Range( |
317 Output.EventType.NAVIGATE).go(); | 304 Cursor.fromNode(cur.lineStart_), Cursor.fromNode(cur.lineEnd_)), |
| 305 prevRange, Output.EventType.NAVIGATE) |
| 306 .go(); |
318 }, | 307 }, |
319 | 308 |
320 /** | 309 /** |
321 * @param {number} markerType | 310 * @param {number} markerType |
322 * @param {boolean=} opt_end | 311 * @param {boolean=} opt_end |
323 * @private | 312 * @private |
324 */ | 313 */ |
325 speakTextMarker_: function(markerType, opt_end) { | 314 speakTextMarker_: function(markerType, opt_end) { |
326 // TODO(dtseng): Plumb through constants to automation. | 315 // TODO(dtseng): Plumb through constants to automation. |
327 var msgs = []; | 316 var msgs = []; |
328 if (markerType & 1) | 317 if (markerType & 1) |
329 msgs.push(opt_end ? 'misspelling_end' : 'misspelling_start'); | 318 msgs.push(opt_end ? 'misspelling_end' : 'misspelling_start'); |
330 if (markerType & 2) | 319 if (markerType & 2) |
331 msgs.push(opt_end ? 'grammar_end' : 'grammar_start'); | 320 msgs.push(opt_end ? 'grammar_end' : 'grammar_start'); |
332 if (markerType & 4) | 321 if (markerType & 4) |
333 msgs.push(opt_end ? 'text_match_end' : 'text_match_start'); | 322 msgs.push(opt_end ? 'text_match_end' : 'text_match_start'); |
334 | 323 |
335 if (msgs.length) { | 324 if (msgs.length) { |
336 msgs.forEach(function(msg) { | 325 msgs.forEach(function(msg) { |
337 cvox.ChromeVox.tts.speak(Msgs.getMsg(msg), | 326 cvox.ChromeVox.tts.speak( |
338 cvox.QueueMode.QUEUE, | 327 Msgs.getMsg(msg), cvox.QueueMode.QUEUE, |
339 cvox.AbstractTts.PERSONALITY_ANNOTATION); | 328 cvox.AbstractTts.PERSONALITY_ANNOTATION); |
340 }); | 329 }); |
341 } | 330 } |
342 }, | 331 }, |
343 | 332 |
344 /** | 333 /** |
345 * @param {!AutomationNode} style | 334 * @param {!AutomationNode} style |
346 * @param {boolean=} opt_end | 335 * @param {boolean=} opt_end |
347 * @private | 336 * @private |
348 */ | 337 */ |
349 speakTextStyle_: function(style, opt_end) { | 338 speakTextStyle_: function(style, opt_end) { |
350 var msgs = []; | 339 var msgs = []; |
351 if (style.bold) | 340 if (style.bold) |
352 msgs.push(opt_end ? 'bold_end' : 'bold_start'); | 341 msgs.push(opt_end ? 'bold_end' : 'bold_start'); |
353 if (style.italic) | 342 if (style.italic) |
354 msgs.push(opt_end ? 'italic_end' : 'italic_start'); | 343 msgs.push(opt_end ? 'italic_end' : 'italic_start'); |
355 if (style.underline) | 344 if (style.underline) |
356 msgs.push(opt_end ? 'underline_end' : 'underline_start'); | 345 msgs.push(opt_end ? 'underline_end' : 'underline_start'); |
357 if (style.lineThrough) | 346 if (style.lineThrough) |
358 msgs.push(opt_end ? 'line_through_end' : 'line_through_start'); | 347 msgs.push(opt_end ? 'line_through_end' : 'line_through_start'); |
359 | 348 |
360 if (msgs.length) { | 349 if (msgs.length) { |
361 msgs.forEach(function(msg) { | 350 msgs.forEach(function(msg) { |
362 cvox.ChromeVox.tts.speak(Msgs.getMsg(msg), | 351 cvox.ChromeVox.tts.speak( |
363 cvox.QueueMode.QUEUE, | 352 Msgs.getMsg(msg), cvox.QueueMode.QUEUE, |
364 cvox.AbstractTts.PERSONALITY_ANNOTATION); | 353 cvox.AbstractTts.PERSONALITY_ANNOTATION); |
365 }); | 354 }); |
366 } | 355 } |
367 }, | 356 }, |
368 | 357 |
369 /** @override */ | 358 /** @override */ |
370 describeSelectionChanged: function(evt) { | 359 describeSelectionChanged: function(evt) { |
371 // Ignore end of text announcements. | 360 // Ignore end of text announcements. |
372 if ((this.start + 1) == evt.start && evt.start == this.value.length) | 361 if ((this.start + 1) == evt.start && evt.start == this.value.length) |
373 return; | 362 return; |
374 | 363 |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
446 | 435 |
447 /** | 436 /** |
448 * @private {ChromeVoxStateObserver} | 437 * @private {ChromeVoxStateObserver} |
449 */ | 438 */ |
450 editing.observer_ = new editing.EditingChromeVoxStateObserver(); | 439 editing.observer_ = new editing.EditingChromeVoxStateObserver(); |
451 | 440 |
452 /** | 441 /** |
453 * An EditableLine encapsulates all data concerning a line in the automation | 442 * An EditableLine encapsulates all data concerning a line in the automation |
454 * tree necessary to provide output. | 443 * tree necessary to provide output. |
455 * @constructor | 444 * @constructor |
456 */ | 445 */ |
457 editing.EditableLine = function(startNode, startIndex, endNode, endIndex) { | 446 editing.EditableLine = function(startNode, startIndex, endNode, endIndex) { |
458 /** @private {!Cursor} */ | 447 /** @private {!Cursor} */ |
459 this.start_ = new Cursor(startNode, startIndex); | 448 this.start_ = new Cursor(startNode, startIndex); |
460 this.start_ = this.start_.deepEquivalent || this.start_; | 449 this.start_ = this.start_.deepEquivalent || this.start_; |
461 | 450 |
462 /** @private {!Cursor} */ | 451 /** @private {!Cursor} */ |
463 this.end_ = new Cursor(endNode, endIndex); | 452 this.end_ = new Cursor(endNode, endIndex); |
464 this.end_ = this.end_.deepEquivalent || this.end_; | 453 this.end_ = this.end_.deepEquivalent || this.end_; |
465 /** @private {number} */ | 454 /** @private {number} */ |
466 this.localContainerStartOffset_ = startIndex; | 455 this.localContainerStartOffset_ = startIndex; |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
583 * Returns true if |otherLine| surrounds the same line as |this|. Note that | 572 * Returns true if |otherLine| surrounds the same line as |this|. Note that |
584 * the contents of the line might be different. | 573 * the contents of the line might be different. |
585 * @return {boolean} | 574 * @return {boolean} |
586 */ | 575 */ |
587 equals: function(otherLine) { | 576 equals: function(otherLine) { |
588 // Equality is intentionally loose here as any of the state nodes can be | 577 // Equality is intentionally loose here as any of the state nodes can be |
589 // invalidated at any time. | 578 // invalidated at any time. |
590 return (otherLine.lineStart_ == this.lineStart_ && | 579 return (otherLine.lineStart_ == this.lineStart_ && |
591 otherLine.lineEnd_ == this.lineEnd_) || | 580 otherLine.lineEnd_ == this.lineEnd_) || |
592 (otherLine.lineContainer_ == this.lineContainer_ && | 581 (otherLine.lineContainer_ == this.lineContainer_ && |
593 otherLine.containerLineStartOffset == this.containerLineStartOffset); | 582 otherLine.containerLineStartOffset == this.containerLineStartOffset); |
594 } | 583 } |
595 }; | 584 }; |
596 | 585 |
597 }); | 586 }); |
OLD | NEW |