Entry-point from GLFW based Window.Event into Field. Defines OnKeyDown and Hold interfaces for Boxes to implement if they want to listen to key presses (and holds and, thus, releases). This operates in exactly the same manner as "Mouse" except there is no analogue of the On
| 16 | * This operates in exactly the same manner as "Mouse" except there is no analogue of the OnMouseMove (no hovering of hands over the keyboard). |
| 17 | */ |
| 18 | public class Keyboard { |
| 19 | |
| 20 | public interface OnKeyDown { |
| 21 | Hold onKeyDown(Window.Event<Window.KeyboardState> e, int key); |
| 22 | } |
| 23 | |
| 24 | public interface OnCharTyped { |
| 25 | void onCharTyped(Window.Event<Window.KeyboardState> e, char key); |
| 26 | } |
| 27 | |
| 28 | public interface Hold { |
| 29 | boolean update(Window.Event<Window.KeyboardState> e, boolean termination); |
| 30 | } |
| 31 | |
| 32 | Map<Integer, Collection<Hold>> ongoingDrags = new HashMap<Integer, Collection<Hold>>(); |
| 33 | |
| 34 | static public final Dict.Prop<Map<String, OnKeyDown>> onKeyDown = new Dict.Prop<>("onKeyDown").type().toCanon().autoConstructs(() -> new IdempotencyMap<>(OnKeyDown.class)); |
| 35 | static public final Dict.Prop<Map<String, OnCharTyped>> onCharTyped = new Dict.Prop<>("onCharTyped").type().toCanon().autoConstructs(() -> new IdempotencyMap<>(OnCharTyped.class)); |
| 36 | |
| 37 | |
| 38 | public void dispatch(Box root, Window.Event<Window.KeyboardState> event) { |
| 39 | |
| 40 | // this is the set of keys that have been newly pressed, but we'd like to respond to key repeats as well |
| 41 | // Set<Integer> pressed = Window.KeyboardState.keysPressed(event.before, event.after); |
| 42 | Set<Integer> pressed = event.after.keysDown; |
| 43 | |
| 44 | Set<Character> typed = Window.KeyboardState.charsPressed(event.before, event.after); |
| 45 | Set<Integer> released = Window.KeyboardState.keysReleased(event.before, event.after); |
| 46 | Set<Integer> down = event.after.keysDown; |
| 47 | |
| 48 | released.stream().map(r -> ongoingDrags.remove(r)).filter(x -> x != null).forEach(drags -> { |
| 49 | drags.stream().forEach(Hold -> Hold.update(event, true)); |
| 50 | drags.clear(); |
| 51 | }); |
| 52 | |
| 53 | for (Collection<Hold> d : ongoingDrags.values()) { |
| 54 | Iterator<Hold> dd = d.iterator(); |
| 55 | while (dd.hasNext()) { |
| 56 | Hold Hold = dd.next(); |
| 57 | boolean cont = false; |
| 58 | try { |
| 59 | cont = Hold.update(event, false); |
| 60 | } catch (Throwable t) { |
| 61 | System.err.println(" Exception thrown in Hold update :" + t); |
| 62 | System.err.println(" Hold will not be called again"); |
| 63 | cont = false; |
| 64 | } |
| 65 | if (!cont) dd.remove(); |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | Box focused = root.find(KeyboardFocus._keyboardFocus, root.both()).map(x -> x.getFocus()).findFirst().orElseGet(() -> Optional.empty()).orElseGet(() -> root); |
| 70 | |
| 71 | Util.Errors errors = new Util.Errors(); |
| 72 | pressed.stream().forEach(p -> { |
| 73 | Collection<Hold> hold = ongoingDrags.computeIfAbsent(p, (x) -> new ArrayList<>()); |
| 74 | |
| 75 | // change map to handle errors on x |
nothing calls this directly
no test coverage detected