blob: b2dac6b1ed537dcf48ae9c0f257936c3c17a3c83 [file] [log] [blame]
<!--
-- Copyright 2013 The Chromium Authors. All rights reserved.
-- Use of this source code is governed by a BSD-style license that can be
-- found in the LICENSE file.
-->
<polymer-element name="kb-shift-key"
attributes="lowerCaseKeysetId upperCaseKeysetId"
class="shift dark" char="Shift" on-pointerout="out" extends="kb-key">
<script>
(function () {
/**
* The possible states of the shift key.
* Unlocked is the default state. Locked for capslocked, pressed is a
* key-down and tapped for a key-down followed by an immediate key-up.
* @const
* @type {Enum}
*/
var KEY_STATES = {
PRESSED: "pressed", // Key-down on shift key.
LOCKED: "locked", // Key is capslocked.
UNLOCKED: "unlocked", // Default state.
TAPPED: "tapped", // Key-down followed by key-up.
CHORDING: "chording" // Key-down followed by other keys.
};
/**
* The pointerdown event on shiftkey that may eventually trigger chording
* state. pointerId and eventTarget are the two fields that is used now.
* @type {PointerEvent}
*/
var enterChordingEvent = undefined;
/**
* Uses a closure to define one long press timer among all shift keys
* regardless of the layout they are in.
* @type {function}
*/
var shiftLongPressTimer = undefined;
/**
* The current state of the shift key.
* @type {Enum}
*/
var state = KEY_STATES.UNLOCKED;
Polymer('kb-shift-key', {
/**
* Defines how capslock effects keyset transition. We always transition
* from the lowerCaseKeysetId to the upperCaseKeysetId if capslock is
* on.
* @type {string}
*/
lowerCaseKeysetId: 'lower',
upperCaseKeysetId: 'upper',
up: function(event) {
if (state == KEY_STATES.CHORDING &&
event.pointerId != enterChordingEvent.pointerId) {
// Disables all other pointer events on shift keys when chording.
return;
}
switch (state) {
case KEY_STATES.PRESSED:
state = KEY_STATES.TAPPED;
break;
case KEY_STATES.CHORDING:
// Leaves chording only if the pointer that triggered it is
// released.
state = KEY_STATES.UNLOCKED;
break;
default:
break;
}
// When releasing the shift key, it is not the same shift key that was
// pressed. Updates the pointerId of the releasing shift key to make
// sure key-up event fires correctly in kb-key-base.
this.pointerId = enterChordingEvent.pointerId;
this.super([event]);
},
out: function(event) {
// Sliding off the shift key while chording is treated as a key-up.
// Note that we switch to a new keyset on shift keydown, and a finger
// movement on the new shift key will trigger this function being
// called on the old shift key. We should not end chording in that
// case.
if (state == KEY_STATES.CHORDING &&
event.pointerId == enterChordingEvent.pointerId &&
event.target != enterChordingEvent.target) {
state = KEY_STATES.UNLOCKED;
var detail = this.populateDetails('out');
this.fire("key-out", detail);
}
},
down: function(event) {
// First transition state so that populateDetails generates
// correct data.
switch (state) {
case KEY_STATES.UNLOCKED:
state = KEY_STATES.PRESSED;
break;
case KEY_STATES.TAPPED:
case KEY_STATES.LOCKED:
state = KEY_STATES.UNLOCKED;
break;
case KEY_STATES.PRESSED:
case KEY_STATES.CHORDING:
// We pressed another shift key at the same time,
// so ignore second press.
return;
default:
console.error("Undefined shift key state: " + state);
break;
}
enterChordingEvent = event;
// Trigger parent behaviour.
this.super([event]);
this.fire('enable-sel');
// Populate double click transition details.
var detail = {};
detail.char = this.char || this.textContent;
detail.toKeyset = this.upperCaseKeysetId;
detail.nextKeyset = undefined;
detail.callback = this.onDoubleClick;
this.fire('enable-dbl', detail);
},
generateLongPressTimer: function() {
return this.asyncMethod(function() {
var detail = this.populateDetails();
if (state == KEY_STATES.LOCKED) {
// We don't care about the longpress if we are already
// capitalized.
return;
} else {
state = KEY_STATES.LOCKED;
detail.toKeyset = this.upperCaseKeysetId;
detail.nextKeyset = undefined;
}
this.fire('key-longpress', detail);
}, null, LONGPRESS_DELAY_MSEC);
},
/**
* Callback function for when a double click is triggered.
*/
onDoubleClick: function() {
state = KEY_STATES.LOCKED;
},
/**
* Notifies shift key that a non-control key was pressed down.
* A control key is defined as one of shift, control or alt.
*/
onNonControlKeyDown: function() {
switch (state) {
case (KEY_STATES.TAPPED):
state = KEY_STATES.UNLOCKED;
break;
case (KEY_STATES.PRESSED):
state = KEY_STATES.CHORDING;
// Disable longpress timer.
clearTimeout(shiftLongPressTimer);
break;
default:
break;
}
},
/**
* Callback function for when a space is pressed after punctuation.
* @return {Object} The keyset transitions the keyboard should make.
*/
onSpaceAfterPunctuation: function() {
var detail = {};
detail.toKeyset = this.upperCaseKeysetId;
detail.nextKeyset = this.lowerCaseKeysetId;
state = KEY_STATES.TAPPED;
return detail;
},
populateDetails: function(caller) {
var detail = this.super([caller]);
switch(state) {
case(KEY_STATES.LOCKED):
detail.toKeyset = this.upperCaseKeysetId;
break;
case(KEY_STATES.UNLOCKED):
detail.toKeyset = this.lowerCaseKeysetId;
break;
case(KEY_STATES.PRESSED):
detail.toKeyset = this.upperCaseKeysetId;
break;
case(KEY_STATES.TAPPED):
detail.toKeyset = this.upperCaseKeysetId;
detail.nextKeyset = this.lowerCaseKeysetId;
break;
case(KEY_STATES.CHORDING):
detail.toKeyset = this.lowerCaseKeysetId;
break;
default:
break;
}
return detail;
},
/**
* Resets the shift key state.
*/
reset: function() {
state = KEY_STATES.UNLOCKED;
},
/**
* Overrides longPressTimer for the shift key.
*/
get longPressTimer() {
return shiftLongPressTimer;
},
set longPressTimer(timer) {
shiftLongPressTimer = timer;
},
get state() {
return state;
},
get textKeyset() {
switch (state) {
case KEY_STATES.UNLOCKED:
return this.lowerCaseKeysetId;
default:
return this.upperCaseKeysetId;
}
},
});
})();
</script>
</polymer-element>