OLD | NEW |
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 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview Braille hardware keyboard input method. | 8 * @fileoverview Braille hardware keyboard input method. |
9 * | 9 * |
10 * This method is automatically enabled when a braille display is connected | 10 * This method is automatically enabled when a braille display is connected |
11 * and ChromeVox is turned on. Most of the braille input and editing logic | 11 * and ChromeVox is turned on. Most of the braille input and editing logic |
12 * is located in ChromeVox where the braille translation library is available. | 12 * is located in ChromeVox where the braille translation library is available. |
13 * This IME connects to ChromeVox and communicates using messages as follows: | 13 * This IME connects to ChromeVox and communicates using messages as follows: |
14 * | 14 * |
15 * Sent from this IME to ChromeVox: | 15 * Sent from this IME to ChromeVox: |
16 * {type: 'activeState', active: boolean} | 16 * {type: 'activeState', active: boolean} |
17 * {type: 'inputContext', context: InputContext} | 17 * {type: 'inputContext', context: InputContext} |
18 * Sent on focus/blur to inform ChromeVox of the type of the current field. | 18 * Sent on focus/blur to inform ChromeVox of the type of the current field. |
19 * In the latter case (blur), context is null. | 19 * In the latter case (blur), context is null. |
20 * {type: 'reset'} | 20 * {type: 'reset'} |
21 * Sent when the {@code onReset} IME event fires. | 21 * Sent when the {@code onReset} IME event fires or uncommitted text is |
| 22 * committed without being triggered by ChromeVox (e.g. because of a |
| 23 * key press). |
22 * {type: 'brailleDots', dots: number} | 24 * {type: 'brailleDots', dots: number} |
23 * Sent when the user typed a braille cell using the standard keyboard. | 25 * Sent when the user typed a braille cell using the standard keyboard. |
24 * ChromeVox treats this similarly to entering braille input using the | 26 * ChromeVox treats this similarly to entering braille input using the |
25 * braille display. | 27 * braille display. |
26 * {type: 'backspace', requestId: string} | 28 * {type: 'backspace', requestId: string} |
27 * Sent when the user presses the backspace key. | 29 * Sent when the user presses the backspace key. |
28 * ChromeVox must respond with a {@code keyEventHandled} message | 30 * ChromeVox must respond with a {@code keyEventHandled} message |
29 * with the same request id. | 31 * with the same request id. |
30 * | 32 * |
31 * Sent from ChromeVox to this IME: | 33 * Sent from ChromeVox to this IME: |
32 * {type: 'replaceText', contextID: number, deleteBefore: number, | 34 * {type: 'replaceText', contextID: number, deleteBefore: number, |
33 * newText: string} | 35 * newText: string} |
34 * Deletes {@code deleteBefore} characters before the cursor (or selection) | 36 * Deletes {@code deleteBefore} characters before the cursor (or selection) |
35 * and inserts {@code newText}. {@code contextID} identifies the text field | 37 * and inserts {@code newText}. {@code contextID} identifies the text field |
36 * to apply the update to (no change will happen if focus has moved to a | 38 * to apply the update to (no change will happen if focus has moved to a |
37 * different field). | 39 * different field). |
| 40 * {type: 'setUncommitted', contextID: number, text: string} |
| 41 * Stores text for the field identified by contextID to be committed |
| 42 * either as a result of a 'commitUncommitted' message or a by the IME |
| 43 * unhandled key press event. Unlike 'replaceText', this does not send the |
| 44 * uncommitted text to the input field, but instead stores it in the IME. |
| 45 * {type: 'commitUncommitted', contextID: number} |
| 46 * Commits any uncommitted text if it matches the given context ID. |
| 47 * See 'setUncommitted' above. |
38 * {type: 'keyEventHandled', requestId: string, result: boolean} | 48 * {type: 'keyEventHandled', requestId: string, result: boolean} |
39 * Response to a {@code backspace} message indicating whether the | 49 * Response to a {@code backspace} message indicating whether the |
40 * backspace was handled by ChromeVox or should be allowed to propagate | 50 * backspace was handled by ChromeVox or should be allowed to propagate |
41 * through the normal event handling pipeline. | 51 * through the normal event handling pipeline. |
42 */ | 52 */ |
43 | 53 |
44 /** | 54 /** |
45 * @constructor | 55 * @constructor |
46 */ | 56 */ |
47 var BrailleIme = function() {}; | 57 var BrailleIme = function() {}; |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
124 engineID_: '', | 134 engineID_: '', |
125 | 135 |
126 /** | 136 /** |
127 * The port used to communicate with ChromeVox. | 137 * The port used to communicate with ChromeVox. |
128 * @type {Port} port_ | 138 * @type {Port} port_ |
129 * @private | 139 * @private |
130 */ | 140 */ |
131 port_: null, | 141 port_: null, |
132 | 142 |
133 /** | 143 /** |
| 144 * Uncommitted text and context ID. |
| 145 * @type {?{contextID: number, text: string}} |
| 146 * @private |
| 147 */ |
| 148 uncommitted_: null, |
| 149 |
| 150 /** |
134 * Registers event listeners in the chrome IME API. | 151 * Registers event listeners in the chrome IME API. |
135 */ | 152 */ |
136 init: function() { | 153 init: function() { |
137 chrome.input.ime.onActivate.addListener(this.onActivate_.bind(this)); | 154 chrome.input.ime.onActivate.addListener(this.onActivate_.bind(this)); |
138 chrome.input.ime.onDeactivated.addListener(this.onDeactivated_.bind(this)); | 155 chrome.input.ime.onDeactivated.addListener(this.onDeactivated_.bind(this)); |
139 chrome.input.ime.onFocus.addListener(this.onFocus_.bind(this)); | 156 chrome.input.ime.onFocus.addListener(this.onFocus_.bind(this)); |
140 chrome.input.ime.onBlur.addListener(this.onBlur_.bind(this)); | 157 chrome.input.ime.onBlur.addListener(this.onBlur_.bind(this)); |
141 chrome.input.ime.onInputContextUpdate.addListener( | 158 chrome.input.ime.onInputContextUpdate.addListener( |
142 this.onInputContextUpdate_.bind(this)); | 159 this.onInputContextUpdate_.bind(this)); |
143 chrome.input.ime.onKeyEvent.addListener(this.onKeyEvent_.bind(this), | 160 chrome.input.ime.onKeyEvent.addListener(this.onKeyEvent_.bind(this), |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
209 }, | 226 }, |
210 | 227 |
211 /** | 228 /** |
212 * Called by the system when this IME is active and a key event is generated. | 229 * Called by the system when this IME is active and a key event is generated. |
213 * @param {string} engineID Engine ID, should be 'braille'. | 230 * @param {string} engineID Engine ID, should be 'braille'. |
214 * @param {!ChromeKeyboardEvent} event The keyboard event. | 231 * @param {!ChromeKeyboardEvent} event The keyboard event. |
215 * @private | 232 * @private |
216 */ | 233 */ |
217 onKeyEvent_: function(engineID, event) { | 234 onKeyEvent_: function(engineID, event) { |
218 var result = this.processKey_(event); | 235 var result = this.processKey_(event); |
219 if (result !== undefined) { | 236 if (result !== undefined) |
220 chrome.input.ime.keyEventHandled(event.requestId, result); | 237 this.keyEventHandled_(event.requestId, event.type, result); |
221 } | |
222 }, | 238 }, |
223 | 239 |
224 /** | 240 /** |
225 * Called when chrome ends the current text input session. | 241 * Called when chrome ends the current text input session. |
226 * @param {string} engineID Engine ID, should be 'braille'. | 242 * @param {string} engineID Engine ID, should be 'braille'. |
227 * @private | 243 * @private |
228 */ | 244 */ |
229 onReset_: function(engineID) { | 245 onReset_: function(engineID) { |
230 this.log_('onReset', engineID); | 246 this.log_('onReset', engineID); |
231 this.engineID_ = engineID; | 247 this.engineID_ = engineID; |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 * @type {{contextID: number, deleteBefore: number, | 365 * @type {{contextID: number, deleteBefore: number, |
350 * newText: string}} | 366 * newText: string}} |
351 */ | 367 */ |
352 (message); | 368 (message); |
353 this.replaceText_(message.contextID, message.deleteBefore, | 369 this.replaceText_(message.contextID, message.deleteBefore, |
354 message.newText); | 370 message.newText); |
355 break; | 371 break; |
356 case 'keyEventHandled': | 372 case 'keyEventHandled': |
357 message = | 373 message = |
358 /** @type {{requestId: string, result: boolean}} */ (message); | 374 /** @type {{requestId: string, result: boolean}} */ (message); |
359 chrome.input.ime.keyEventHandled(message.requestId, message.result); | 375 this.keyEventHandled_(message.requestId, 'keydown', message.result); |
| 376 break; |
| 377 case 'setUncommitted': |
| 378 message = |
| 379 /** @type {{contextID: number, text: string}} */ (message); |
| 380 this.setUncommitted_(message.contextID, message.text); |
| 381 break; |
| 382 case 'commitUncommitted': |
| 383 message = |
| 384 /** @type {{contextID: number}} */ (message); |
| 385 this.commitUncommitted_(message.contextID); |
360 break; | 386 break; |
361 default: | 387 default: |
362 console.error('Unknown message from ChromeVox: ' + | 388 console.error('Unknown message from ChromeVox: ' + |
363 JSON.stringify(message)); | 389 JSON.stringify(message)); |
364 break; | 390 break; |
365 } | 391 } |
366 }, | 392 }, |
367 | 393 |
368 /** | 394 /** |
369 * Handles a disconnect event from the ChromeVox side. | 395 * Handles a disconnect event from the ChromeVox side. |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
423 // deleteSurroundingText works correctly. | 449 // deleteSurroundingText works correctly. |
424 chrome.input.ime.deleteSurroundingText( | 450 chrome.input.ime.deleteSurroundingText( |
425 {engineID: this.engineID_, contextID: contextID, | 451 {engineID: this.engineID_, contextID: contextID, |
426 offset: 0, length: 0}, deleteText); | 452 offset: 0, length: 0}, deleteText); |
427 } else { | 453 } else { |
428 addText(); | 454 addText(); |
429 } | 455 } |
430 }, | 456 }, |
431 | 457 |
432 /** | 458 /** |
| 459 * Responds to an asynchronous key event, indicating whether it was handled |
| 460 * or not. If it wasn't handled, any uncommitted text is committed |
| 461 * before sending the response to the IME API. |
| 462 * @param {string} requestId Key event request id. |
| 463 * @param {string} type Type of key event being responded to. |
| 464 * @param {boolean} response Whether the IME handled the event. |
| 465 */ |
| 466 keyEventHandled_: function(requestId, type, response) { |
| 467 if (!response && type === 'keydown' && this.uncommitted_) { |
| 468 this.commitUncommitted_(this.uncommitted_.contextID); |
| 469 this.sendToChromeVox_({type: 'reset'}); |
| 470 } |
| 471 chrome.input.ime.keyEventHandled(requestId, response); |
| 472 }, |
| 473 |
| 474 /** |
| 475 * Stores uncommitted text that will be committed on any key press or |
| 476 * when {@code commitUncommitted_} is called. |
| 477 * @param {number} contextID of the current field. |
| 478 * @param {string} text to store. |
| 479 */ |
| 480 setUncommitted_: function(contextID, text) { |
| 481 this.uncommitted_ = {contextID: contextID, text: text}; |
| 482 }, |
| 483 |
| 484 /** |
| 485 * Commits the last set uncommitted text if it matches the given context id. |
| 486 * @param {number} contextID |
| 487 */ |
| 488 commitUncommitted_: function(contextID) { |
| 489 if (this.uncommitted_ && contextID === this.uncommitted_.contextID) |
| 490 chrome.input.ime.commitText(this.uncommitted_); |
| 491 this.uncommitted_ = null; |
| 492 }, |
| 493 |
| 494 /** |
433 * Updates the menu items for this IME. | 495 * Updates the menu items for this IME. |
434 */ | 496 */ |
435 updateMenuItems_: function() { | 497 updateMenuItems_: function() { |
436 // TODO(plundblad): Localize when translations available. | 498 // TODO(plundblad): Localize when translations available. |
437 chrome.input.ime.setMenuItems( | 499 chrome.input.ime.setMenuItems( |
438 {engineID: this.engineID_, | 500 {engineID: this.engineID_, |
439 items: [ | 501 items: [ |
440 { | 502 { |
441 id: this.USE_STANDARD_KEYBOARD_ID, | 503 id: this.USE_STANDARD_KEYBOARD_ID, |
442 label: 'Use standard keyboard for braille', | 504 label: 'Use standard keyboard for braille', |
443 style: 'check', | 505 style: 'check', |
444 visible: true, | 506 visible: true, |
445 checked: this.useStandardKeyboard_, | 507 checked: this.useStandardKeyboard_, |
446 enabled: true | 508 enabled: true |
447 } | 509 } |
448 ] | 510 ] |
449 }); | 511 }); |
450 } | 512 } |
451 }; | 513 }; |
OLD | NEW |