• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--
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.
5  -->
6
7<polymer-element name="kb-shift-key"
8    attributes="lowerCaseKeysetId upperCaseKeysetId"
9    class="shift dark" char="Shift" on-pointerout="{{out}}" extends="kb-key">
10  <script>
11    (function () {
12
13      /**
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.
17       * @const
18       * @type {Enum}
19       */
20      var KEY_STATES = {
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.
26      };
27
28      /**
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}
32       */
33      var enterChordingEvent = undefined;
34
35      /**
36       * Uses a closure to define one long press timer among all shift keys
37       * regardless of the layout they are in.
38       * @type {function}
39       */
40      var shiftLongPressTimer = undefined;
41
42      /**
43       * The current state of the shift key.
44       * @type {Enum}
45       */
46      var state = KEY_STATES.UNLOCKED;
47
48      Polymer('kb-shift-key', {
49        /**
50         * Defines how capslock effects keyset transition. We always transition
51         * from the lowerCaseKeysetId to the upperCaseKeysetId if capslock is
52         * on.
53         * @type {string}
54         */
55        lowerCaseKeysetId: 'lower',
56        upperCaseKeysetId: 'upper',
57
58        up: function(event) {
59          if (state == KEY_STATES.CHORDING &&
60              event.pointerId != enterChordingEvent.pointerId) {
61            // Disables all other pointer events on shift keys when chording.
62            return;
63          }
64          switch (state) {
65            case KEY_STATES.PRESSED:
66              state = KEY_STATES.TAPPED;
67              break;
68            case KEY_STATES.CHORDING:
69              // Leaves chording only if the pointer that triggered it is
70              // released.
71              state = KEY_STATES.UNLOCKED;
72              break;
73            default:
74              break;
75          }
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;
80          this.super([event]);
81        },
82
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
88          // case.
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);
95          }
96        },
97
98        down: function(event) {
99          // First transition state so that populateDetails generates
100          // correct data.
101          switch (state) {
102            case KEY_STATES.UNLOCKED:
103              state = KEY_STATES.PRESSED;
104              break;
105            case KEY_STATES.TAPPED:
106            case KEY_STATES.LOCKED:
107              state = KEY_STATES.UNLOCKED;
108              break;
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.
113              return;
114            default:
115              console.error("Undefined shift key state: " + state);
116              break;
117          }
118          enterChordingEvent = event;
119          // Trigger parent behaviour.
120          this.super([event]);
121          this.fire('enable-sel');
122          // Populate double click transition details.
123          var detail = {};
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);
129        },
130
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
136              // capitalized.
137              return;
138            } else {
139              state = KEY_STATES.LOCKED;
140              detail.toKeyset = this.upperCaseKeysetId;
141              detail.nextKeyset = undefined;
142            }
143            this.fire('key-longpress', detail);
144          }, null, LONGPRESS_DELAY_MSEC);
145        },
146
147        // @return Whether the shift modifier is currently active.
148        isActive: function() {
149          return state != KEY_STATES.UNLOCKED;
150        },
151
152        /**
153         * Callback function for when a double click is triggered.
154         */
155        onDoubleClick: function() {
156          state = KEY_STATES.LOCKED;
157        },
158
159        /**
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.
162         */
163        onNonControlKeyDown: function() {
164          switch (state) {
165            case (KEY_STATES.PRESSED):
166              state = KEY_STATES.CHORDING;
167              // Disable longpress timer.
168              clearTimeout(shiftLongPressTimer);
169              break;
170            default:
171              break;
172          }
173        },
174
175        /**
176         * Notifies key that a non-control keyed was typed.
177         * A control key is defined as one of shift, control or alt.
178         */
179        onNonControlKeyTyped: function() {
180          if (state == KEY_STATES.TAPPED)
181            state = KEY_STATES.UNLOCKED;
182        },
183
184        /**
185         * Callback function for when a space is pressed after punctuation.
186         * @return {Object} The keyset transitions the keyboard should make.
187         */
188        onSpaceAfterPunctuation: function() {
189           var detail = {};
190           detail.toKeyset = this.upperCaseKeysetId;
191           detail.nextKeyset = this.lowerCaseKeysetId;
192           state = KEY_STATES.TAPPED;
193           return detail;
194        },
195
196        populateDetails: function(caller) {
197          var detail = this.super([caller]);
198          switch(state) {
199            case(KEY_STATES.LOCKED):
200              detail.toKeyset = this.upperCaseKeysetId;
201              break;
202            case(KEY_STATES.UNLOCKED):
203              detail.toKeyset = this.lowerCaseKeysetId;
204              break;
205            case(KEY_STATES.PRESSED):
206              detail.toKeyset = this.upperCaseKeysetId;
207              break;
208            case(KEY_STATES.TAPPED):
209              detail.toKeyset = this.upperCaseKeysetId;
210              detail.nextKeyset = this.lowerCaseKeysetId;
211              break;
212            case(KEY_STATES.CHORDING):
213              detail.toKeyset = this.lowerCaseKeysetId;
214              break;
215            default:
216              break;
217          }
218          return detail;
219        },
220
221        /**
222         *  Resets the shift key state.
223         */
224        reset: function() {
225          state = KEY_STATES.UNLOCKED;
226        },
227
228        /**
229         * Overrides longPressTimer for the shift key.
230         */
231        get longPressTimer() {
232          return shiftLongPressTimer;
233        },
234
235        set longPressTimer(timer) {
236          shiftLongPressTimer = timer;
237        },
238
239        get state() {
240          return state;
241        },
242
243        get textKeyset() {
244          switch (state) {
245            case KEY_STATES.UNLOCKED:
246              return this.lowerCaseKeysetId;
247            default:
248              return this.upperCaseKeysetId;
249          }
250        },
251      });
252    })();
253  </script>
254</polymer-element>
255