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

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

Issue 2496823002: Implement word wrapping and panning in multiline Braille. (Closed)
Patch Set: Addressed David's comments Created 4 years 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
« no previous file with comments | « no previous file | chrome/browser/resources/chromeos/chromevox/braille/braille_display_manager_test.unitjs » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 Puts text on a braille display. 6 * @fileoverview Puts text on a braille display.
7 * 7 *
8 */ 8 */
9 9
10 goog.provide('cvox.BrailleDisplayManager'); 10 goog.provide('cvox.BrailleDisplayManager');
(...skipping 22 matching lines...) Expand all
33 * @private 33 * @private
34 */ 34 */
35 this.content_ = new cvox.NavBraille({}); 35 this.content_ = new cvox.NavBraille({});
36 /** 36 /**
37 * @type {!cvox.ExpandingBrailleTranslator.ExpansionType} valueExpansion 37 * @type {!cvox.ExpandingBrailleTranslator.ExpansionType} valueExpansion
38 * @private 38 * @private
39 */ 39 */
40 this.expansionType_ = 40 this.expansionType_ =
41 cvox.ExpandingBrailleTranslator.ExpansionType.SELECTION; 41 cvox.ExpandingBrailleTranslator.ExpansionType.SELECTION;
42 /** 42 /**
43 * @type {!ArrayBuffer}
44 * @private
45 */
46 this.translatedContent_ = new ArrayBuffer(0);
47 /**
48 * @type {!ArrayBuffer}
49 * @private
50 */
51 this.displayedContent_ = this.translatedContent_;
52 /**
53 * @type {cvox.PanStrategy} 43 * @type {cvox.PanStrategy}
54 * @private 44 * @private
55 */ 45 */
56 this.panStrategy_ = new cvox.WrappingPanStrategy(); 46 this.panStrategy_ = new cvox.PanStrategy();
57 /** 47 /**
58 * @type {function(!cvox.BrailleKeyEvent, !cvox.NavBraille)} 48 * @type {function(!cvox.BrailleKeyEvent, !cvox.NavBraille)}
59 * @private 49 * @private
60 */ 50 */
61 this.commandListener_ = function() {}; 51 this.commandListener_ = function() {};
62 /** 52 /**
63 * Current display state used for width calculations. This is different from 53 * Current display state to show in the Virtual Braille Captions display.
64 * realDisplayState_ if the braille captions feature is enabled and there is 54 * This is different from realDisplayState_ if the braille captions feature
65 * no hardware display connected. Otherwise, it is the same object 55 * is enabled and there is no hardware display connected. Otherwise, it is
66 * as realDisplayState_. 56 * the same object as realDisplayState_.
67 * @type {!cvox.BrailleDisplayState} 57 * @type {!cvox.BrailleDisplayState}
68 * @private 58 * @private
69 */ 59 */
70 this.displayState_ = {available: false, textRowCount: undefined, 60 this.displayState_ = {available: false, textRowCount: undefined,
71 textColumnCount: undefined}; 61 textColumnCount: undefined};
72 /** 62 /**
73 * State reported from the chrome api, reflecting a real hardware 63 * State reported from the chrome api, reflecting a real hardware
74 * display. 64 * display.
75 * @type {!cvox.BrailleDisplayState} 65 * @type {!cvox.BrailleDisplayState}
76 * @private 66 * @private
77 */ 67 */
78 this.realDisplayState_ = this.displayState_; 68 this.realDisplayState_ = this.displayState_;
79 /**
80 * @type {!Array<number>}
81 * @private
82 */
83 this.textToBraille_ = [];
84 /**
85 * @type {!Array<number>}
86 * @private
87 */
88 this.brailleToText_ = [];
89 69
90 translatorManager.addChangeListener(function() { 70 translatorManager.addChangeListener(function() {
91 this.translateContent_(this.content_, this.expansionType_); 71 this.translateContent_(this.content_, this.expansionType_);
92 }.bind(this)); 72 }.bind(this));
93 73
94 chrome.storage.onChanged.addListener(function(changes, area) { 74 chrome.storage.onChanged.addListener(function(changes, area) {
95 if (area == 'local' && 'brailleWordWrap' in changes) { 75 if (area == 'local' && 'brailleWordWrap' in changes) {
96 this.updatePanStrategy_(changes.brailleWordWrap.newValue); 76 this.updatePanStrategy_(changes.brailleWordWrap.newValue);
97 } 77 }
78 if (area == 'local' && ('virtualBrailleRows' in changes ||
79 'virtualBrailleColumns' in changes)) {
80 this.onCaptionsStateChanged_();
81 }
98 }.bind(this)); 82 }.bind(this));
99 chrome.storage.local.get({brailleWordWrap: true}, function(items) { 83 chrome.storage.local.get({brailleWordWrap: true}, function(items) {
100 this.updatePanStrategy_(items.brailleWordWrap); 84 this.updatePanStrategy_(items.brailleWordWrap);
101 }.bind(this)); 85 }.bind(this));
102 86
103 cvox.BrailleCaptionsBackground.init(goog.bind( 87 cvox.BrailleCaptionsBackground.init(goog.bind(
104 this.onCaptionsStateChanged_, this)); 88 this.onCaptionsStateChanged_, this));
105 if (goog.isDef(chrome.brailleDisplayPrivate)) { 89 if (goog.isDef(chrome.brailleDisplayPrivate)) {
106 var onDisplayStateChanged = goog.bind(this.refreshDisplayState_, this); 90 var onDisplayStateChanged = goog.bind(this.refreshDisplayState_, this);
107 chrome.brailleDisplayPrivate.getDisplayState(onDisplayStateChanged); 91 chrome.brailleDisplayPrivate.getDisplayState(onDisplayStateChanged);
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 }; 135 };
152 136
153 137
154 /** 138 /**
155 * @param {!cvox.BrailleDisplayState} newState Display state reported 139 * @param {!cvox.BrailleDisplayState} newState Display state reported
156 * by the extension API. 140 * by the extension API.
157 * @private 141 * @private
158 */ 142 */
159 cvox.BrailleDisplayManager.prototype.refreshDisplayState_ = function( 143 cvox.BrailleDisplayManager.prototype.refreshDisplayState_ = function(
160 newState) { 144 newState) {
161 var oldSize = this.displayState_.textColumnCount * 145 var oldColumnCount = this.displayState_.textColumnCount || 0;
162 this.displayState_.textRowCount || 0; 146 var oldRowCount = this.displayState_.textRowCount || 0;
147 var processDisplayState = function(displayState) {
148 this.displayState_ = displayState;
149 var newColumnCount = displayState.textColumnCount || 0;
150 var newRowCount = displayState.textRowCount || 0;
151
152 if (oldColumnCount != newColumnCount || oldRowCount != newRowCount) {
153 this.panStrategy_.setDisplaySize(newRowCount, newColumnCount);
154 }
155 this.refresh_();
156 }.bind(this);
163 this.realDisplayState_ = newState; 157 this.realDisplayState_ = newState;
164 if (newState.available) { 158 if (newState.available) {
165 this.displayState_ = newState; 159 processDisplayState(newState);
160 // Update the dimensions of the virtual braille captions display to those
161 // of a real physical display when one is plugged in.
166 } else { 162 } else {
167 this.displayState_ = 163 cvox.BrailleCaptionsBackground.getVirtualDisplayState(processDisplayState);
168 cvox.BrailleCaptionsBackground.getVirtualDisplayState();
169 } 164 }
170 var newSize = this.displayState_.textColumnCount *
171 this.displayState_.textRowCount || 0;
172 if (oldSize != newSize) {
173 this.panStrategy_.setDisplaySize(newSize);
174 }
175 this.refresh_();
176 }; 165 };
177 166
178 167
179 /** 168 /**
180 * Called when the state of braille captions changes. 169 * Called when the state of braille captions changes.
181 * @private 170 * @private
182 */ 171 */
183 cvox.BrailleDisplayManager.prototype.onCaptionsStateChanged_ = function() { 172 cvox.BrailleDisplayManager.prototype.onCaptionsStateChanged_ = function() {
184 // Force reevaluation of the display state based on our stored real 173 // Force reevaluation of the display state based on our stored real
185 // hardware display state, meaning that if a real display is connected, 174 // hardware display state, meaning that if a real display is connected,
186 // that takes precedence over the state from the captions 'virtual' display. 175 // that takes precedence over the state from the captions 'virtual' display.
187 this.refreshDisplayState_(this.realDisplayState_); 176 this.refreshDisplayState_(this.realDisplayState_);
188 }; 177 };
189 178
190 179
191 /** @private */ 180 /**
181 * Refreshes what is shown on the physical braille display and the virtual
182 * braille captions display.
183 * @private
184 */
192 cvox.BrailleDisplayManager.prototype.refresh_ = function() { 185 cvox.BrailleDisplayManager.prototype.refresh_ = function() {
193 if (!this.displayState_.available) { 186 if (!this.displayState_.available) {
194 return; 187 return;
195 } 188 }
196 var viewPort = this.panStrategy_.viewPort; 189 var brailleBuf = this.panStrategy_.getCurrentBrailleViewportContents();
197 var buf = this.displayedContent_.slice(viewPort.start, viewPort.end); 190 var textBuf = this.panStrategy_.getCurrentTextViewportContents();
198 if (this.realDisplayState_.available) { 191 if (this.realDisplayState_.available) {
199 chrome.brailleDisplayPrivate.writeDots(buf, buf.byteLength, 1); 192 chrome.brailleDisplayPrivate.writeDots(brailleBuf,
193 brailleBuf.byteLength, 1);
200 } 194 }
201 if (cvox.BrailleCaptionsBackground.isEnabled()) { 195 if (cvox.BrailleCaptionsBackground.isEnabled()) {
202 var start = this.brailleToTextPosition_(viewPort.start); 196 cvox.BrailleCaptionsBackground.setContent(textBuf, brailleBuf,
203 var end = this.brailleToTextPosition_(viewPort.end); 197 this.panStrategy_.brailleToText, this.panStrategy_.offsetsForSlices,
204 cvox.BrailleCaptionsBackground.setContent( 198 this.displayState_.textRowCount, this.displayState_.textColumnCount);
205 this.content_.text.toString().substring(start, end), buf,
206 this.brailleToText_);
207 } 199 }
208 }; 200 };
209 201
210
211 /** 202 /**
212 * @param {!cvox.NavBraille} newContent New display content. 203 * @param {!cvox.NavBraille} newContent New display content.
213 * @param {cvox.ExpandingBrailleTranslator.ExpansionType} newExpansionType 204 * @param {cvox.ExpandingBrailleTranslator.ExpansionType} newExpansionType
214 * How the value part of of the new content should be expanded 205 * How the value part of of the new content should be expanded
215 * with regards to contractions. 206 * with regards to contractions.
216 * @private 207 * @private
217 */ 208 */
218 cvox.BrailleDisplayManager.prototype.translateContent_ = function( 209 cvox.BrailleDisplayManager.prototype.translateContent_ = function(
219 newContent, newExpansionType) { 210 newContent, newExpansionType) {
220 var writeTranslatedContent = function(cells, textToBraille, brailleToText) { 211 var writeTranslatedContent = function(cells, textToBraille, brailleToText) {
221 this.content_ = newContent; 212 this.content_ = newContent;
222 this.expansionType_ = newExpansionType; 213 this.expansionType_ = newExpansionType;
223 this.textToBraille_ = textToBraille;
224 this.brailleToText_ = brailleToText;
225 var startIndex = this.content_.startIndex; 214 var startIndex = this.content_.startIndex;
226 var endIndex = this.content_.endIndex; 215 var endIndex = this.content_.endIndex;
227 var targetPosition; 216 var targetPosition;
228 if (startIndex >= 0) { 217 if (startIndex >= 0) {
229 var translatedStartIndex; 218 var translatedStartIndex;
230 var translatedEndIndex; 219 var translatedEndIndex;
231 if (startIndex >= textToBraille.length) { 220 if (startIndex >= textToBraille.length) {
232 // Allow the cells to be extended with one extra cell for 221 // Allow the cells to be extended with one extra cell for
233 // a carret after the last character. 222 // a carret after the last character.
234 var extCells = new ArrayBuffer(cells.byteLength + 1); 223 var extCells = new ArrayBuffer(cells.byteLength + 1);
235 new Uint8Array(extCells).set(new Uint8Array(cells)); 224 new Uint8Array(extCells).set(new Uint8Array(cells));
236 // Last byte is initialized to 0. 225 // Last byte is initialized to 0.
237 cells = extCells; 226 cells = extCells;
238 translatedStartIndex = cells.byteLength - 1; 227 translatedStartIndex = cells.byteLength - 1;
239 } else { 228 } else {
240 translatedStartIndex = textToBraille[startIndex]; 229 translatedStartIndex = textToBraille[startIndex];
241 } 230 }
242 if (endIndex >= textToBraille.length) { 231 if (endIndex >= textToBraille.length) {
243 // endIndex can't be past-the-end of the last cell unless 232 // endIndex can't be past-the-end of the last cell unless
244 // startIndex is too, so we don't have to do another 233 // startIndex is too, so we don't have to do another
245 // extension here. 234 // extension here.
246 translatedEndIndex = cells.byteLength; 235 translatedEndIndex = cells.byteLength;
247 } else { 236 } else {
248 translatedEndIndex = textToBraille[endIndex]; 237 translatedEndIndex = textToBraille[endIndex];
249 } 238 }
250 this.translatedContent_ = cells; 239 // Add the cursor to cells.
251 // Copy the translated content to a separate buffer and add the cursor 240 this.writeCursor_(cells, translatedStartIndex, translatedEndIndex);
252 // to it.
253 this.displayedContent_ = new ArrayBuffer(cells.byteLength);
254 new Uint8Array(this.displayedContent_).set(new Uint8Array(cells));
255 this.writeCursor_(this.displayedContent_,
256 translatedStartIndex, translatedEndIndex);
257 targetPosition = translatedStartIndex; 241 targetPosition = translatedStartIndex;
258 } else { 242 } else {
259 this.translatedContent_ = this.displayedContent_ = cells;
260 targetPosition = 0; 243 targetPosition = 0;
261 } 244 }
262 this.panStrategy_.setContent(this.translatedContent_, targetPosition); 245 this.panStrategy_.setContent(this.content_.text.toString(),
246 cells, brailleToText, targetPosition);
247
263 this.refresh_(); 248 this.refresh_();
264 }.bind(this); 249 }.bind(this);
265 250
266 var translator = this.translatorManager_.getExpandingTranslator(); 251 var translator = this.translatorManager_.getExpandingTranslator();
267 if (!translator) { 252 if (!translator) {
268 writeTranslatedContent(new ArrayBuffer(0), [], []); 253 writeTranslatedContent(new ArrayBuffer(0), [], []);
269 } else { 254 } else {
270 translator.translate( 255 translator.translate(
271 newContent.text, 256 newContent.text,
272 newExpansionType, 257 newExpansionType,
273 writeTranslatedContent); 258 writeTranslatedContent);
274 } 259 }
275 }; 260 };
276 261
277 262
278 /** 263 /**
279 * @param {cvox.BrailleKeyEvent} event The key event. 264 * @param {cvox.BrailleKeyEvent} event The key event.
280 * @private 265 * @private
281 */ 266 */
282 cvox.BrailleDisplayManager.prototype.onKeyEvent_ = function(event) { 267 cvox.BrailleDisplayManager.prototype.onKeyEvent_ = function(event) {
283 switch (event.command) { 268 switch (event.command) {
284 case cvox.BrailleKeyCommand.PAN_LEFT: 269 case cvox.BrailleKeyCommand.PAN_LEFT:
285 this.panLeft_(); 270 this.panLeft_();
286 break; 271 break;
287 case cvox.BrailleKeyCommand.PAN_RIGHT: 272 case cvox.BrailleKeyCommand.PAN_RIGHT:
288 this.panRight_(); 273 this.panRight_();
289 break; 274 break;
290 case cvox.BrailleKeyCommand.ROUTING: 275 case cvox.BrailleKeyCommand.ROUTING:
291 event.displayPosition = this.brailleToTextPosition_( 276 event.displayPosition = this.brailleToTextPosition_(
292 event.displayPosition + this.panStrategy_.viewPort.start); 277 event.displayPosition + this.panStrategy_.viewPort.firstRow *
278 this.panStrategy_.displaySize.columns);
293 // fall through 279 // fall through
294 default: 280 default:
295 this.commandListener_(event, this.content_); 281 this.commandListener_(event, this.content_);
296 break; 282 break;
297 } 283 }
298 }; 284 };
299 285
300 286
301 /** 287 /**
302 * Shift the display by one full display size and refresh the content. 288 * Shift the display by one full display size and refresh the content.
(...skipping 21 matching lines...) Expand all
324 cvox.BrailleDisplayManager.prototype.panRight_ = function() { 310 cvox.BrailleDisplayManager.prototype.panRight_ = function() {
325 if (this.panStrategy_.next()) { 311 if (this.panStrategy_.next()) {
326 this.refresh_(); 312 this.refresh_();
327 } else { 313 } else {
328 this.commandListener_({ 314 this.commandListener_({
329 command: cvox.BrailleKeyCommand.PAN_RIGHT 315 command: cvox.BrailleKeyCommand.PAN_RIGHT
330 }, this.content_); 316 }, this.content_);
331 } 317 }
332 }; 318 };
333 319
334
335 /** 320 /**
336 * Writes a cursor in the specified range into translated content. 321 * Writes a cursor in the specified range into translated content.
337 * @param {ArrayBuffer} buffer Buffer to add cursor to. 322 * @param {ArrayBuffer} buffer Buffer to add cursor to.
338 * @param {number} startIndex The start index to place the cursor. 323 * @param {number} startIndex The start index to place the cursor.
339 * @param {number} endIndex The end index to place the cursor (exclusive). 324 * @param {number} endIndex The end index to place the cursor (exclusive).
340 * @private 325 * @private
341 */ 326 */
342 cvox.BrailleDisplayManager.prototype.writeCursor_ = function( 327 cvox.BrailleDisplayManager.prototype.writeCursor_ = function(
343 buffer, startIndex, endIndex) { 328 buffer, startIndex, endIndex) {
344 if (startIndex < 0 || startIndex >= buffer.byteLength || 329 if (startIndex < 0 || startIndex >= buffer.byteLength ||
345 endIndex < startIndex || endIndex > buffer.byteLength) { 330 endIndex < startIndex || endIndex > buffer.byteLength) {
346 return; 331 return;
347 } 332 }
348 if (startIndex == endIndex) { 333 if (startIndex == endIndex) {
349 endIndex = startIndex + 1; 334 endIndex = startIndex + 1;
350 } 335 }
351 var dataView = new DataView(buffer); 336 var dataView = new DataView(buffer);
352 while (startIndex < endIndex) { 337 while (startIndex < endIndex) {
353 var value = dataView.getUint8(startIndex); 338 var value = dataView.getUint8(startIndex);
354 value |= cvox.BrailleDisplayManager.CURSOR_DOTS_; 339 value |= cvox.BrailleDisplayManager.CURSOR_DOTS_;
355 dataView.setUint8(startIndex, value); 340 dataView.setUint8(startIndex, value);
356 startIndex++; 341 startIndex++;
357 } 342 }
358 }; 343 };
359 344
360
361 /** 345 /**
362 * Returns the text position corresponding to an absolute braille position, 346 * Returns the text position corresponding to an absolute braille position,
363 * that is not accounting for the current pan position. 347 * that is not accounting for the current pan position.
364 * @private 348 * @private
365 * @param {number} braillePosition Braille position relative to the startof 349 * @param {number} braillePosition Braille position relative to the startof
366 * the translated content. 350 * the translated content.
367 * @return {number} The mapped position in code units. 351 * @return {number} The mapped position in code units.
368 */ 352 */
369 cvox.BrailleDisplayManager.prototype.brailleToTextPosition_ = 353 cvox.BrailleDisplayManager.prototype.brailleToTextPosition_ =
370 function(braillePosition) { 354 function(braillePosition) {
371 var mapping = this.brailleToText_; 355 var mapping = this.panStrategy_.brailleToText;
372 if (braillePosition < 0) { 356 if (braillePosition < 0) {
373 // This shouldn't happen. 357 // This shouldn't happen.
374 console.error('WARNING: Braille position < 0: ' + braillePosition); 358 console.error('WARNING: Braille position < 0: ' + braillePosition);
375 return 0; 359 return 0;
376 } else if (braillePosition >= mapping.length) { 360 } else if (braillePosition >= mapping.length) {
377 // This happens when the user clicks on the right part of the display 361 // This happens when the user clicks on the right part of the display
378 // when it is not entirely filled with content. Allow addressing the 362 // when it is not entirely filled with content. Allow addressing the
379 // position after the last character. 363 // position after the last character.
380 return this.content_.text.length; 364 return this.content_.text.length;
381 } else { 365 } else {
382 return mapping[braillePosition]; 366 return mapping[braillePosition];
383 } 367 }
384 }; 368 };
385 369
386
387 /** 370 /**
388 * @param {boolean} wordWrap 371 * @param {boolean} wordWrap
389 * @private 372 * @private
390 */ 373 */
391 cvox.BrailleDisplayManager.prototype.updatePanStrategy_ = function(wordWrap) { 374 cvox.BrailleDisplayManager.prototype.updatePanStrategy_ = function(wordWrap) {
392 var newStrategy = wordWrap ? new cvox.WrappingPanStrategy() : 375 this.panStrategy_.setPanStrategy(wordWrap);
393 new cvox.FixedPanStrategy();
394 newStrategy.setDisplaySize(this.displayState_.textColumnCount || 0);
395 newStrategy.setContent(this.translatedContent_,
396 this.panStrategy_.viewPort.start);
397 this.panStrategy_ = newStrategy;
398 this.refresh_(); 376 this.refresh_();
399 }; 377 };
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/resources/chromeos/chromevox/braille/braille_display_manager_test.unitjs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698