Fixed mouse hovering detection
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Wed, 26 Dec 2018 21:22:44 +0000 (23:22 +0200)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Wed, 26 Dec 2018 21:22:44 +0000 (23:22 +0200)
sixth-3d.iml
src/main/java/eu/svjatoslav/sixth/e3d/geometry/Point2D.java
src/main/java/eu/svjatoslav/sixth/e3d/gui/RenderingContext.java
src/main/java/eu/svjatoslav/sixth/e3d/gui/ViewPanel.java
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/HIDInputTracker.java
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseEvent.java
src/main/java/eu/svjatoslav/sixth/e3d/gui/humaninput/MouseInteractionController.java
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/solidpolygon/SolidPolygon.java
src/main/java/eu/svjatoslav/sixth/e3d/renderer/raster/shapes/basic/texturedpolygon/TexturedPolygon.java

index 83369a9..2d2089f 100644 (file)
@@ -13,7 +13,7 @@
             <entry key="com.zeroturnaround.jrebel.remoting.DeleteUnindexedFiles" value="false" />
             <entry key="com.zeroturnaround.jrebel.remoting.ModuleRemoteServerSelection" value="off" />
             <entry key="jrebelEnabled" value="true" />
-            <entry key="lastExternalPluginCheckTime" value="1531210862024" />
+            <entry key="lastExternalPluginCheckTime" value="1545854124608" />
           </map>
         </option>
         <option name="masterModuleName" />
index d650a9e..71ad564 100755 (executable)
@@ -99,4 +99,11 @@ public class Point2D implements Cloneable {
         return this;
     }
 
+    @Override
+    public String toString() {
+        return "Point2D{" +
+                "x=" + x +
+                ", y=" + y +
+                '}';
+    }
 }
