| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 part of html; | 5 part of html; |
| 6 | 6 |
| 7 /** | 7 /** |
| 8 * Internal class that does the actual calculations to determine keyCode and | 8 * Internal class that does the actual calculations to determine keyCode and |
| 9 * charCode for keydown, keypress, and keyup events for all browsers. | 9 * charCode for keydown, keypress, and keyup events for all browsers. |
| 10 */ | 10 */ |
| 11 class _KeyboardEventHandler implements EventStreamProvider<KeyEvent> { | 11 class _KeyboardEventHandler extends EventStreamProvider<KeyEvent> { |
| 12 // This code inspired by Closure's KeyHandling library. | 12 // This code inspired by Closure's KeyHandling library. |
| 13 // http://closure-library.googlecode.com/svn/docs/closure_goog_events_keyhandl
er.js.source.html | 13 // http://closure-library.googlecode.com/svn/docs/closure_goog_events_keyhandl
er.js.source.html |
| 14 | 14 |
| 15 /** | 15 /** |
| 16 * The set of keys that have been pressed down without seeing their | 16 * The set of keys that have been pressed down without seeing their |
| 17 * corresponding keyup event. | 17 * corresponding keyup event. |
| 18 */ | 18 */ |
| 19 List<KeyboardEvent> _keyDownList; | 19 final List<KeyboardEvent> _keyDownList = <KeyboardEvent>[]; |
| 20 | |
| 21 /** The set of functions that wish to be notified when a KeyEvent happens. */ | |
| 22 List<Function> _callbacks; | |
| 23 | 20 |
| 24 /** The type of KeyEvent we are tracking (keyup, keydown, keypress). */ | 21 /** The type of KeyEvent we are tracking (keyup, keydown, keypress). */ |
| 25 String _type; | 22 final String _type; |
| 26 | 23 |
| 27 /** The element we are watching for events to happen on. */ | 24 /** The element we are watching for events to happen on. */ |
| 28 EventTarget _target; | 25 final EventTarget _target; |
| 29 | 26 |
| 30 // The distance to shift from upper case alphabet Roman letters to lower case. | 27 // The distance to shift from upper case alphabet Roman letters to lower case. |
| 31 final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0]; | 28 static final int _ROMAN_ALPHABET_OFFSET = "a".codeUnits[0] - "A".codeUnits[0]; |
| 32 | 29 |
| 33 /** Controller to produce KeyEvents for the stream. */ | 30 /** Controller to produce KeyEvents for the stream. */ |
| 34 StreamController _controller; | 31 final StreamController _controller = new StreamController.broadcast(); |
| 35 | 32 |
| 36 String _eventType = 'KeyEvent'; | 33 static const _EVENT_TYPE = 'KeyEvent'; |
| 37 | 34 |
| 38 /** | 35 /** |
| 39 * An enumeration of key identifiers currently part of the W3C draft for DOM3 | 36 * An enumeration of key identifiers currently part of the W3C draft for DOM3 |
| 40 * and their mappings to keyCodes. | 37 * and their mappings to keyCodes. |
| 41 * http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set | 38 * http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set |
| 42 */ | 39 */ |
| 43 static Map<String, int> _keyIdentifier = { | 40 static const Map<String, int> _keyIdentifier = const { |
| 44 'Up': KeyCode.UP, | 41 'Up': KeyCode.UP, |
| 45 'Down': KeyCode.DOWN, | 42 'Down': KeyCode.DOWN, |
| 46 'Left': KeyCode.LEFT, | 43 'Left': KeyCode.LEFT, |
| 47 'Right': KeyCode.RIGHT, | 44 'Right': KeyCode.RIGHT, |
| 48 'Enter': KeyCode.ENTER, | 45 'Enter': KeyCode.ENTER, |
| 49 'F1': KeyCode.F1, | 46 'F1': KeyCode.F1, |
| 50 'F2': KeyCode.F2, | 47 'F2': KeyCode.F2, |
| 51 'F3': KeyCode.F3, | 48 'F3': KeyCode.F3, |
| 52 'F4': KeyCode.F4, | 49 'F4': KeyCode.F4, |
| 53 'F5': KeyCode.F5, | 50 'F5': KeyCode.F5, |
| 54 'F6': KeyCode.F6, | 51 'F6': KeyCode.F6, |
| 55 'F7': KeyCode.F7, | 52 'F7': KeyCode.F7, |
| 56 'F8': KeyCode.F8, | 53 'F8': KeyCode.F8, |
| 57 'F9': KeyCode.F9, | 54 'F9': KeyCode.F9, |
| 58 'F10': KeyCode.F10, | 55 'F10': KeyCode.F10, |
| 59 'F11': KeyCode.F11, | 56 'F11': KeyCode.F11, |
| 60 'F12': KeyCode.F12, | 57 'F12': KeyCode.F12, |
| 61 'U+007F': KeyCode.DELETE, | 58 'U+007F': KeyCode.DELETE, |
| 62 'Home': KeyCode.HOME, | 59 'Home': KeyCode.HOME, |
| 63 'End': KeyCode.END, | 60 'End': KeyCode.END, |
| 64 'PageUp': KeyCode.PAGE_UP, | 61 'PageUp': KeyCode.PAGE_UP, |
| 65 'PageDown': KeyCode.PAGE_DOWN, | 62 'PageDown': KeyCode.PAGE_DOWN, |
| 66 'Insert': KeyCode.INSERT | 63 'Insert': KeyCode.INSERT |
| 67 }; | 64 }; |
| 68 | 65 |
| 69 /** | |
| 70 * Gets the type of the event which this would listen for on the specified | |
| 71 * event target. | |
| 72 */ | |
| 73 String getEventType(EventTarget target) => _eventType; | |
| 74 | |
| 75 /** Return a stream for KeyEvents for the specified target. */ | 66 /** Return a stream for KeyEvents for the specified target. */ |
| 76 Stream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) { | 67 Stream<KeyEvent> forTarget(EventTarget e, {bool useCapture: false}) { |
| 77 return new _KeyboardEventHandler.initializeAllEventListeners( | 68 return new _KeyboardEventHandler.initializeAllEventListeners( |
| 78 _type, e).stream; | 69 _type, e).stream; |
| 79 } | 70 } |
| 80 | 71 |
| 81 /** | 72 /** |
| 82 * Accessor to the stream associated with a particular KeyboardEvent | 73 * Accessor to the stream associated with a particular KeyboardEvent |
| 83 * EventTarget. | 74 * EventTarget. |
| 84 * | 75 * |
| 85 * [forTarget] must be called to initialize this stream to listen to a | 76 * [forTarget] must be called to initialize this stream to listen to a |
| 86 * particular EventTarget. | 77 * particular EventTarget. |
| 87 */ | 78 */ |
| 88 Stream<KeyEvent> get stream { | 79 Stream<KeyEvent> get stream { |
| 89 if(_target != null) { | 80 if(_target != null) { |
| 90 return _controller.stream; | 81 return _controller.stream; |
| 91 } else { | 82 } else { |
| 92 throw new StateError("Not initialized. Call forTarget to access a stream " | 83 throw new StateError("Not initialized. Call forTarget to access a stream " |
| 93 "initialized with a particular EventTarget."); | 84 "initialized with a particular EventTarget."); |
| 94 } | 85 } |
| 95 } | 86 } |
| 96 | 87 |
| 97 /** | 88 /** |
| 98 * General constructor, performs basic initialization for our improved | 89 * General constructor, performs basic initialization for our improved |
| 99 * KeyboardEvent controller. | 90 * KeyboardEvent controller. |
| 100 */ | 91 */ |
| 101 _KeyboardEventHandler(String type) { | 92 _KeyboardEventHandler(this._type) : |
| 102 _commonInit(type); | 93 _target = null, super(_EVENT_TYPE) { |
| 103 } | |
| 104 | |
| 105 void _commonInit(String type) { | |
| 106 _type = type; | |
| 107 _controller = new StreamController.broadcast(); | |
| 108 _callbacks = []; | |
| 109 _target = null; | |
| 110 } | 94 } |
| 111 | 95 |
| 112 /** | 96 /** |
| 113 * Hook up all event listeners under the covers so we can estimate keycodes | 97 * Hook up all event listeners under the covers so we can estimate keycodes |
| 114 * and charcodes when they are not provided. | 98 * and charcodes when they are not provided. |
| 115 */ | 99 */ |
| 116 _KeyboardEventHandler.initializeAllEventListeners(String type, | 100 _KeyboardEventHandler.initializeAllEventListeners(this._type, this._target) : |
| 117 EventTarget target) { | 101 super(_EVENT_TYPE) { |
| 118 _commonInit(type); | |
| 119 _target = target; | |
| 120 _keyDownList = []; | |
| 121 Element.keyDownEvent.forTarget(_target, useCapture: true).listen( | 102 Element.keyDownEvent.forTarget(_target, useCapture: true).listen( |
| 122 processKeyDown); | 103 processKeyDown); |
| 123 Element.keyPressEvent.forTarget(_target, useCapture: true).listen( | 104 Element.keyPressEvent.forTarget(_target, useCapture: true).listen( |
| 124 processKeyPress); | 105 processKeyPress); |
| 125 Element.keyUpEvent.forTarget(_target, useCapture: true).listen( | 106 Element.keyUpEvent.forTarget(_target, useCapture: true).listen( |
| 126 processKeyUp); | 107 processKeyUp); |
| 127 } | 108 } |
| 128 | 109 |
| 129 /** | 110 /** |
| 130 * Notify all callback listeners that a KeyEvent of the relevant type has | 111 * Notify all callback listeners that a KeyEvent of the relevant type has |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 /** Handle keydown events. */ | 294 /** Handle keydown events. */ |
| 314 void processKeyDown(KeyboardEvent e) { | 295 void processKeyDown(KeyboardEvent e) { |
| 315 // Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window | 296 // Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window |
| 316 // before we've caught a key-up event. If the last-key was one of these | 297 // before we've caught a key-up event. If the last-key was one of these |
| 317 // we reset the state. | 298 // we reset the state. |
| 318 if (_keyDownList.length > 0 && | 299 if (_keyDownList.length > 0 && |
| 319 (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey || | 300 (_keyDownList.last.keyCode == KeyCode.CTRL && !e.ctrlKey || |
| 320 _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey || | 301 _keyDownList.last.keyCode == KeyCode.ALT && !e.altKey || |
| 321 Device.userAgent.contains('Mac') && | 302 Device.userAgent.contains('Mac') && |
| 322 _keyDownList.last.keyCode == KeyCode.META && !e.metaKey)) { | 303 _keyDownList.last.keyCode == KeyCode.META && !e.metaKey)) { |
| 323 _keyDownList = []; | 304 _keyDownList.clear(); |
| 324 } | 305 } |
| 325 | 306 |
| 326 var event = new KeyEvent(e); | 307 var event = new KeyEvent(e); |
| 327 event._shadowKeyCode = _normalizeKeyCodes(event); | 308 event._shadowKeyCode = _normalizeKeyCodes(event); |
| 328 // Technically a "keydown" event doesn't have a charCode. This is | 309 // Technically a "keydown" event doesn't have a charCode. This is |
| 329 // calculated nonetheless to provide us with more information in giving | 310 // calculated nonetheless to provide us with more information in giving |
| 330 // as much information as possible on keypress about keycode and also | 311 // as much information as possible on keypress about keycode and also |
| 331 // charCode. | 312 // charCode. |
| 332 event._shadowCharCode = _findCharCodeKeyDown(event); | 313 event._shadowCharCode = _findCharCodeKeyDown(event); |
| 333 if (_keyDownList.length > 0 && event.keyCode != _keyDownList.last.keyCode && | 314 if (_keyDownList.length > 0 && event.keyCode != _keyDownList.last.keyCode && |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 373 /** Handle keyup events. */ | 354 /** Handle keyup events. */ |
| 374 void processKeyUp(KeyboardEvent event) { | 355 void processKeyUp(KeyboardEvent event) { |
| 375 var e = new KeyEvent(event); | 356 var e = new KeyEvent(event); |
| 376 KeyboardEvent toRemove = null; | 357 KeyboardEvent toRemove = null; |
| 377 for (var key in _keyDownList) { | 358 for (var key in _keyDownList) { |
| 378 if (key.keyCode == e.keyCode) { | 359 if (key.keyCode == e.keyCode) { |
| 379 toRemove = key; | 360 toRemove = key; |
| 380 } | 361 } |
| 381 } | 362 } |
| 382 if (toRemove != null) { | 363 if (toRemove != null) { |
| 383 _keyDownList = | 364 _keyDownList.removeWhere((element) => element == toRemove); |
| 384 _keyDownList.where((element) => element != toRemove).toList(); | |
| 385 } else if (_keyDownList.length > 0) { | 365 } else if (_keyDownList.length > 0) { |
| 386 // This happens when we've reached some international keyboard case we | 366 // This happens when we've reached some international keyboard case we |
| 387 // haven't accounted for or we haven't correctly eliminated all browser | 367 // haven't accounted for or we haven't correctly eliminated all browser |
| 388 // inconsistencies. Filing bugs on when this is reached is welcome! | 368 // inconsistencies. Filing bugs on when this is reached is welcome! |
| 389 _keyDownList.removeLast(); | 369 _keyDownList.removeLast(); |
| 390 } | 370 } |
| 391 _dispatch(e); | 371 _dispatch(e); |
| 392 } | 372 } |
| 393 } | 373 } |
| 394 | 374 |
| (...skipping 20 matching lines...) Expand all Loading... |
| 415 new _KeyboardEventHandler('keypress').forTarget(target); | 395 new _KeyboardEventHandler('keypress').forTarget(target); |
| 416 | 396 |
| 417 /** Named constructor to produce a stream for onKeyUp events. */ | 397 /** Named constructor to produce a stream for onKeyUp events. */ |
| 418 static Stream<KeyEvent> onKeyUp(EventTarget target) => | 398 static Stream<KeyEvent> onKeyUp(EventTarget target) => |
| 419 new _KeyboardEventHandler('keyup').forTarget(target); | 399 new _KeyboardEventHandler('keyup').forTarget(target); |
| 420 | 400 |
| 421 /** Named constructor to produce a stream for onKeyDown events. */ | 401 /** Named constructor to produce a stream for onKeyDown events. */ |
| 422 static Stream<KeyEvent> onKeyDown(EventTarget target) => | 402 static Stream<KeyEvent> onKeyDown(EventTarget target) => |
| 423 new _KeyboardEventHandler('keydown').forTarget(target); | 403 new _KeyboardEventHandler('keydown').forTarget(target); |
| 424 } | 404 } |
| OLD | NEW |