Skip to content

Commit b31a371

Browse files
author
jmhofer
committed
Added observable for all currently pressed keys
1 parent 306d207 commit b31a371

File tree

2 files changed

+110
-14
lines changed

2 files changed

+110
-14
lines changed

rxjava-contrib/rxjava-swing/src/main/java/rx/SwingObservable.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,15 @@ public Boolean call(KeyEvent event) {
7070
}
7171
});
7272
}
73+
74+
/**
75+
* Creates an observable that emits the set of all currently pressed keys each time
76+
* this set changes.
77+
* @param component
78+
* The component to register the observable for.
79+
* @return Observable of currently pressed keys.
80+
*/
81+
public static Observable<Set<Integer>> currentlyPressedKeys(JComponent component) {
82+
return KeyEventSource.currentlyPressedKeysOf(component);
83+
}
7384
}

rxjava-contrib/rxjava-swing/src/main/java/rx/swing/sources/KeyEventSource.java

Lines changed: 99 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,34 @@
1+
/**
2+
* Copyright 2013 Netflix, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package rx.swing.sources;
217

18+
import static java.util.Arrays.asList;
319
import static org.mockito.Mockito.*;
420

5-
import java.awt.event.ActionEvent;
621
import java.awt.event.KeyEvent;
722
import java.awt.event.KeyListener;
23+
import java.util.Collections;
24+
import java.util.HashSet;
25+
import java.util.Set;
826

927
import javax.swing.JComponent;
1028
import javax.swing.JPanel;
1129

1230
import org.junit.Test;
31+
import org.mockito.InOrder;
1332
import org.mockito.Matchers;
1433

1534
import rx.Observable;
@@ -19,6 +38,7 @@
1938
import rx.util.functions.Action0;
2039
import rx.util.functions.Action1;
2140
import rx.util.functions.Func1;
41+
import rx.util.functions.Func2;
2242

2343
public enum KeyEventSource { ; // no instances
2444

@@ -54,40 +74,105 @@ public void call() {
5474
});
5575
}
5676

77+
public static Observable<Set<Integer>> currentlyPressedKeysOf(JComponent component) {
78+
return Observable.<KeyEvent, Set<Integer>>scan(fromKeyEventsOf(component), new HashSet<Integer>(), new Func2<Set<Integer>, KeyEvent, Set<Integer>>() {
79+
@Override
80+
public Set<Integer> call(Set<Integer> pressedKeys, KeyEvent event) {
81+
Set<Integer> afterEvent = new HashSet<Integer>(pressedKeys);
82+
switch (event.getID()) {
83+
case KeyEvent.KEY_PRESSED:
84+
afterEvent.add(event.getKeyCode());
85+
break;
86+
87+
case KeyEvent.KEY_RELEASED:
88+
afterEvent.remove(event.getKeyCode());
89+
break;
90+
91+
default: // nothing to do
92+
}
93+
return afterEvent;
94+
}
95+
});
96+
}
97+
5798
public static class UnitTest {
99+
private JComponent comp = new JPanel();
100+
58101
@Test
59-
public void testObservingActionEvents() {
102+
public void testObservingKeyEvents() {
60103
@SuppressWarnings("unchecked")
61-
Action1<ActionEvent> action = mock(Action1.class);
104+
Action1<KeyEvent> action = mock(Action1.class);
62105
@SuppressWarnings("unchecked")
63106
Action1<Exception> error = mock(Action1.class);
64107
Action0 complete = mock(Action0.class);
65108

66109
final KeyEvent event = mock(KeyEvent.class);
67110

68-
JComponent comp = new JPanel();
69-
70111
Subscription sub = fromKeyEventsOf(comp).subscribe(action, error, complete);
71112

72-
verify(action, never()).call(Matchers.<ActionEvent>any());
113+
verify(action, never()).call(Matchers.<KeyEvent>any());
73114
verify(error, never()).call(Matchers.<Exception>any());
74115
verify(complete, never()).call();
75116

76-
fireKeyEvent(comp, event);
77-
verify(action, times(1)).call(Matchers.<ActionEvent>any());
117+
fireKeyEvent(event);
118+
verify(action, times(1)).call(Matchers.<KeyEvent>any());
78119

79-
fireKeyEvent(comp, event);
80-
verify(action, times(2)).call(Matchers.<ActionEvent>any());
120+
fireKeyEvent(event);
121+
verify(action, times(2)).call(Matchers.<KeyEvent>any());
81122

82123
sub.unsubscribe();
83-
fireKeyEvent(comp, event);
84-
verify(action, times(2)).call(Matchers.<ActionEvent>any());
124+
fireKeyEvent(event);
125+
verify(action, times(2)).call(Matchers.<KeyEvent>any());
85126
verify(error, never()).call(Matchers.<Exception>any());
86127
verify(complete, never()).call();
87128
}
88129

89-
private static void fireKeyEvent(JComponent component, KeyEvent event) {
90-
for (KeyListener listener: component.getKeyListeners()) {
130+
@Test
131+
public void testObservingPressedKeys() {
132+
@SuppressWarnings("unchecked")
133+
Action1<Set<Integer>> action = mock(Action1.class);
134+
@SuppressWarnings("unchecked")
135+
Action1<Exception> error = mock(Action1.class);
136+
Action0 complete = mock(Action0.class);
137+
138+
Subscription sub = currentlyPressedKeysOf(comp).subscribe(action, error, complete);
139+
140+
InOrder inOrder = inOrder(action);
141+
inOrder.verify(action, times(1)).call(Collections.<Integer>emptySet());
142+
verify(error, never()).call(Matchers.<Exception>any());
143+
verify(complete, never()).call();
144+
145+
fireKeyEvent(keyEvent(1, KeyEvent.KEY_PRESSED));
146+
inOrder.verify(action, times(1)).call(new HashSet<Integer>(asList(1)));
147+
verify(error, never()).call(Matchers.<Exception>any());
148+
verify(complete, never()).call();
149+
150+
fireKeyEvent(keyEvent(2, KeyEvent.KEY_PRESSED));
151+
inOrder.verify(action, times(1)).call(new HashSet<Integer>(asList(1, 2)));
152+
153+
fireKeyEvent(keyEvent(2, KeyEvent.KEY_RELEASED));
154+
inOrder.verify(action, times(1)).call(new HashSet<Integer>(asList(1)));
155+
156+
fireKeyEvent(keyEvent(3, KeyEvent.KEY_RELEASED));
157+
inOrder.verify(action, times(1)).call(new HashSet<Integer>(asList(1)));
158+
159+
fireKeyEvent(keyEvent(1, KeyEvent.KEY_RELEASED));
160+
inOrder.verify(action, times(1)).call(Collections.<Integer>emptySet());
161+
162+
sub.unsubscribe();
163+
164+
fireKeyEvent(keyEvent(1, KeyEvent.KEY_PRESSED));
165+
inOrder.verify(action, never()).call(Matchers.<Set<Integer>>any());
166+
verify(error, never()).call(Matchers.<Exception>any());
167+
verify(complete, never()).call();
168+
}
169+
170+
private KeyEvent keyEvent(int keyCode, int id) {
171+
return new KeyEvent(comp, id, -1L, 0, keyCode, ' ');
172+
}
173+
174+
private void fireKeyEvent(KeyEvent event) {
175+
for (KeyListener listener: comp.getKeyListeners()) {
91176
listener.keyTyped(event);
92177
}
93178
}

0 commit comments

Comments
 (0)