index 2603c11..31cbb13 100644 (file)
@@ -31,14 +31,41 @@ public class RenderingContext {
     public int frameNumber = 0;
 
     /**
-     * Mouse click. During rendering we can detect which item user clicked on.
+     * UI component that mouse is currently hovering over.
      */
-    public MouseEvent mouseEvent;
+    private MouseInteractionController currentMouseOverComponent;
+
+    public void prepareForNewFrameRendering(){
+        mouseEvent = null;
+        objectUnderMouse = null;
+    }
+
+    /**
+     * Mouse click event that needs to be processed.
+     */
+    private  MouseEvent mouseEvent;
+
+    public void setMouseEvent(MouseEvent mouseEvent) {
+        this.mouseEvent = mouseEvent;
+    }
+
+    public MouseEvent getMouseEvent() {
+        return mouseEvent;
+    }
 
     /**
      * Item that user clicked on.
      */
-    public MouseInteractionController objectUnderMouse;
+    private MouseInteractionController objectUnderMouse;
+
+    /**
+     * Called when given object was detected under mouse cursor, while processing {@link #mouseEvent}.
+     * Because objects are rendered back to front. The last method caller will set the top-most object, if
+     * there are multiple objects under mouse cursor.
+     */
+    public void setObjectUnderMouse(MouseInteractionController objectUnderMouse) {
+        this.objectUnderMouse = objectUnderMouse;
+    }
 
     public RenderingContext(final int width, final int height) {
         this.width = width;
@@ -60,4 +87,31 @@ public class RenderingContext {
         graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
     }
 
+    /**
+     * @return <code>true</code> if view repaint is needed.
+     */
+    public boolean handleDetectedComponentMouseEvents() {
+        if (objectUnderMouse != null) {
+            if (mouseEvent.button == 0) {
+                // mouse over
+                if (currentMouseOverComponent == null) {
+                    currentMouseOverComponent = objectUnderMouse;
+                    return currentMouseOverComponent.mouseEntered();
+                } else if (currentMouseOverComponent != objectUnderMouse) {
+                    boolean viewRepaintNeeded = currentMouseOverComponent.mouseExited();
+                    currentMouseOverComponent = objectUnderMouse;
+                    return viewRepaintNeeded | currentMouseOverComponent.mouseEntered();
+                }
+            } else {
+                // mouse click
+                return objectUnderMouse.mouseClicked();
+            }
+        } else if (currentMouseOverComponent != null) {
+            boolean viewRepaintNeeded = currentMouseOverComponent.mouseExited();
+            currentMouseOverComponent = null;
+            return viewRepaintNeeded;
+        }
+        return false;
+    }
+
 }
index 84d4529..86006b6 100755 (executable)
@@ -11,7 +11,6 @@ package eu.svjatoslav.sixth.e3d.gui;
 
 import eu.svjatoslav.sixth.e3d.gui.humaninput.HIDInputTracker;
 import eu.svjatoslav.sixth.e3d.gui.humaninput.KeyboardFocusStack;
-import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
 import eu.svjatoslav.sixth.e3d.renderer.raster.ShapeCollection;
 
 import javax.swing.*;
@@ -40,10 +39,6 @@ public class ViewPanel extends JPanel implements ComponentListener {
     private Timer canvasUpdateTimer;
     private ViewUpdateTimerTask canvasUpdateTimerTask;
     private RenderingContext renderingContext = null;
-    /**
-     * UI component that mouse is currently hovering over.
-     */
-    private MouseInteractionController currentMouseOverComponent;
     /**
      * Currently target FPS for this view. It can be changed at runtime. Also when nothing
      * changes in the view, then frames are not really repainted.
@@ -54,6 +49,7 @@ public class ViewPanel extends JPanel implements ComponentListener {
      * immediately after frame got updated.
      */
     private boolean viewRepaintNeeded = true;
+
     public ViewPanel() {
         viewRenderListeners.add(avatar);
         viewRenderListeners.add(HIDInputTracker);
@@ -126,28 +122,6 @@ public class ViewPanel extends JPanel implements ComponentListener {
         return renderingContext;
     }
 
-    private void handleDetectedComponentMouseEvents() {
-        if (renderingContext.objectUnderMouse != null) {
-            if (renderingContext.mouseEvent.button == 0) {
-                // mouse over
-                if (currentMouseOverComponent == null) {
-                    currentMouseOverComponent = renderingContext.objectUnderMouse;
-                    viewRepaintNeeded |= currentMouseOverComponent.mouseEntered();
-                } else if (currentMouseOverComponent != renderingContext.objectUnderMouse) {
-                    viewRepaintNeeded |= currentMouseOverComponent.mouseExited();
-                    currentMouseOverComponent = renderingContext.objectUnderMouse;
-                    viewRepaintNeeded |= currentMouseOverComponent.mouseEntered();
-                }
-            } else {
-                // mouse click
-                viewRepaintNeeded |= renderingContext.objectUnderMouse.mouseClicked();
-            }
-        } else if (currentMouseOverComponent != null) {
-            viewRepaintNeeded |= currentMouseOverComponent.mouseExited();
-            currentMouseOverComponent = null;
-        }
-    }
-
     private void initializePanelLayout() {
         setFocusCycleRoot(true);
         setOpaque(true);
@@ -158,7 +132,6 @@ public class ViewPanel extends JPanel implements ComponentListener {
     }
 
     private void renderFrame() {
-
         // paint root geometry collection to the offscreen render buffer
         clearCanvas();
         rootShapeCollection.paint(this, renderingContext);
@@ -236,7 +209,7 @@ public class ViewPanel extends JPanel implements ComponentListener {
         // abort rendering if window size is invalid
         if ((getWidth() > 0) && (getHeight() > 0) && renderFrame) {
             renderFrame();
-            handleDetectedComponentMouseEvents();
+            viewRepaintNeeded = renderingContext.handleDetectedComponentMouseEvents();
         }
 
     }
@@ -256,8 +229,7 @@ public class ViewPanel extends JPanel implements ComponentListener {
             renderingContext = new RenderingContext(panelWidth, panelHeight);
         }
 
-        renderingContext.mouseEvent = null;
-        renderingContext.objectUnderMouse = null;
+        renderingContext.prepareForNewFrameRendering();
     }
 
     private boolean notifyViewRenderListeners(int millisecondsPassedSinceLastUpdate) {
index 5bf7927..303f195 100755 (executable)
@@ -117,21 +117,30 @@ public class HIDInputTracker implements
      * @return <code>true</code> if view needs to be repainted.
      */
     private synchronized boolean handleMouseClicksAndHover(final ViewPanel viewPanel) {
-        MouseEvent mouseEventAndLocationToTrace = getMouseEventAndLocationToTrace(viewPanel);
-        if (mouseEventAndLocationToTrace != null)
-        {
-            viewPanel.getRenderingContext().mouseEvent = mouseEventAndLocationToTrace;
-            return true;
+        boolean rerenderNeeded = false;
+        MouseEvent event = findClickLocationToTrace();
+        if (event != null){
+            // process mouse clicks as a first priority
+            rerenderNeeded = true;
+        } else {
+            // when there are no mouse clicks, process mouse hovering
+
+            if (mouseMoved) {
+                mouseMoved = false;
+                // we would like to re-render frame when user moved mouse, to see what objects mouse is hovering over
+                rerenderNeeded = true;
+            }
+
+            if (currentMouseLocation != null) {
+                // mouse click with button 0 amounts to mouse hovering event
+                event = new MouseEvent(currentMouseLocation, 0);
+            }
         }
-        return false;
-    }
 
-    private MouseEvent getMouseEventAndLocationToTrace(ViewPanel viewPanel) {
-        MouseEvent unprocessedMouseEvent = findClickLocationToTrace();
-        if (unprocessedMouseEvent != null) {
-            return unprocessedMouseEvent;
-        } else
-            return getHoverLocationToTrace(viewPanel);
+        if (viewPanel.getRenderingContext() != null)
+            viewPanel.getRenderingContext().setMouseEvent(event);
+
+        return rerenderNeeded;
     }
 
     private MouseEvent findClickLocationToTrace() {
@@ -143,17 +152,6 @@ public class HIDInputTracker implements
         }
     }
 
-    private MouseEvent getHoverLocationToTrace(ViewPanel viewPanel) {
-        if (mouseMoved) {
-            mouseMoved = false;
-            if (currentMouseLocation != null) {
-                return new MouseEvent(currentMouseLocation, 0);
-                // mouse click with button 0 amounts to mouse hovering event
-            }
-        }
-        return null;
-    }
-
     boolean isKeyPressed(final int keyCode) {
         return pressedKeysToPressedTimeMap.containsKey(keyCode);
     }
index 6589ac7..c3de760 100644 (file)
@@ -21,13 +21,20 @@ public class MouseEvent {
      */
     public int button;
 
-    public MouseEvent(final int x, final int y, final int button) {
+    MouseEvent(final int x, final int y, final int button) {
         this(new Point2D(x, y), button);
     }
 
-    public MouseEvent(final Point2D coordinate, final int button) {
+    MouseEvent(final Point2D coordinate, final int button) {
         this.coordinate = coordinate;
         this.button = button;
     }
 
+    @Override
+    public String toString() {
+        return "MouseEvent{" +
+                "coordinate=" + coordinate +
+                ", button=" + button +
+                '}';
+    }
 }
index 75abbaf..5fd43f4 100644 (file)
@@ -12,20 +12,20 @@ package eu.svjatoslav.sixth.e3d.gui.humaninput;
 public interface MouseInteractionController {
 
     /**
-     * Called when mouse is clicked on component
-     * @return <code>true</code> if view update is needed.
+     * Called when mouse is clicked on component.
+     * @return <code>true</code> if view update is needed as a consequence of this mouse click.
      */
     boolean mouseClicked();
 
     /**
      * Called when mouse gets over given component.
-     * @return <code>true</code> if view update is needed.
+     * @return <code>true</code> if view update is needed as a consequence of this mouse enter.
      */
     boolean mouseEntered();
 
     /**
      * Called when mouse leaves screen area occupied by component.
-     * @return <code>true</code> if view update is needed.
+     * @return <code>true</code> if view update is needed as a consequence of this mouse exit.
      */
     boolean mouseExited();
 
index e45a991..faefb2a 100644 (file)
@@ -11,12 +11,13 @@ package eu.svjatoslav.sixth.e3d.renderer.raster.shapes.basic.solidpolygon;
 
 import eu.svjatoslav.sixth.e3d.geometry.Point2D;
 import eu.svjatoslav.sixth.e3d.geometry.Point3D;
-import eu.svjatoslav.sixth.e3d.geometry.Polygon;
 import eu.svjatoslav.sixth.e3d.gui.RenderingContext;
 import eu.svjatoslav.sixth.e3d.gui.humaninput.MouseInteractionController;
 import eu.svjatoslav.sixth.e3d.renderer.raster.Color;
 import eu.svjatoslav.sixth.e3d.renderer.raster.shapes.AbstractCoordinateShape;
 
+import static eu.svjatoslav.sixth.e3d.geometry.Polygon.pointWithinPolygon;
+
 public class SolidPolygon extends AbstractCoordinateShape {
 
     private final static LineInterpolator polygonBoundary1 = new LineInterpolator();
@@ -103,10 +104,10 @@ public class SolidPolygon extends AbstractCoordinateShape {
         onScreenPoint3.roundToInteger();
 
         if (mouseInteractionController != null)
-            if (context.mouseEvent != null)
-                if (Polygon.pointWithinPolygon(context.mouseEvent.coordinate,
+            if (context.getMouseEvent() != null)
+                if (pointWithinPolygon(context.getMouseEvent().coordinate,
                         onScreenPoint1, onScreenPoint2, onScreenPoint3))
-                    context.objectUnderMouse = mouseInteractionController;
+                    context.setObjectUnderMouse(mouseInteractionController);
 
         if (color.isTransparent())
             return;
index cafcbbf..3539ab1 100644 (file)
@@ -141,11 +141,11 @@ public class TexturedPolygon extends AbstractCoordinateShape {
         projectedPoint3.roundToInteger();
 
         if (mouseInteractionController != null)
-            if (renderBuffer.mouseEvent != null)
+            if (renderBuffer.getMouseEvent() != null)
                 if (pointWithinPolygon(
-                        renderBuffer.mouseEvent.coordinate, projectedPoint1,
+                        renderBuffer.getMouseEvent().coordinate, projectedPoint1,
                         projectedPoint2, projectedPoint3))
-                    renderBuffer.objectUnderMouse = mouseInteractionController;
+                    renderBuffer.setObjectUnderMouse(mouseInteractionController);
 
         // Show polygon boundaries (for debugging)
         if (showBorders)