2 -- Copyright 2013 The Chromium Authors. All rights reserved.
3 -- Use of this source code is governed by a BSD-style license that can be
4 -- found in the LICENSE file.
7 <polymer-element name="kb-shift-key"
8 attributes="lowerCaseKeysetId upperCaseKeysetId"
9 class="shift dark" char="Shift" on-pointerout="out" extends="kb-key">
14 * The possible states of the shift key.
15 * Unlocked is the default state. Locked for capslocked, pressed is a
16 * key-down and tapped for a key-down followed by an immediate key-up.
21 PRESSED: "pressed", // Key-down on shift key.
22 LOCKED: "locked", // Key is capslocked.
23 UNLOCKED: "unlocked", // Default state.
24 TAPPED: "tapped", // Key-down followed by key-up.
25 CHORDING: "chording" // Key-down followed by other keys.
29 * The pointerdown event on shiftkey that may eventually trigger chording
30 * state. pointerId and eventTarget are the two fields that is used now.
31 * @type {PointerEvent}
33 var enterChordingEvent = undefined;
36 * Uses a closure to define one long press timer among all shift keys
37 * regardless of the layout they are in.
40 var shiftLongPressTimer = undefined;
43 * The current state of the shift key.
46 var state = KEY_STATES.UNLOCKED;
48 Polymer('kb-shift-key', {
50 * Defines how capslock effects keyset transition. We always transition
51 * from the lowerCaseKeysetId to the upperCaseKeysetId if capslock is
55 lowerCaseKeysetId: 'lower',
56 upperCaseKeysetId: 'upper',
59 if (state == KEY_STATES.CHORDING &&
60 event.pointerId != enterChordingEvent.pointerId) {
61 // Disables all other pointer events on shift keys when chording.
65 case KEY_STATES.PRESSED:
66 state = KEY_STATES.TAPPED;
68 case KEY_STATES.CHORDING:
69 // Leaves chording only if the pointer that triggered it is
71 state = KEY_STATES.UNLOCKED;
76 // When releasing the shift key, it is not the same shift key that was
77 // pressed. Updates the pointerId of the releasing shift key to make
78 // sure key-up event fires correctly in kb-key-base.
79 this.pointerId = enterChordingEvent.pointerId;
83 out: function(event) {
84 // Sliding off the shift key while chording is treated as a key-up.
85 // Note that we switch to a new keyset on shift keydown, and a finger
86 // movement on the new shift key will trigger this function being
87 // called on the old shift key. We should not end chording in that
89 if (state == KEY_STATES.CHORDING &&
90 event.pointerId == enterChordingEvent.pointerId &&
91 event.target != enterChordingEvent.target) {
92 state = KEY_STATES.UNLOCKED;
93 var detail = this.populateDetails('out');
94 this.fire("key-out", detail);
98 down: function(event) {
99 // First transition state so that populateDetails generates
102 case KEY_STATES.UNLOCKED:
103 state = KEY_STATES.PRESSED;
105 case KEY_STATES.TAPPED:
106 case KEY_STATES.LOCKED:
107 state = KEY_STATES.UNLOCKED;
109 case KEY_STATES.PRESSED:
110 case KEY_STATES.CHORDING:
111 // We pressed another shift key at the same time,
112 // so ignore second press.
115 console.error("Undefined shift key state: " + state);
118 enterChordingEvent = event;
119 // Trigger parent behaviour.
121 this.fire('enable-sel');
122 // Populate double click transition details.
124 detail.char = this.char || this.textContent;
125 detail.toKeyset = this.upperCaseKeysetId;
126 detail.nextKeyset = undefined;
127 detail.callback = this.onDoubleClick;
128 this.fire('enable-dbl', detail);
131 generateLongPressTimer: function() {
132 return this.asyncMethod(function() {
133 var detail = this.populateDetails();
134 if (state == KEY_STATES.LOCKED) {
135 // We don't care about the longpress if we are already
139 state = KEY_STATES.LOCKED;
140 detail.toKeyset = this.upperCaseKeysetId;
141 detail.nextKeyset = undefined;
143 this.fire('key-longpress', detail);
144 }, null, LONGPRESS_DELAY_MSEC);
147 // @return Whether the shift modifier is currently active.
148 isActive: function() {
149 return state != KEY_STATES.UNLOCKED;
153 * Callback function for when a double click is triggered.
155 onDoubleClick: function() {
156 state = KEY_STATES.LOCKED;
160 * Notifies shift key that a non-control key was pressed down.
161 * A control key is defined as one of shift, control or alt.
163 onNonControlKeyDown: function() {
165 case (KEY_STATES.PRESSED):
166 state = KEY_STATES.CHORDING;
167 // Disable longpress timer.
168 clearTimeout(shiftLongPressTimer);
176 * Notifies key that a non-control keyed was typed.
177 * A control key is defined as one of shift, control or alt.
179 onNonControlKeyTyped: function() {
180 if (state == KEY_STATES.TAPPED)
181 state = KEY_STATES.UNLOCKED;
185 * Callback function for when a space is pressed after punctuation.
186 * @return {Object} The keyset transitions the keyboard should make.
188 onSpaceAfterPunctuation: function() {
190 detail.toKeyset = this.upperCaseKeysetId;
191 detail.nextKeyset = this.lowerCaseKeysetId;
192 state = KEY_STATES.TAPPED;
196 populateDetails: function(caller) {
197 var detail = this.super([caller]);
199 case(KEY_STATES.LOCKED):
200 detail.toKeyset = this.upperCaseKeysetId;
202 case(KEY_STATES.UNLOCKED):
203 detail.toKeyset = this.lowerCaseKeysetId;
205 case(KEY_STATES.PRESSED):
206 detail.toKeyset = this.upperCaseKeysetId;
208 case(KEY_STATES.TAPPED):
209 detail.toKeyset = this.upperCaseKeysetId;
210 detail.nextKeyset = this.lowerCaseKeysetId;
212 case(KEY_STATES.CHORDING):
213 detail.toKeyset = this.lowerCaseKeysetId;
222 * Resets the shift key state.
225 state = KEY_STATES.UNLOCKED;
229 * Overrides longPressTimer for the shift key.
231 get longPressTimer() {
232 return shiftLongPressTimer;
235 set longPressTimer(timer) {
236 shiftLongPressTimer = timer;
245 case KEY_STATES.UNLOCKED:
246 return this.lowerCaseKeysetId;
248 return this.upperCaseKeysetId;