on master help sign in
Add native support for alt and meta keys
6623419Evan Czaplicki
libraries/Native/Signal/Keyboard.js+55−22
@@ -17,27 +17,36 @@ Elm.Native.Keyboard.make = function(elm) {
17 var NList = Elm.Native.List.make(elm);
17 var NList = Elm.Native.List.make(elm);
18 var Utils = Elm.Native.Utils.make(elm);
18 var Utils = Elm.Native.Utils.make(elm);
19
19
20− var downEvents = Signal.constant(0);
20+ var downEvents = Signal.constant(null);
21− var upEvents = Signal.constant(0);
21+ var upEvents = Signal.constant(null);
22− var blurEvents = Signal.constant(0);
22+ var blurEvents = Signal.constant(null);
23
23
24 elm.addListener([downEvents.id], document, 'keydown', function down(e) {
24 elm.addListener([downEvents.id], document, 'keydown', function down(e) {
25− elm.notify(downEvents.id, e.keyCode);
25+ elm.notify(downEvents.id, e);
26 });
26 });
27
27
28 elm.addListener([upEvents.id], document, 'keyup', function up(e) {
28 elm.addListener([upEvents.id], document, 'keyup', function up(e) {
29− elm.notify(upEvents.id, e.keyCode);
29+ elm.notify(upEvents.id, e);
30 });
30 });
31
31
32 elm.addListener([blurEvents.id], document, 'blur', function blur(e) {
32 elm.addListener([blurEvents.id], document, 'blur', function blur(e) {
33− elm.notify(blurEvents.id, NList.Nil);
33+ elm.notify(blurEvents.id, null);
34 });
34 });
35
35
36+ function state(alt, meta, keyCodes) {
37+ return {
38+ alt: alt,
39+ meta: meta,
40+ keyCodes: keyCodes
41+ };
42+ }
43+ var emptyState = state(false, false, NList.Nil);
44+
36 function KeyMerge(down, up, blur) {
45 function KeyMerge(down, up, blur) {
37 var args = [down,up,blur];
46 var args = [down,up,blur];
38 this.id = Utils.guid();
47 this.id = Utils.guid();
39 // Ignore starting values here
48 // Ignore starting values here
40− this.value = NList.Nil
49+ this.value = emptyState;
41 this.kids = [];
50 this.kids = [];
42
51
43 var n = args.length;
52 var n = args.length;
@@ -48,18 +57,22 @@ Elm.Native.Keyboard.make = function(elm) {
48 ++count;
57 ++count;
49 if (changed) {
58 if (changed) {
50 // We know this a change must only be one of the following cases
59 // We know this a change must only be one of the following cases
51− if (parentID === down.id && !A2(NList.member, down.value, this.value)) {
60+ if (parentID === down.id && !A2(NList.member, down.value.keyCode, this.value.keyCodes)) {
52 isChanged = true;
61 isChanged = true;
62+ var v = down.value;
53− this.value = NList.Cons(down.value, this.value);
63+ var newCodes = NList.Cons(v.keyCode, this.value.keyCodes);
64+ this.value = state(v.altKey, v.metaKey, newCodes);
54 }
65 }
55 else if (parentID === up.id) {
66 else if (parentID === up.id) {
56 isChanged = true;
67 isChanged = true;
68+ var v = up.value;
57− var notEq = function(kc) { return kc !== up.value };
69+ var notEq = function(kc) { return kc !== v.keyCode };
58− this.value = A2(NList.filter, notEq, this.value);
70+ var newCodes = A2(NList.filter, notEq, this.value.keyCodes);
71+ this.value = state(v.altKey, v.metaKey, newCodes);
59 }
72 }
60 else if (parentID === blur.id) {
73 else if (parentID === blur.id) {
61 isChanged = true;
74 isChanged = true;
62− this.value = NList.Nil;
75+ this.value = emptyState;
63 }
76 }
64 }
77 }
65 if (count == n) {
78 if (count == n) {
@@ -71,45 +84,65 @@ Elm.Native.Keyboard.make = function(elm) {
71
84
72 for (var i = n; i--; ) {
85 for (var i = n; i--; ) {
73 args[i].kids.push(this);
86 args[i].kids.push(this);
87+ args[i].defaultNumberOfKids += 1;
74 }
88 }
75 }
89 }
76
90
77− var keysDown = Signal.dropRepeats(new KeyMerge(downEvents,upEvents,blurEvents));
91+ var keyMerge = new KeyMerge(downEvents,upEvents,blurEvents);
78
92
93+ // select a part of a keyMerge and dropRepeats the result
79 function keySignal(f) {
94 function keySignal(f) {
80− var signal = A2(Signal.lift, f, keysDown);
95+ var signal = A2(Signal.lift, f, keyMerge);
81 // must set the default number of kids to make it possible to filter
96 // must set the default number of kids to make it possible to filter
82 // these signals if they are not actually used.
97 // these signals if they are not actually used.
83− keysDown.defaultNumberOfKids += 1;
98+ keyMerge.defaultNumberOfKids += 1;
84 signal.defaultNumberOfKids = 1;
99 signal.defaultNumberOfKids = 1;
85− var filtered = Signal.dropRepeats(signal)
100+ var filtered = Signal.dropRepeats(signal);
86 filtered.defaultNumberOfKids = 0;
101 filtered.defaultNumberOfKids = 0;
87 return filtered;
102 return filtered;
88 }
103 }
89
104
105+ // break keyMerge into parts
106+ var keysDown = keySignal(function getKeyCodes(v) {
107+ return v.keyCodes;
108+ });
109+ var alt = keySignal(function getKeyCodes(v) {
110+ return v.alt;
111+ });
112+ var meta = keySignal(function getKeyCodes(v) {
113+ return v.meta;
114+ });
115+
90 function dir(up, down, left, right) {
116 function dir(up, down, left, right) {
91− function f(ks) {
117+ function toDirections(state) {
118+ var keyCodes = state.keyCodes;
92 var x = 0, y = 0;
119 var x = 0, y = 0;
93− while (ks.ctor === "::") {
120+ while (keyCodes.ctor === "::") {
94− switch (ks._0) {
121+ switch (keyCodes._0) {
95 case left : --x; break;
122 case left : --x; break;
96 case right: ++x; break;
123 case right: ++x; break;
97 case up : ++y; break;
124 case up : ++y; break;
98 case down : --y; break;
125 case down : --y; break;
99 }
126 }
100− ks = ks._1;
127+ keyCodes = keyCodes._1;
101 }
128 }
102 return { _:{}, x:x, y:y };
129 return { _:{}, x:x, y:y };
103 }
130 }
104− return keySignal(f);
131+ return keySignal(toDirections);
105 }
132 }
106
133
107− function is(key) { return keySignal(NList.member(key)); }
134+ function is(key) {
135+ return keySignal(function(v) {
136+ return A2( NList.member, key, v.keyCodes );
137+ });
138+ }
108
139
109 var lastPressed = downEvents;
140 var lastPressed = downEvents;
110
141
111 return elm.Native.Keyboard.values = {
142 return elm.Native.Keyboard.values = {
112 isDown:is,
143 isDown:is,
144+ alt: alt,
145+ meta: meta,
113 directions:F4(dir),
146 directions:F4(dir),
114 keysDown:keysDown,
147 keysDown:keysDown,
115 lastPressed:lastPressed
148 lastPressed:lastPressed