Skip to content

Commit 1c47603

Browse files
committed
Added source files
1 parent 008d4c4 commit 1c47603

File tree

2 files changed

+359
-0
lines changed

2 files changed

+359
-0
lines changed

src/VirtualKeyboard.java

Lines changed: 292 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,292 @@
1+
import javafx.beans.binding.Bindings;
2+
import javafx.beans.binding.StringBinding;
3+
import javafx.beans.property.BooleanProperty;
4+
import javafx.beans.property.ReadOnlyBooleanProperty;
5+
import javafx.beans.property.ReadOnlyObjectProperty;
6+
import javafx.beans.property.SimpleBooleanProperty;
7+
import javafx.beans.property.SimpleStringProperty;
8+
import javafx.beans.property.StringProperty;
9+
import javafx.beans.value.ObservableStringValue;
10+
import javafx.event.ActionEvent;
11+
import javafx.event.EventHandler;
12+
import javafx.event.EventTarget;
13+
import javafx.event.EventType;
14+
import javafx.geometry.Insets;
15+
import javafx.geometry.Pos;
16+
import javafx.scene.Node;
17+
import javafx.scene.control.Button;
18+
import javafx.scene.control.ContentDisplay;
19+
import javafx.scene.control.ToggleButton;
20+
import javafx.scene.input.KeyCode;
21+
import javafx.scene.input.KeyEvent;
22+
import javafx.scene.layout.HBox;
23+
import javafx.scene.layout.Priority;
24+
import javafx.scene.layout.VBox;
25+
import javafx.scene.paint.Color;
26+
import javafx.scene.shape.PolygonBuilder;
27+
28+
public class VirtualKeyboard {
29+
private final VBox root ;
30+
31+
/**
32+
* Creates a Virtual Keyboard.
33+
* @param target The node that will receive KeyEvents from this keyboard.
34+
*/
35+
public VirtualKeyboard(ReadOnlyObjectProperty<Node> target) {
36+
this.root = new VBox(5);
37+
root.setPadding(new Insets(10));
38+
root.getStyleClass().add("virtual-keyboard");
39+
40+
final Modifiers modifiers = new Modifiers();
41+
42+
// Data for regular buttons; split into rows
43+
final String[][] unshifted = new String[][] {
44+
{ "`", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=" },
45+
{ "q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "\\" },
46+
{ "a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'" },
47+
{ "z", "x", "c", "v", "b", "n", "m", ",", ".", "/" } };
48+
49+
final String[][] shifted = new String[][] {
50+
{ "~", "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+" },
51+
{ "Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "|" },
52+
{ "A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"" },
53+
{ "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?" } };
54+
55+
final KeyCode[][] codes = new KeyCode[][] {
56+
{ KeyCode.BACK_QUOTE, KeyCode.DIGIT1, KeyCode.DIGIT2, KeyCode.DIGIT3,
57+
KeyCode.DIGIT4, KeyCode.DIGIT5, KeyCode.DIGIT6, KeyCode.DIGIT7,
58+
KeyCode.DIGIT8, KeyCode.DIGIT9, KeyCode.DIGIT0, KeyCode.SUBTRACT,
59+
KeyCode.EQUALS },
60+
{ KeyCode.Q, KeyCode.W, KeyCode.E, KeyCode.R, KeyCode.T, KeyCode.Y,
61+
KeyCode.U, KeyCode.I, KeyCode.O, KeyCode.P, KeyCode.OPEN_BRACKET,
62+
KeyCode.CLOSE_BRACKET, KeyCode.BACK_SLASH },
63+
{ KeyCode.A, KeyCode.S, KeyCode.D, KeyCode.F, KeyCode.G, KeyCode.H,
64+
KeyCode.J, KeyCode.K, KeyCode.L, KeyCode.SEMICOLON, KeyCode.QUOTE },
65+
{ KeyCode.Z, KeyCode.X, KeyCode.C, KeyCode.V, KeyCode.B, KeyCode.N,
66+
KeyCode.M, KeyCode.COMMA, KeyCode.PERIOD, KeyCode.SLASH } };
67+
68+
// non-regular buttons (don't respond to Shift)
69+
final Button escape = createButtonWithFixedText("Esc", KeyCode.ESCAPE, modifiers, target);
70+
final Button backspace = createButtonWithFixedText("Backspace", KeyCode.BACK_SPACE, modifiers, target);
71+
final Button delete = createButtonWithFixedText("Del", KeyCode.DELETE, modifiers, target);
72+
final Button enter = createButtonWithFixedText("Enter", KeyCode.ENTER, modifiers, target);
73+
final Button tab = createButtonWithFixedText("Tab", KeyCode.TAB, modifiers, target);
74+
75+
// Cursor keys, with graphic instead of text
76+
final Button cursorLeft = createButtonWithFixedText("", KeyCode.LEFT, modifiers, target);
77+
cursorLeft.setGraphic(PolygonBuilder.create()
78+
.points(15.0, 5.0, 15.0, 15.0, 5.0, 10.0).fill(Color.rgb(64, 64, 64))
79+
.build());
80+
cursorLeft.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
81+
82+
final Button cursorRight = createButtonWithFixedText("", KeyCode.RIGHT, modifiers, target);
83+
cursorRight.setGraphic(PolygonBuilder.create()
84+
.points(5.0, 5.0, 5.0, 15.0, 15.0, 10.0).fill(Color.rgb(64, 64, 64))
85+
.build());
86+
cursorRight.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
87+
88+
final Button cursorUp = createButtonWithFixedText("", KeyCode.UP, modifiers, target);
89+
cursorUp.setGraphic(PolygonBuilder.create()
90+
.points(10.0, 0.0, 15.0, 5.0, 5.0, 5.0).fill(Color.rgb(64, 64, 64))
91+
.build());
92+
cursorUp.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
93+
94+
final Button cursorDown = createButtonWithFixedText("", KeyCode.DOWN, modifiers, target);
95+
cursorDown.setGraphic(PolygonBuilder.create()
96+
.points(10.0, 5.0, 15.0, 0.0, 5.0, 0.0).fill(Color.rgb(64, 64, 64))
97+
.build());
98+
cursorDown.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
99+
100+
final VBox cursorUpDown = new VBox(2);
101+
cursorUpDown.getChildren().addAll(cursorUp, cursorDown);
102+
103+
// "Extras" to go at the left or right end of each row of buttons.
104+
final Node[][] extraLeftButtons = new Node[][] { {escape}, {tab}, {modifiers.capsLockKey()}, {modifiers.shiftKey()} };
105+
final Node[][] extraRightButtons = new Node[][] { {backspace}, {delete}, {enter}, {modifiers.secondShiftKey()} };
106+
107+
// build layout
108+
for (int row = 0; row < unshifted.length; row++) {
109+
HBox hbox = new HBox(5);
110+
hbox.setAlignment(Pos.CENTER);
111+
root.getChildren().add(hbox);
112+
113+
if (extraLeftButtons[row] != null) {
114+
hbox.getChildren().addAll(extraLeftButtons[row]);
115+
}
116+
117+
for (int k = 0; k < unshifted[row].length; k++) {
118+
hbox.getChildren().add( createButton(unshifted[row][k], shifted[row][k], codes[row][k], modifiers, target));
119+
}
120+
121+
if (extraRightButtons[row] != null) {
122+
hbox.getChildren().addAll(extraRightButtons[row]);
123+
}
124+
125+
}
126+
127+
final Button spaceBar = createButton(" ", " ", KeyCode.SPACE, modifiers, target);
128+
spaceBar.setMaxWidth(Double.POSITIVE_INFINITY);
129+
HBox.setHgrow(spaceBar, Priority.ALWAYS);
130+
131+
final HBox bottomRow = new HBox(5);
132+
bottomRow.setAlignment(Pos.CENTER);
133+
bottomRow.getChildren().addAll(modifiers.ctrlKey(), modifiers.altKey(),
134+
modifiers.metaKey(), spaceBar, cursorLeft, cursorUpDown, cursorRight);
135+
root.getChildren().add(bottomRow);
136+
}
137+
138+
/**
139+
* Visual component displaying this keyboard
140+
* @return A view of the keyboard
141+
*/
142+
public Node view() {
143+
return root ;
144+
}
145+
146+
// Creates a "regular" button that has an unshifted and shifted value
147+
private Button createButton(final String unshifted, final String shifted,
148+
final KeyCode code, Modifiers modifiers, final ReadOnlyObjectProperty<Node> target) {
149+
final ReadOnlyBooleanProperty letter = new SimpleBooleanProperty( unshifted.length() == 1 && Character.isLetter(unshifted.charAt(0)));
150+
final StringBinding text =
151+
Bindings.when(modifiers.shiftDown().or(modifiers.capsLockOn().and(letter)))
152+
.then(shifted)
153+
.otherwise(unshifted);
154+
Button button = createButton(text, code, modifiers, target);
155+
button.textProperty().bind(text);
156+
return button;
157+
}
158+
159+
// Creates a button with fixed text not responding to Shift
160+
private Button createButtonWithFixedText(final String text, final KeyCode code, final Modifiers modifiers, final ReadOnlyObjectProperty<Node> target) {
161+
StringProperty textProperty = new SimpleStringProperty(text);
162+
Button button = createButton(textProperty, code, modifiers, target);
163+
button.setText(text);
164+
return button;
165+
}
166+
167+
// Creates a button with mutable text, and registers listener with it
168+
private Button createButton(final ObservableStringValue text, final KeyCode code, final Modifiers modifiers, final ReadOnlyObjectProperty<Node> target) {
169+
final Button button = new Button();
170+
171+
// Important not to grab the focus from the target:
172+
button.setFocusTraversable(false);
173+
174+
// Add a style class for css:
175+
button.getStyleClass().add("virtual-keyboard-button");
176+
177+
button.setOnAction(new EventHandler<ActionEvent>() {
178+
@Override
179+
public void handle(ActionEvent event) {
180+
final Node targetNode = target.get();
181+
if (targetNode != null) {
182+
final String character;
183+
if (text.get().length() == 1) {
184+
character = text.get();
185+
} else {
186+
character = KeyEvent.CHAR_UNDEFINED;
187+
}
188+
final KeyEvent keyPressEvent = createKeyEvent(button, targetNode, KeyEvent.KEY_PRESSED, character, code,
189+
modifiers);
190+
targetNode.fireEvent(keyPressEvent);
191+
final KeyEvent keyReleasedEvent = createKeyEvent(button, targetNode, KeyEvent.KEY_RELEASED, character,
192+
code, modifiers);
193+
targetNode.fireEvent(keyReleasedEvent);
194+
if (character != KeyEvent.CHAR_UNDEFINED) {
195+
final KeyEvent keyTypedEvent = createKeyEvent(button, targetNode, KeyEvent.KEY_TYPED, character, code,
196+
modifiers);
197+
targetNode.fireEvent(keyTypedEvent);
198+
}
199+
modifiers.releaseKeys();
200+
}
201+
}
202+
});
203+
return button;
204+
}
205+
206+
// Utility method to create a KeyEvent from the Modifiers
207+
private KeyEvent createKeyEvent(Object source, EventTarget target,
208+
EventType<KeyEvent> eventType, String character, KeyCode code,
209+
Modifiers modifiers) {
210+
return new KeyEvent(source, target, eventType, character, code.toString(),
211+
code, modifiers.shiftDown().get(), modifiers.ctrlDown().get(),
212+
modifiers.altDown().get(), modifiers.metaDown().get());
213+
}
214+
215+
// Convenience class to bundle together the modifier keys and their selected state
216+
private static class Modifiers {
217+
private final ToggleButton shift;
218+
private final ToggleButton shift2;
219+
private final ToggleButton ctrl;
220+
private final ToggleButton alt;
221+
private final ToggleButton meta;
222+
private final ToggleButton capsLock;
223+
224+
Modifiers() {
225+
this.shift = createToggle("Shift");
226+
this.shift2 = createToggle("Shift");
227+
this.ctrl = createToggle("Ctrl");
228+
this.alt = createToggle("Alt");
229+
this.meta = createToggle("Meta");
230+
this.capsLock = createToggle("Caps");
231+
232+
shift2.selectedProperty().bindBidirectional(shift.selectedProperty());
233+
}
234+
235+
private ToggleButton createToggle(final String text) {
236+
final ToggleButton tb = new ToggleButton(text);
237+
tb.setFocusTraversable(false);
238+
return tb;
239+
}
240+
241+
public ToggleButton shiftKey() {
242+
return shift;
243+
}
244+
245+
public ToggleButton secondShiftKey() {
246+
return shift2;
247+
}
248+
249+
public ToggleButton ctrlKey() {
250+
return ctrl;
251+
}
252+
253+
public ToggleButton altKey() {
254+
return alt;
255+
}
256+
257+
public ToggleButton metaKey() {
258+
return meta;
259+
}
260+
261+
public ToggleButton capsLockKey() {
262+
return capsLock;
263+
}
264+
265+
public BooleanProperty shiftDown() {
266+
return shift.selectedProperty();
267+
}
268+
269+
public BooleanProperty ctrlDown() {
270+
return ctrl.selectedProperty();
271+
}
272+
273+
public BooleanProperty altDown() {
274+
return alt.selectedProperty();
275+
}
276+
277+
public BooleanProperty metaDown() {
278+
return meta.selectedProperty();
279+
}
280+
281+
public BooleanProperty capsLockOn() {
282+
return capsLock.selectedProperty();
283+
}
284+
285+
public void releaseKeys() {
286+
shift.setSelected(false);
287+
ctrl.setSelected(false);
288+
alt.setSelected(false);
289+
meta.setSelected(false);
290+
}
291+
}
292+
}

src/VirtualKeyboardExample.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import javafx.application.Application;
2+
import javafx.event.ActionEvent;
3+
import javafx.event.EventHandler;
4+
import javafx.geometry.Insets;
5+
import javafx.scene.Scene;
6+
import javafx.scene.control.Button;
7+
import javafx.scene.control.TextArea;
8+
import javafx.scene.control.TextField;
9+
import javafx.scene.layout.HBox;
10+
import javafx.scene.layout.VBox;
11+
import javafx.stage.Stage;
12+
13+
public class VirtualKeyboardExample extends Application {
14+
15+
@Override
16+
public void start(Stage primaryStage) {
17+
18+
final TextField textField = new TextField();
19+
textField.setOnAction(new EventHandler<ActionEvent>() {
20+
@Override
21+
public void handle(ActionEvent event) {
22+
System.out.println("text field: "+textField.getText());
23+
}
24+
});
25+
final TextArea textArea = new TextArea();
26+
27+
final Button okButton = new Button("OK");
28+
okButton.setDefaultButton(true);
29+
okButton.setOnAction(new EventHandler<ActionEvent>() {
30+
@Override
31+
public void handle(ActionEvent event) {
32+
System.out.println("OK Button Pressed!");
33+
}
34+
});
35+
36+
final Button cancelButton = new Button("Cancel");
37+
cancelButton.setCancelButton(true);
38+
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
39+
@Override
40+
public void handle(ActionEvent event) {
41+
System.out.println("Canceled!");
42+
}
43+
});
44+
45+
final HBox buttons = new HBox(5);
46+
buttons.getChildren().addAll(okButton, cancelButton);
47+
48+
final VBox root = new VBox(5);
49+
root.setPadding(new Insets(10));
50+
Scene scene = new Scene(root);
51+
52+
VirtualKeyboard vkb = new VirtualKeyboard(scene.focusOwnerProperty());
53+
54+
// just add a border to easily visualize the boundary of the keyboard:
55+
vkb.view().setStyle("-fx-border-color: darkblue; -fx-border-radius: 5;");
56+
57+
root.getChildren().addAll(textField, textArea, buttons, vkb.view());
58+
59+
primaryStage.setScene(scene);
60+
primaryStage.show();
61+
}
62+
63+
public static void main(String[] args) {
64+
launch(args);
65+
}
66+
67+
}

0 commit comments

Comments
 (0)