2 * Sixth 3D engine. Copyright ©2012-2018, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 3 of the GNU Lesser General Public License
6 * or later as published by the Free Software Foundation.
10 package eu.svjatoslav.sixth.e3d.gui.humaninput;
12 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
13 import eu.svjatoslav.sixth.e3d.gui.Avatar;
14 import eu.svjatoslav.sixth.e3d.gui.ViewPanel;
15 import eu.svjatoslav.sixth.e3d.gui.ViewRenderListener;
18 import java.awt.event.*;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
24 public class HIDInputTracker implements
25 MouseMotionListener, KeyListener, MouseListener, MouseWheelListener, ViewRenderListener {
29 * Key is keyboard key code.
30 * Value is system milliseconds when key was pressed.
32 * So by reading the map one can determine currently pressed keys as well as duration.
35 private final Map<Integer, Long> pressedKeysToPressedTimeMap = new HashMap<>();
36 private final List<MouseEvent> detectedMouseEvents = new ArrayList<>();
37 private final List<KeyEvent> detectedKeyEvents = new ArrayList<>();
38 private int wheelMovedDirection = 0;
39 private Point2D mouseDraggedDirection = new Point2D();
40 private Point2D oldMouseCoordinatesWhenDragging;
41 private ViewPanel viewPanel;
42 private Point2D currentMouseLocation;
43 private boolean mouseMoved;
44 private boolean mouseWithinWindow = false;
46 public HIDInputTracker(final ViewPanel viewPanel) {
47 this.viewPanel = viewPanel;
55 public boolean beforeRender(final ViewPanel viewPanel, final int millisecondsSinceLastFrame) {
56 boolean viewUpdateNeeded = handleKeyboardEvents();
57 viewUpdateNeeded |= handleMouseClicksAndHover(viewPanel);
58 viewUpdateNeeded |= handleMouseDragging();
59 viewUpdateNeeded |= handleMouseVerticalScrolling();
60 return viewUpdateNeeded;
63 private void bind(final JPanel panel) {
64 panel.addMouseMotionListener(this);
66 panel.addKeyListener(this);
68 panel.addMouseListener(this);
70 panel.addMouseWheelListener(this);
74 * @return <code>true</code> if view needs to be repainted.
76 private boolean handleKeyboardEvents() {
77 final UserInputHandler currentFocusOwner = viewPanel.getKeyboardFocusStack().getCurrentFocusOwner();
78 ArrayList<KeyEvent> unprocessedKeyboardEvents = getUnprocessedKeyboardEvents();
80 return currentFocusOwner != null
81 && forwardKeyboardEventsToFocusOwner(currentFocusOwner, unprocessedKeyboardEvents);
84 private ArrayList<KeyEvent> getUnprocessedKeyboardEvents() {
85 synchronized (detectedKeyEvents) {
86 ArrayList<KeyEvent> result = new ArrayList<>(detectedKeyEvents);
87 detectedKeyEvents.clear();
93 * @return <code>true</code> if view update is needed.
95 private boolean forwardKeyboardEventsToFocusOwner(
96 UserInputHandler currentFocusOwner, ArrayList<KeyEvent> keyEvents) {
97 boolean viewUpdateNeeded = false;
99 for (KeyEvent keyEvent : keyEvents)
100 viewUpdateNeeded |= processKeyEvent(currentFocusOwner, keyEvent);
102 return viewUpdateNeeded;
105 private boolean processKeyEvent(UserInputHandler currentFocusOwner, KeyEvent keyEvent) {
106 switch (keyEvent.getID()) {
107 case KeyEvent.KEY_PRESSED:
108 return currentFocusOwner.keyPressed(keyEvent, viewPanel);
110 case KeyEvent.KEY_RELEASED:
111 return currentFocusOwner.keyReleased(keyEvent, viewPanel);
117 * @return <code>true</code> if view needs to be repainted.
119 private synchronized boolean handleMouseClicksAndHover(final ViewPanel viewPanel) {
120 MouseEvent mouseEventAndLocationToTrace = getMouseEventAndLocationToTrace(viewPanel);
121 if (mouseEventAndLocationToTrace != null)
123 viewPanel.getRenderingContext().mouseEvent = mouseEventAndLocationToTrace;
129 private MouseEvent getMouseEventAndLocationToTrace(ViewPanel viewPanel) {
130 MouseEvent unprocessedMouseEvent = findClickLocationToTrace();
131 if (unprocessedMouseEvent != null) {
132 return unprocessedMouseEvent;
134 return getHoverLocationToTrace(viewPanel);
137 private MouseEvent findClickLocationToTrace() {
138 synchronized (detectedMouseEvents) {
139 if (detectedMouseEvents.isEmpty())
142 return detectedMouseEvents.remove(0);
146 private MouseEvent getHoverLocationToTrace(ViewPanel viewPanel) {
149 if (currentMouseLocation != null) {
150 return new MouseEvent(currentMouseLocation, 0);
151 // mouse click with button 0 amounts to mouse hovering event
157 boolean isKeyPressed(final int keyCode) {
158 return pressedKeysToPressedTimeMap.containsKey(keyCode);
162 public void keyPressed(final KeyEvent evt) {
163 synchronized (detectedKeyEvents) {
164 pressedKeysToPressedTimeMap.put(evt.getKeyCode(), System.currentTimeMillis());
165 detectedKeyEvents.add(evt);
170 public void keyReleased(final KeyEvent evt) {
171 synchronized (detectedKeyEvents) {
172 pressedKeysToPressedTimeMap.remove(evt.getKeyCode());
173 detectedKeyEvents.add(evt);
178 public void keyTyped(final KeyEvent e) {
182 public void mouseClicked(final java.awt.event.MouseEvent e) {
183 synchronized (detectedMouseEvents) {
184 detectedMouseEvents.add(new MouseEvent(e.getX(), e.getY(), e.getButton()));
189 public void mouseDragged(final java.awt.event.MouseEvent evt) {
190 final Point2D mouseLocation = new Point2D(evt.getX(), evt.getY());
192 if (oldMouseCoordinatesWhenDragging == null) {
193 oldMouseCoordinatesWhenDragging = mouseLocation;
197 mouseDraggedDirection.add(mouseLocation.clone().subtract(oldMouseCoordinatesWhenDragging));
199 oldMouseCoordinatesWhenDragging = mouseLocation;
203 public void mouseEntered(final java.awt.event.MouseEvent e) {
204 mouseWithinWindow = true;
208 public synchronized void mouseExited(final java.awt.event.MouseEvent e) {
209 mouseWithinWindow = false;
210 currentMouseLocation = null;
214 public synchronized void mouseMoved(final java.awt.event.MouseEvent e) {
215 currentMouseLocation = new Point2D(e.getX(), e.getY());
220 public void mousePressed(final java.awt.event.MouseEvent e) {
224 public void mouseReleased(final java.awt.event.MouseEvent evt) {
225 oldMouseCoordinatesWhenDragging = null;
229 public void mouseWheelMoved(final java.awt.event.MouseWheelEvent evt) {
230 wheelMovedDirection += evt.getWheelRotation();
234 * @return <code>true</code> if view needs to be repainted.
236 private boolean handleMouseVerticalScrolling() {
237 final Avatar avatar = viewPanel.getAvatar();
238 final double actualAcceleration = 50 * avatar.avatarAcceleration * (1 + (avatar.getMovementSpeed() / 10));
239 avatar.getMovementVector().y += (wheelMovedDirection * actualAcceleration);
240 avatar.enforceSpeedLimit();
241 boolean repaintNeeded = wheelMovedDirection != 0;
242 wheelMovedDirection = 0;
243 return repaintNeeded;
247 * @return <code>true</code> if view needs to be repainted.
249 private boolean handleMouseDragging() {
250 // TODO: need to detect whether user moved mouse or touch screen
252 final Avatar avatar = viewPanel.getAvatar();
254 avatar.setAngleXZ(avatar.getAngleXZ() - ((float) mouseDraggedDirection.x / 50));
255 avatar.setAngleYZ(avatar.getAngleYZ() - ((float) mouseDraggedDirection.y / 50));
258 // avatar.setAngleXZ(avatar.getAngleXZ() + ((float)
259 // mouseDraggedDirection.x / 50));
260 // avatar.setAngleYZ(avatar.getAngleYZ() + ((float)
261 // mouseDraggedDirection.y / 50));
263 boolean viewUpdateNeeded = !mouseDraggedDirection.isZero();
264 mouseDraggedDirection.zero();
265 return viewUpdateNeeded;