Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(420)

Side by Side Diff: chrome/browser/resources/chromeos/chromevox/braille/braille_input_handler.js

Issue 1039703002: Make contracted braille input work in more contexts. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@docs2
Patch Set: Rebase Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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 Handles braille input keys when the user is typing or editing 6 * @fileoverview Handles braille input keys when the user is typing or editing
7 * text in an input field. This class cooperates with the Braille IME 7 * text in an input field. This class cooperates with the Braille IME
8 * that is built into Chrome OS to do the actual text editing. 8 * that is built into Chrome OS to do the actual text editing.
9 */ 9 */
10 10
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
66 * the race, we store the cell entered until we can submit it to the IME. 66 * the race, we store the cell entered until we can submit it to the IME.
67 * @type {!Array<number>} 67 * @type {!Array<number>}
68 * @private 68 * @private
69 */ 69 */
70 this.pendingCells_ = []; 70 this.pendingCells_ = [];
71 /** 71 /**
72 * @type {cvox.BrailleInputHandler.EntryState_} 72 * @type {cvox.BrailleInputHandler.EntryState_}
73 * @private 73 * @private
74 */ 74 */
75 this.entryState_ = null; 75 this.entryState_ = null;
76 /**
77 * @type {cvox.ExtraCellsSpan}
78 * @private
79 */
80 this.uncommittedCellsSpan_ = null;
81 /**
82 * @type {function()?}
83 * @private
84 */
85 this.uncommittedCellsChangedListener_ = null;
76 86
77 this.translatorManager_.addChangeListener( 87 this.translatorManager_.addChangeListener(
78 this.clearEntryState_.bind(this)); 88 this.commitAndClearEntryState_.bind(this));
79 }; 89 };
80 90
81 /** 91 /**
82 * The ID of the Braille IME extension built into Chrome OS. 92 * The ID of the Braille IME extension built into Chrome OS.
83 * @const {string} 93 * @const {string}
84 * @private 94 * @private
85 */ 95 */
86 cvox.BrailleInputHandler.IME_EXTENSION_ID_ = 96 cvox.BrailleInputHandler.IME_EXTENSION_ID_ =
87 'jddehjeebkoimngcbdkaahpobgicbffp'; 97 'jddehjeebkoimngcbdkaahpobgicbffp';
88 98
(...skipping 26 matching lines...) Expand all
115 */ 125 */
116 init: function() { 126 init: function() {
117 chrome.runtime.onConnectExternal.addListener(this.onImeConnect_.bind(this)); 127 chrome.runtime.onConnectExternal.addListener(this.onImeConnect_.bind(this));
118 }, 128 },
119 129
120 /** 130 /**
121 * Called when the content on the braille display is updated. Modifies the 131 * Called when the content on the braille display is updated. Modifies the
122 * input state according to the new content. 132 * input state according to the new content.
123 * @param {cvox.Spannable} text Text, optionally with value and selection 133 * @param {cvox.Spannable} text Text, optionally with value and selection
124 * spans. 134 * spans.
135 * @param {function()} listener Called when the uncommitted cells
136 * have changed.
125 */ 137 */
126 onDisplayContentChanged: function(text) { 138 onDisplayContentChanged: function(text, listener) {
127 var valueSpan = text.getSpanInstanceOf(cvox.ValueSpan); 139 var valueSpan = text.getSpanInstanceOf(cvox.ValueSpan);
128 var selectionSpan = text.getSpanInstanceOf(cvox.ValueSelectionSpan); 140 var selectionSpan = text.getSpanInstanceOf(cvox.ValueSelectionSpan);
129 if (!(valueSpan && selectionSpan)) 141 if (!(valueSpan && selectionSpan))
130 return; 142 return;
143 // Don't call the old listener any further, since new content is being
144 // set. If the old listener is not cleared here, it could be called
145 // spuriously if the entry state is cleared below.
146 this.uncommittedCellsChangedListener_ = null;
131 // The type casts are ok because the spans are known to exist. 147 // The type casts are ok because the spans are known to exist.
132 var valueStart = /** @type {number} */ (text.getSpanStart(valueSpan)); 148 var valueStart = /** @type {number} */ (text.getSpanStart(valueSpan));
133 var valueEnd = /** @type {number} */ (text.getSpanEnd(valueSpan)); 149 var valueEnd = /** @type {number} */ (text.getSpanEnd(valueSpan));
134 var selectionStart = 150 var selectionStart =
135 /** @type {number} */ (text.getSpanStart(selectionSpan)); 151 /** @type {number} */ (text.getSpanStart(selectionSpan));
136 var selectionEnd = /** @type {number} */ (text.getSpanEnd(selectionSpan)); 152 var selectionEnd = /** @type {number} */ (text.getSpanEnd(selectionSpan));
137 if (selectionStart < valueStart || selectionEnd > valueEnd) { 153 if (selectionStart < valueStart || selectionEnd > valueEnd) {
138 console.error('Selection outside of value in braille content'); 154 console.error('Selection outside of value in braille content');
139 this.clearEntryState_(); 155 this.clearEntryState_();
140 return; 156 return;
141 } 157 }
142 var newTextBefore = text.toString().substring(valueStart, selectionStart); 158 var newTextBefore = text.toString().substring(valueStart, selectionStart);
143 if (this.currentTextBefore_ !== newTextBefore && this.entryState_) 159 if (this.currentTextBefore_ !== newTextBefore && this.entryState_)
144 this.entryState_.onTextBeforeChanged(newTextBefore); 160 this.entryState_.onTextBeforeChanged(newTextBefore);
145 this.currentTextBefore_ = newTextBefore; 161 this.currentTextBefore_ = newTextBefore;
146 this.currentTextAfter_ = text.toString().substring(selectionEnd, valueEnd); 162 this.currentTextAfter_ = text.toString().substring(selectionEnd, valueEnd);
163 this.uncommittedCellsSpan_ = new cvox.ExtraCellsSpan();
164 text.setSpan(this.uncommittedCellsSpan_, selectionStart, selectionStart);
165 if (this.entryState_ && this.entryState_.usesUncommittedCells) {
166 this.updateUncommittedCells_(
167 new Uint8Array(this.entryState_.cells_).buffer);
168 }
169 this.uncommittedCellsChangedListener_ = listener;
147 }, 170 },
148 171
149 /** 172 /**
150 * Handles braille key events used for input by editing the current input 173 * Handles braille key events used for input by editing the current input
151 * field appropriately. 174 * field appropriately.
152 * @param {!cvox.BrailleKeyEvent} event The key event. 175 * @param {!cvox.BrailleKeyEvent} event The key event.
153 * @return {boolean} {@code true} if the event was handled, {@code false} 176 * @return {boolean} {@code true} if the event was handled, {@code false}
154 * if it should propagate further. 177 * if it should propagate further.
155 */ 178 */
156 onBrailleKeyEvent: function(event) { 179 onBrailleKeyEvent: function(event) {
157 if (event.command === cvox.BrailleKeyCommand.DOTS) 180 if (event.command === cvox.BrailleKeyCommand.DOTS)
158 return this.onBrailleDots_(/** @type {number} */(event.brailleDots)); 181 return this.onBrailleDots_(/** @type {number} */(event.brailleDots));
159 // Any other braille command cancels the pending cells. 182 // Any other braille command cancels the pending cells.
160 this.pendingCells_.length = 0; 183 this.pendingCells_.length = 0;
161 if (event.command === cvox.BrailleKeyCommand.STANDARD_KEY) { 184 if (event.command === cvox.BrailleKeyCommand.STANDARD_KEY) {
162 if (event.standardKeyCode === 'Backspace' && 185 if (event.standardKeyCode === 'Backspace' &&
163 !event.altKey && !event.ctrlKey && !event.shiftKey && 186 !event.altKey && !event.ctrlKey && !event.shiftKey &&
164 this.onBackspace_()) { 187 this.onBackspace_()) {
165 return true; 188 return true;
166 } else { 189 } else {
167 this.clearEntryState_(); 190 this.commitAndClearEntryState_();
168 this.sendKeyEventPair_(event); 191 this.sendKeyEventPair_(event);
169 return true; 192 return true;
170 } 193 }
171 } 194 }
172 return false; 195 return false;
173 }, 196 },
174 197
175 /** 198 /**
176 * Returns how the value of the currently displayed content should be 199 * Returns how the value of the currently displayed content should be
177 * expanded given the current input state. 200 * expanded given the current input state.
(...skipping 28 matching lines...) Expand all
206 * propagate further. 229 * propagate further.
207 * @private 230 * @private
208 */ 231 */
209 onBrailleDots_: function(dots) { 232 onBrailleDots_: function(dots) {
210 if (!this.imeActive_) { 233 if (!this.imeActive_) {
211 this.pendingCells_.push(dots); 234 this.pendingCells_.push(dots);
212 return true; 235 return true;
213 } 236 }
214 if (!this.inputContext_) 237 if (!this.inputContext_)
215 return false; 238 return false;
216 // Avoid accumulating cells forever when typing without moving the cursor
217 // by flushing the input when we see a blank cell.
218 // Note that this might switch to contracted if appropriate.
219 if (this.entryState_ && this.entryState_.lastCellIsBlank())
220 this.clearEntryState_();
221 if (!this.entryState_) { 239 if (!this.entryState_) {
222 this.entryState_ = this.createEntryState_(); 240 if (!(this.entryState_ = this.createEntryState_()))
223 if (!this.entryState_)
224 return false; 241 return false;
225 } 242 }
226 this.entryState_.appendCell(dots); 243 this.entryState_.appendCell(dots);
227 return true; 244 return true;
228 }, 245 },
229 246
230 /** 247 /**
231 * Handles the backspace key by deleting the last typed cell if possible. 248 * Handles the backspace key by deleting the last typed cell if possible.
232 * @return {boolean} {@code true} if the event was handled, {@code false} 249 * @return {boolean} {@code true} if the event was handled, {@code false}
233 * if it wasn't and should propagate further. 250 * if it wasn't and should propagate further.
(...skipping 14 matching lines...) Expand all
248 * object, or null if it couldn't be created (e.g. if there's no braille 265 * object, or null if it couldn't be created (e.g. if there's no braille
249 * translator available yet). 266 * translator available yet).
250 * @private 267 * @private
251 */ 268 */
252 createEntryState_: function() { 269 createEntryState_: function() {
253 var translator = this.translatorManager_.getDefaultTranslator(); 270 var translator = this.translatorManager_.getDefaultTranslator();
254 if (!translator) 271 if (!translator)
255 return null; 272 return null;
256 var uncontractedTranslator = 273 var uncontractedTranslator =
257 this.translatorManager_.getUncontractedTranslator(); 274 this.translatorManager_.getUncontractedTranslator();
275 var constructor = cvox.BrailleInputHandler.EditsEntryState_;
258 if (uncontractedTranslator) { 276 if (uncontractedTranslator) {
259 var textBefore = this.currentTextBefore_; 277 var textBefore = this.currentTextBefore_;
260 var textAfter = this.currentTextAfter_; 278 var textAfter = this.currentTextAfter_;
261 if (this.inAlwaysUncontractedContext_() || 279 if (this.inAlwaysUncontractedContext_() ||
262 (cvox.BrailleInputHandler.ENDS_WITH_NON_WHITESPACE_RE_.test( 280 (cvox.BrailleInputHandler.ENDS_WITH_NON_WHITESPACE_RE_.test(
263 textBefore)) || 281 textBefore)) ||
264 (cvox.BrailleInputHandler.STARTS_WITH_NON_WHITESPACE_RE_.test( 282 (cvox.BrailleInputHandler.STARTS_WITH_NON_WHITESPACE_RE_.test(
265 textAfter))) { 283 textAfter))) {
266 translator = uncontractedTranslator; 284 translator = uncontractedTranslator;
285 } else {
286 constructor = cvox.BrailleInputHandler.LateCommitEntryState_;
267 } 287 }
268 } 288 }
269 289
270 return new cvox.BrailleInputHandler.EditsEntryState_(this, translator); 290 return new constructor(this, translator);
271 }, 291 },
272 292
273 /** 293 /**
294 * Commits the current entry state and clears it, if any.
295 * @private
296 */
297 commitAndClearEntryState_: function() {
298 if (this.entryState_) {
299 this.entryState_.commit();
300 this.clearEntryState_();
301 }
302 },
303
304 /**
274 * Clears the current entry state without committing it. 305 * Clears the current entry state without committing it.
275 * @private 306 * @private
276 */ 307 */
277 clearEntryState_: function() { 308 clearEntryState_: function() {
278 if (this.entryState_) { 309 if (this.entryState_) {
310 if (this.entryState_.usesUncommittedCells)
311 this.updateUncommittedCells_(new ArrayBuffer(0));
279 this.entryState_.inputHandler_ = null; 312 this.entryState_.inputHandler_ = null;
280 this.entryState_ = null; 313 this.entryState_ = null;
281 } 314 }
282 }, 315 },
283 316
284 /** 317 /**
318 * @param {ArrayBuffer} cells
319 * @private
320 */
321 updateUncommittedCells_: function(cells) {
322 if (this.uncommittedCellsSpan_)
323 this.uncommittedCellsSpan_.cells = cells;
324 if (this.uncommittedCellsChangedListener_)
325 this.uncommittedCellsChangedListener_();
326 },
327
328 /**
285 * Called when another extension connects to this extension. Accepts 329 * Called when another extension connects to this extension. Accepts
286 * connections from the ChromeOS builtin Braille IME and ignores connections 330 * connections from the ChromeOS builtin Braille IME and ignores connections
287 * from other extensions. 331 * from other extensions.
288 * @param {Port} port The port used to communicate with the other extension. 332 * @param {Port} port The port used to communicate with the other extension.
289 * @private 333 * @private
290 */ 334 */
291 onImeConnect_: function(port) { 335 onImeConnect_: function(port) {
292 if (port.name !== cvox.BrailleInputHandler.IME_PORT_NAME_ || 336 if (port.name !== cvox.BrailleInputHandler.IME_PORT_NAME_ ||
293 port.sender.id !== cvox.BrailleInputHandler.IME_EXTENSION_ID_) { 337 port.sender.id !== cvox.BrailleInputHandler.IME_EXTENSION_ID_) {
294 return; 338 return;
(...skipping 197 matching lines...) Expand 10 before | Expand all | Expand 10 after
492 // Delete all previous expected changes and ignore this one. 536 // Delete all previous expected changes and ignore this one.
493 this.pendingTextsBefore_.splice(0, i + 1); 537 this.pendingTextsBefore_.splice(0, i + 1);
494 return; 538 return;
495 } 539 }
496 } 540 }
497 // There was an actual text change (or cursor movement) that we hadn't 541 // There was an actual text change (or cursor movement) that we hadn't
498 // caused ourselves, reset any pending input. 542 // caused ourselves, reset any pending input.
499 this.inputHandler_.clearEntryState_(); 543 this.inputHandler_.clearEntryState_();
500 }, 544 },
501 545
502 /** @return {boolean} */ 546 /**
503 lastCellIsBlank: function() { 547 * Makes sure the current text is permanently added to the edit field.
504 return this.cells_[this.cells_.length - 1] === 0; 548 * After this call, this object should be abandoned.
549 */
550 commit: function() {
505 }, 551 },
506 552
507 /** 553 /**
554 * @return {boolean} true if the entry state uses uncommitted cells.
555 */
556 get usesUncommittedCells() {
557 return false;
558 },
559
560 /**
508 * Updates the translated text based on the current cells and sends the 561 * Updates the translated text based on the current cells and sends the
509 * delta to the IME. 562 * delta to the IME.
510 * @private 563 * @private
511 */ 564 */
512 updateText_: function() { 565 updateText_: function() {
513 var cellsBuffer = new Uint8Array(this.cells_).buffer; 566 var cellsBuffer = new Uint8Array(this.cells_).buffer;
567 var commit = this.lastCellIsBlank_;
568 if (!commit && this.usesUncommittedCells)
569 this.inputHandler_.updateUncommittedCells_(cellsBuffer);
514 this.translator_.backTranslate(cellsBuffer, function(result) { 570 this.translator_.backTranslate(cellsBuffer, function(result) {
515 if (result === null) { 571 if (result === null) {
516 console.error('Error when backtranslating braille cells'); 572 console.error('Error when backtranslating braille cells');
517 return; 573 return;
518 } 574 }
519 if (!this.inputHandler_) 575 if (!this.inputHandler_)
520 return; 576 return;
521 this.sendTextChange_(result); 577 this.sendTextChange_(result);
522 this.text_ = result; 578 this.text_ = result;
579 if (commit)
580 this.inputHandler_.commitAndClearEntryState_();
523 }.bind(this)); 581 }.bind(this));
524 }, 582 },
525 583
526 /** 584 /**
585 * @return {boolean}
586 * @private
587 */
588 get lastCellIsBlank_() {
589 return this.cells_[this.cells_.length - 1] === 0;
590 },
591
592 /**
527 * Sends new text to the IME. This dhould be overriden by subclasses. 593 * Sends new text to the IME. This dhould be overriden by subclasses.
528 * The old text is still available in the {@code text_} property. 594 * The old text is still available in the {@code text_} property.
529 * @param {string} newText Text to send. 595 * @param {string} newText Text to send.
530 * @private 596 * @private
531 */ 597 */
532 sendTextChange_: function(newText) { 598 sendTextChange_: function(newText) {
533 } 599 }
534 }; 600 };
535 601
536 /** 602 /**
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
578 } 644 }
579 // Send the replace operation to be performed asynchronously by the IME. 645 // Send the replace operation to be performed asynchronously by the IME.
580 this.inputHandler_.postImeMessage_( 646 this.inputHandler_.postImeMessage_(
581 {type: 'replaceText', 647 {type: 'replaceText',
582 contextID: this.inputHandler_.inputContext_.contextID, 648 contextID: this.inputHandler_.inputContext_.contextID,
583 deleteBefore: deleteLength, 649 deleteBefore: deleteLength,
584 newText: toInsert}); 650 newText: toInsert});
585 } 651 }
586 } 652 }
587 }; 653 };
654
655 /**
656 * Entry state that only updates the edit field when a blank cell is entered.
657 * During the input of a single 'word', the uncommitted text is stored by the
658 * IME.
659 * @param {!cvox.BrailleInputHandler} inputHandler
660 * @param {!cvox.LibLouis.Translator} translator
661 * @constructor
662 * @private
663 * @extends {cvox.BrailleInputHandler.EntryState_}
664 */
665 cvox.BrailleInputHandler.LateCommitEntryState_ = function(
666 inputHandler, translator) {
667 cvox.BrailleInputHandler.EntryState_.call(this, inputHandler, translator);
668 };
669
670 cvox.BrailleInputHandler.LateCommitEntryState_.prototype = {
671 __proto__: cvox.BrailleInputHandler.EntryState_.prototype,
672
673 /** @override */
674 commit: function() {
675 this.inputHandler_.postImeMessage_(
676 {type: 'commitUncommitted',
677 contextID: this.inputHandler_.inputContext_.contextID});
678 },
679
680 /** @override */
681 get usesUncommittedCells() {
682 return true;
683 },
684
685 /** @override */
686 sendTextChange_: function(newText) {
687 this.inputHandler_.postImeMessage_(
688 {type: 'setUncommitted',
689 contextID: this.inputHandler_.inputContext_.contextID,
690 text: newText});
691 }
692 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698