Refactoring.
[sixth-3d.git] / src / main / java / eu / svjatoslav / sixth / e3d / gui / humaninput / UserInputTracker.java
1 /*
2  * Sixth 3D engine. Copyright ©2012-2018, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
3  *
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.
7  *
8  */
9
10 package eu.svjatoslav.sixth.e3d.gui.humaninput;
11
12 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
13 import eu.svjatoslav.sixth.e3d.gui.Avatar;
14 import eu.svjatoslav.sixth.e3d.gui.View;
15 import eu.svjatoslav.sixth.e3d.gui.ViewContext;
16 import eu.svjatoslav.sixth.e3d.gui.ViewUpdateListener;
17
18 import javax.swing.*;
19 import java.awt.event.*;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24
25 public class UserInputTracker
26         implements MouseMotionListener, KeyListener, MouseListener, MouseWheelListener, ViewUpdateListener {
27
28     /**
29      * <pre>
30      * Key is keyboard key code.
31      * Value is system milliseconds when key was pressed.
32      *
33      * So by reading the map one can determine currently pressed keys as well as duration.
34      * </pre>
35      */
36     private final Map<Integer, Long> pressedKeysToPressedTimeMap = new HashMap<>();
37     private final List<MouseClick> detectedMouseClicks = new ArrayList<>();
38     private final List<KeyEvent> detectedKeyEvents = new ArrayList<>();
39     public int wheelMovedDirection = 0;
40     Point2D mouseDraggedDirection = new Point2D();
41     Point2D oldMouseCoordinatesWhenDragging;
42     ViewContext viewContext;
43     Point2D currentMouseLocation;
44     boolean mouseMoved;
45     private boolean mouseWithinWindow = false;
46
47     public UserInputTracker(final ViewContext viewContext) {
48         this.viewContext = viewContext;
49     }
50
51     /**
52      * {@inheritDoc}
53      */
54     @Override
55     public boolean beforeViewUpdate(final ViewContext viewContext, final int millisecondsSinceLastFrame) {
56
57         boolean viewUpdateNeeded = handleDetectedMouseClicks(viewContext.getView());
58
59         viewUpdateNeeded |= handleDetectedKeyEvents();
60
61         viewContext.getKeyboardFocusTracker().getCurrentFocusOwner().beforeViewUpdate(viewContext,
62                 millisecondsSinceLastFrame);
63         viewUpdateNeeded |= trackMouse();
64
65         return viewUpdateNeeded;
66     }
67
68     public void bind(final JPanel panel) {
69         panel.addMouseMotionListener(this);
70
71         panel.addKeyListener(this);
72
73         panel.addMouseListener(this);
74
75         panel.addMouseWheelListener(this);
76     }
77
78     public boolean handleDetectedKeyEvents() {
79         boolean keyEventsHandled = false;
80
81         final UserInputHandler currentFocusOwner = viewContext.getKeyboardFocusTracker().getCurrentFocusOwner();
82
83         synchronized (detectedKeyEvents) {
84             if (currentFocusOwner == null) {
85                 detectedKeyEvents.clear();
86                 return false;
87             }
88
89             while (!detectedKeyEvents.isEmpty()) {
90                 final KeyEvent keyEvent = detectedKeyEvents.remove(0);
91
92                 switch (keyEvent.getID()) {
93                     case KeyEvent.KEY_PRESSED:
94                         currentFocusOwner.keyPressed(keyEvent, viewContext);
95                         keyEventsHandled = true;
96                         break;
97
98                     case KeyEvent.KEY_RELEASED:
99                         currentFocusOwner.keyReleased(keyEvent, viewContext);
100                         keyEventsHandled = true;
101                         break;
102                 }
103             }
104         }
105
106         return keyEventsHandled;
107     }
108
109     /**
110      * Returns <code>true</code> if mouse events are detected and view needs to
111      * be repainted.
112      */
113     public synchronized boolean handleDetectedMouseClicks(final View view) {
114         if (detectedMouseClicks.isEmpty()) {
115
116             if (currentMouseLocation != null)
117                 view.getRenderingContext().mouseClick = new MouseClick(currentMouseLocation, 0);
118
119             if (mouseMoved) {
120                 mouseMoved = false;
121                 return true;
122             } else
123                 return false;
124         }
125
126         view.getRenderingContext().mouseClick = detectedMouseClicks.remove(0);
127
128         return true;
129     }
130
131     public boolean isKeyPressed(final int keyCode) {
132         return pressedKeysToPressedTimeMap.containsKey(keyCode);
133     }
134
135     @Override
136     public void keyPressed(final KeyEvent evt) {
137         synchronized (detectedKeyEvents) {
138             pressedKeysToPressedTimeMap.put(evt.getKeyCode(), System.currentTimeMillis());
139             detectedKeyEvents.add(evt);
140             viewContext.getView().repaintDuringNextViewUpdate();
141         }
142     }
143
144     @Override
145     public void keyReleased(final KeyEvent evt) {
146         synchronized (detectedKeyEvents) {
147             pressedKeysToPressedTimeMap.remove(evt.getKeyCode());
148             detectedKeyEvents.add(evt);
149             viewContext.getView().repaintDuringNextViewUpdate();
150         }
151     }
152
153     @Override
154     public void keyTyped(final KeyEvent e) {
155     }
156
157     @Override
158     public void mouseClicked(final MouseEvent e) {
159         synchronized (detectedMouseClicks) {
160             detectedMouseClicks.add(new MouseClick(e.getX(), e.getY(), e.getButton()));
161         }
162     }
163
164     @Override
165     public void mouseDragged(final java.awt.event.MouseEvent evt) {
166         final Point2D mouseLocation = new Point2D(evt.getX(), evt.getY());
167
168         if (oldMouseCoordinatesWhenDragging == null) {
169             oldMouseCoordinatesWhenDragging = mouseLocation;
170             return;
171         }
172
173         mouseDraggedDirection.add(mouseLocation.clone().subtract(oldMouseCoordinatesWhenDragging));
174
175         oldMouseCoordinatesWhenDragging = mouseLocation;
176     }
177
178     @Override
179     public void mouseEntered(final MouseEvent e) {
180         mouseWithinWindow = true;
181     }
182
183     @Override
184     public synchronized void mouseExited(final MouseEvent e) {
185         mouseWithinWindow = false;
186         currentMouseLocation = null;
187     }
188
189     @Override
190     public synchronized void mouseMoved(final MouseEvent e) {
191         currentMouseLocation = new Point2D(e.getX(), e.getY());
192         mouseMoved = true;
193     }
194
195     @Override
196     public void mousePressed(final MouseEvent e) {
197     }
198
199     @Override
200     public void mouseReleased(final java.awt.event.MouseEvent evt) {
201         oldMouseCoordinatesWhenDragging = null;
202     }
203
204     @Override
205     public void mouseWheelMoved(final java.awt.event.MouseWheelEvent evt) {
206         wheelMovedDirection += evt.getWheelRotation();
207     }
208
209     /**
210      * Interpret mouse movement
211      */
212     public boolean trackMouse() {
213         final Avatar avatar = viewContext.getAvatar();
214
215         // track mouse dragging
216
217         // TODO: need to detect whether user moved mouse or touch screen
218
219         // for mouse
220         avatar.setAngleXZ(avatar.getAngleXZ() - ((float) mouseDraggedDirection.x / 50));
221         avatar.setAngleYZ(avatar.getAngleYZ() - ((float) mouseDraggedDirection.y / 50));
222
223         // for touch screen
224         // avatar.setAngleXZ(avatar.getAngleXZ() + ((float)
225         // mouseDraggedDirection.x / 50));
226         // avatar.setAngleYZ(avatar.getAngleYZ() + ((float)
227         // mouseDraggedDirection.y / 50));
228
229         // track mouse wheel movements
230         final double actualAcceleration = 50 * avatar.avatarAcceleration * (1 + (avatar.getMovementSpeed() / 10));
231
232         avatar.getMovementVector().y += (wheelMovedDirection * actualAcceleration);
233         avatar.enforceSpeedLimit();
234
235         // check if view shall be repainted
236         boolean repaintNeeded;
237         repaintNeeded = (mouseDraggedDirection.x != 0) || (mouseDraggedDirection.y != 0) || (wheelMovedDirection != 0);
238
239         // reset movement counters
240         wheelMovedDirection = 0;
241         mouseDraggedDirection.zero();
242
243         return repaintNeeded;
244     }
245
246 }