OLD | NEW |
(Empty) | |
| 1 // Copyright 2014 The ChromeOS IME Authors. All Rights Reserved. |
| 2 // limitations under the License. |
| 3 // See the License for the specific language governing permissions and |
| 4 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 5 // distributed under the License is distributed on an "AS-IS" BASIS, |
| 6 // Unless required by applicable law or agreed to in writing, software |
| 7 // |
| 8 // http://www.apache.org/licenses/LICENSE-2.0 |
| 9 // |
| 10 // You may obtain a copy of the License at |
| 11 // you may not use this file except in compliance with the License. |
| 12 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 13 // |
| 14 |
| 15 /** |
| 16 * @fileoverview Handwriting input canvas. |
| 17 * @author har@google.com (Henry A. Rowley) |
| 18 */ |
| 19 |
| 20 goog.provide('i18n.input.hwt.Canvas'); |
| 21 |
| 22 goog.require('goog.a11y.aria.Announcer'); |
| 23 goog.require('goog.a11y.aria.LivePriority'); |
| 24 goog.require('goog.dom.TagName'); |
| 25 goog.require('goog.dom.classlist'); |
| 26 goog.require('goog.events.Event'); |
| 27 goog.require('goog.events.EventHandler'); |
| 28 goog.require('goog.events.EventType'); |
| 29 goog.require('goog.log'); |
| 30 goog.require('goog.ui.Container'); |
| 31 goog.require('i18n.input.hwt.EventType'); |
| 32 goog.require('i18n.input.hwt.StrokeHandler'); |
| 33 goog.require('i18n.input.hwt.css'); |
| 34 |
| 35 |
| 36 |
| 37 /** |
| 38 * The handwriting canvas UI element. |
| 39 * |
| 40 * @constructor |
| 41 * @param {!Document=} opt_topDocument The top document for MOUSEUP event. |
| 42 * @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper. |
| 43 * @param {goog.events.EventTarget=} opt_eventTarget The event target. |
| 44 * @param {number=} opt_inkWidth The ink width. |
| 45 * @param {string=} opt_inkColor The ink color. |
| 46 * @extends {goog.ui.Container} |
| 47 */ |
| 48 i18n.input.hwt.Canvas = function(opt_topDocument, opt_domHelper, |
| 49 opt_eventTarget, opt_inkWidth, opt_inkColor) { |
| 50 goog.base(this, undefined, undefined, opt_domHelper); |
| 51 this.setParentEventTarget(opt_eventTarget || null); |
| 52 |
| 53 /** |
| 54 * The stroke handler. |
| 55 * |
| 56 * @type {!i18n.input.hwt.StrokeHandler} |
| 57 * @private |
| 58 */ |
| 59 this.strokeHandler_; |
| 60 |
| 61 /** |
| 62 * The top document. |
| 63 * |
| 64 * @type {!Document} |
| 65 * @private |
| 66 */ |
| 67 this.topDocument_ = opt_topDocument || document; |
| 68 |
| 69 /** |
| 70 * Canvas which is the area showing the ink. Note that since we're |
| 71 * using a canvas here probably this code won't work in IE. |
| 72 * |
| 73 * @type {!Element} |
| 74 * @private |
| 75 */ |
| 76 this.writingCanvas_; |
| 77 |
| 78 /** |
| 79 * Context for drawing into the writing canvas. |
| 80 * |
| 81 * @type {!CanvasRenderingContext2D} |
| 82 * @private |
| 83 */ |
| 84 this.writingContext_; |
| 85 |
| 86 /** |
| 87 * An array of strokes for passing to the recognizer. |
| 88 * |
| 89 * @type {!Array} |
| 90 */ |
| 91 this.strokeList = []; |
| 92 |
| 93 /** |
| 94 * The current stroke. |
| 95 * |
| 96 * @type {!Array} |
| 97 * @private |
| 98 */ |
| 99 this.stroke_ = []; |
| 100 |
| 101 /** |
| 102 * Starting time of the stroke in milliseconds. |
| 103 * |
| 104 * @type {number} |
| 105 * @private |
| 106 */ |
| 107 this.startTime_ = 0; |
| 108 |
| 109 /** |
| 110 * Event handler. |
| 111 * |
| 112 * @type {!goog.events.EventHandler} |
| 113 * @private |
| 114 */ |
| 115 this.handler_ = new goog.events.EventHandler(this); |
| 116 |
| 117 /** |
| 118 * The annoucer for screen reader. |
| 119 * |
| 120 * @type {!goog.a11y.aria.Announcer} |
| 121 * @private |
| 122 */ |
| 123 this.announcer_ = new goog.a11y.aria.Announcer(this.getDomHelper()); |
| 124 |
| 125 /** |
| 126 * Width of the ink line. |
| 127 * |
| 128 * @type {number} |
| 129 * @private |
| 130 */ |
| 131 this.inkWidth_ = opt_inkWidth || 6; |
| 132 |
| 133 /** |
| 134 * Color of the ink before it has been recognized. |
| 135 * |
| 136 * @type {string} |
| 137 * @private |
| 138 */ |
| 139 this.inkColor_ = opt_inkColor || '#4D90FE'; |
| 140 }; |
| 141 goog.inherits(i18n.input.hwt.Canvas, goog.ui.Container); |
| 142 |
| 143 |
| 144 /** |
| 145 * The class logger. |
| 146 * |
| 147 * @type {goog.log.Logger} |
| 148 * @private |
| 149 */ |
| 150 i18n.input.hwt.Canvas.prototype.logger_ = |
| 151 goog.log.getLogger('i18n.input.hwt.Canvas'); |
| 152 |
| 153 |
| 154 /** |
| 155 * @desc Label for handwriting panel used to draw strokes. |
| 156 */ |
| 157 i18n.input.hwt.Canvas.MSG_INPUTTOOLS_HWT_PANEL = goog.getMsg('panel'); |
| 158 |
| 159 |
| 160 /** |
| 161 * @desc The hint on the canvas to indicate users they can draw here. |
| 162 */ |
| 163 i18n.input.hwt.Canvas.MSG_HANDWRITING_HINT = goog.getMsg('Draw a symbol here'); |
| 164 |
| 165 |
| 166 /** @override */ |
| 167 i18n.input.hwt.Canvas.prototype.createDom = function() { |
| 168 goog.base(this, 'createDom'); |
| 169 |
| 170 var dom = this.getDomHelper(); |
| 171 this.writingCanvas_ = dom.createDom(goog.dom.TagName.CANVAS, |
| 172 i18n.input.hwt.css.CANVAS); |
| 173 this.writingCanvas_.width = 425; |
| 174 this.writingCanvas_.height = 194; |
| 175 dom.appendChild(this.getElement(), this.writingCanvas_); |
| 176 this.writingContext_ = this.writingCanvas_.getContext('2d'); |
| 177 }; |
| 178 |
| 179 |
| 180 /** @override */ |
| 181 i18n.input.hwt.Canvas.prototype.enterDocument = function() { |
| 182 goog.base(this, 'enterDocument'); |
| 183 |
| 184 this.setFocusable(false); |
| 185 this.setFocusableChildrenAllowed(false); |
| 186 // Sets up stroke handler. |
| 187 this.strokeHandler_ = new i18n.input.hwt.StrokeHandler(this.writingCanvas_, |
| 188 this.topDocument_); |
| 189 this.handler_. |
| 190 listen(this.strokeHandler_, |
| 191 i18n.input.hwt.StrokeHandler.EventType.STROKE_START, |
| 192 this.onStrokeStart_). |
| 193 listen(this.strokeHandler_, |
| 194 i18n.input.hwt.StrokeHandler.EventType.STROKE, |
| 195 this.onStroke_). |
| 196 listen(this.strokeHandler_, |
| 197 i18n.input.hwt.StrokeHandler.EventType.STROKE_END, |
| 198 this.onStrokeEnd_). |
| 199 listen(this.writingCanvas_, goog.events.EventType.MOUSEOVER, |
| 200 this.onMouseOver_). |
| 201 listen(this.writingCanvas_, goog.events.EventType.MOUSEDOWN, |
| 202 goog.events.Event.stopPropagation); |
| 203 }; |
| 204 |
| 205 |
| 206 /** |
| 207 * Shows the hint message for the canvas. |
| 208 */ |
| 209 i18n.input.hwt.Canvas.prototype.showHint = function() { |
| 210 this.writingContext_.font = '20px aria,sans-serif'; |
| 211 this.writingContext_.fontWeight = 'bold'; |
| 212 this.writingContext_.fillStyle = '#CCC'; |
| 213 this.writingContext_.fillText(i18n.input.hwt.Canvas.MSG_HANDWRITING_HINT, |
| 214 30, 120); |
| 215 }; |
| 216 |
| 217 |
| 218 /** |
| 219 * Draw a point with the given color. |
| 220 * |
| 221 * @param {string} color Color to draw the point. |
| 222 * @param {!i18n.input.hwt.StrokeHandler.Point} point The point. |
| 223 * @private |
| 224 */ |
| 225 i18n.input.hwt.Canvas.prototype.drawPoint_ = function(color, point) { |
| 226 this.writingContext_.beginPath(); |
| 227 this.writingContext_.strokeStyle = color; |
| 228 this.writingContext_.fillStyle = color; |
| 229 this.writingContext_.arc(point.x, point.y, |
| 230 this.inkWidth_ / 2, 0, Math.PI * 2, true); |
| 231 this.writingContext_.fill(); |
| 232 }; |
| 233 |
| 234 |
| 235 /** |
| 236 * Draw a poly-line given the list of array of points. |
| 237 * |
| 238 * @param {string} color Color to draw the line. |
| 239 * @param {!Array.<!i18n.input.hwt.StrokeHandler.Point>} points The array of |
| 240 * points. |
| 241 * @param {number=} opt_start The start point to draw. |
| 242 * @private |
| 243 */ |
| 244 i18n.input.hwt.Canvas.prototype.drawLine_ = function(color, points, opt_start) { |
| 245 var start = opt_start || 0; |
| 246 if (start == (points.length - 1)) { |
| 247 // If there's only one point. |
| 248 this.drawPoint_(color, points[0]); |
| 249 } else { |
| 250 this.writingContext_.beginPath(); |
| 251 this.writingContext_.strokeStyle = color; |
| 252 this.writingContext_.fillStyle = 'none'; |
| 253 this.writingContext_.lineWidth = this.inkWidth_; |
| 254 this.writingContext_.lineCap = 'round'; |
| 255 this.writingContext_.lineJoin = 'round'; |
| 256 this.writingContext_.moveTo(points[start].x, points[start].y); |
| 257 for (var i = start + 1; i < points.length; i++) { |
| 258 this.writingContext_.lineTo(points[i].x, points[i].y); |
| 259 } |
| 260 this.writingContext_.stroke(); |
| 261 } |
| 262 }; |
| 263 |
| 264 |
| 265 /** |
| 266 * Add a point to the current stroke. |
| 267 * |
| 268 * @param {!i18n.input.hwt.StrokeHandler.Point} point The point. |
| 269 * @private |
| 270 */ |
| 271 i18n.input.hwt.Canvas.prototype.addPoint_ = function(point) { |
| 272 point.time -= this.startTime_; |
| 273 this.stroke_.push(point); |
| 274 var len = this.stroke_.length; |
| 275 var start = Math.max(len - 2, 0); |
| 276 this.drawLine_(this.inkColor_, this.stroke_, start); |
| 277 }; |
| 278 |
| 279 |
| 280 /** |
| 281 * Callback for the start of a stroke. |
| 282 * |
| 283 * @param {!i18n.input.hwt.StrokeHandler.StrokeEvent} e The stroke event. |
| 284 * @private |
| 285 */ |
| 286 i18n.input.hwt.Canvas.prototype.onStrokeStart_ = function(e) { |
| 287 this.stroke_ = []; |
| 288 var point = e.point; |
| 289 if (this.strokeList.length == 0) { |
| 290 this.startTime_ = point.time; |
| 291 } |
| 292 this.addPoint_(e.point); |
| 293 e.preventDefault(); |
| 294 |
| 295 goog.dom.classlist.add(this.getElement(), i18n.input.hwt.css.IME_INIT_OPAQUE); |
| 296 }; |
| 297 |
| 298 |
| 299 /** |
| 300 * Callback for the stroke event. |
| 301 * |
| 302 * @param {!i18n.input.hwt.StrokeHandler.StrokeEvent} e The stroke event. |
| 303 * @private |
| 304 */ |
| 305 i18n.input.hwt.Canvas.prototype.onStroke_ = function(e) { |
| 306 this.addPoint_(e.point); |
| 307 e.preventDefault(); |
| 308 }; |
| 309 |
| 310 |
| 311 /** |
| 312 * Callback for the end of a stroke. |
| 313 * |
| 314 * @param {i18n.input.hwt.StrokeHandler.StrokeEvent} e The stroke event. |
| 315 * @private |
| 316 */ |
| 317 i18n.input.hwt.Canvas.prototype.onStrokeEnd_ = function(e) { |
| 318 this.strokeList.push(this.stroke_); |
| 319 e.preventDefault(); |
| 320 this.dispatchEvent(new goog.events.Event( |
| 321 i18n.input.hwt.EventType.RECOGNITION_READY)); |
| 322 }; |
| 323 |
| 324 |
| 325 /** |
| 326 * Clears the writing canvas. |
| 327 */ |
| 328 i18n.input.hwt.Canvas.prototype.reset = function() { |
| 329 goog.log.info(this.logger_, 'clear ' + this.writingCanvas_.width + 'x' + |
| 330 this.writingCanvas_.height); |
| 331 this.writingContext_.clearRect( |
| 332 0, 0, this.writingCanvas_.width, this.writingCanvas_.height); |
| 333 this.strokeList = []; |
| 334 this.stroke_ = []; |
| 335 this.strokeHandler_.reset(); |
| 336 }; |
| 337 |
| 338 |
| 339 /** @override */ |
| 340 i18n.input.hwt.Canvas.prototype.disposeInternal = function() { |
| 341 goog.dispose(this.handler_); |
| 342 goog.base(this, 'disposeInternal'); |
| 343 }; |
| 344 |
| 345 |
| 346 /** |
| 347 * Gets the width of the canvas. |
| 348 * |
| 349 * @return {number} The width of the canvas. |
| 350 */ |
| 351 i18n.input.hwt.Canvas.prototype.getWidth = function() { |
| 352 return this.writingCanvas_.width; |
| 353 }; |
| 354 |
| 355 |
| 356 /** |
| 357 * Gets the height of the canvas. |
| 358 * |
| 359 * @return {number} The height of the canvas. |
| 360 */ |
| 361 i18n.input.hwt.Canvas.prototype.getHeight = function() { |
| 362 return this.writingCanvas_.height; |
| 363 }; |
| 364 |
| 365 |
| 366 /** |
| 367 * True if user is drawing. |
| 368 * |
| 369 * @return {boolean} True if is drawing. |
| 370 */ |
| 371 i18n.input.hwt.Canvas.prototype.isDrawing = function() { |
| 372 return this.strokeHandler_.drawing; |
| 373 }; |
| 374 |
| 375 |
| 376 /** |
| 377 * True if the canvas is clean without strokes. |
| 378 * |
| 379 * @return {boolean} True if the canvas is clean. |
| 380 */ |
| 381 i18n.input.hwt.Canvas.prototype.isClean = function() { |
| 382 return !this.strokeHandler_.drawing && this.strokeList.length == 0; |
| 383 }; |
| 384 |
| 385 |
| 386 /** |
| 387 * Sets the size of the canvas. |
| 388 * |
| 389 * @param {number=} opt_height The height. |
| 390 * @param {number=} opt_width The width. |
| 391 */ |
| 392 i18n.input.hwt.Canvas.prototype.setSize = function(opt_height, opt_width) { |
| 393 if (opt_height && this.writingCanvas_.height != opt_height || |
| 394 opt_width && this.writingCanvas_.width != opt_width) { |
| 395 this.reset(); |
| 396 } |
| 397 if (opt_height) { |
| 398 this.writingCanvas_.height = opt_height; |
| 399 } |
| 400 if (opt_width) { |
| 401 this.writingCanvas_.width = opt_width; |
| 402 } |
| 403 }; |
| 404 |
| 405 |
| 406 /** |
| 407 * Gets the stroke handler. |
| 408 * |
| 409 * @return {!i18n.input.hwt.StrokeHandler} The stroke handler. |
| 410 */ |
| 411 i18n.input.hwt.Canvas.prototype.getStrokeHandler = function() { |
| 412 return this.strokeHandler_; |
| 413 }; |
| 414 |
| 415 |
| 416 /** |
| 417 * Exports message to screen reader. |
| 418 * |
| 419 * @param {!goog.events.BrowserEvent} e . |
| 420 * @private |
| 421 */ |
| 422 i18n.input.hwt.Canvas.prototype.onMouseOver_ = function(e) { |
| 423 this.announcer_.say(i18n.input.hwt.Canvas.MSG_INPUTTOOLS_HWT_PANEL, |
| 424 goog.a11y.aria.LivePriority.ASSERTIVE); |
| 425 }; |
OLD | NEW |