Java tutorial
/** * Copyright 2010 Philippe Beaudoin * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.puzzlebazar.client.ui; import com.google.gwt.event.dom.client.HasMouseDownHandlers; import com.google.gwt.event.dom.client.HasMouseMoveHandlers; import com.google.gwt.event.dom.client.HasMouseOutHandlers; import com.google.gwt.event.dom.client.HasMouseUpHandlers; import com.google.gwt.event.dom.client.MouseDownEvent; import com.google.gwt.event.dom.client.MouseDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; import com.google.gwt.event.dom.client.MouseMoveHandler; import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; import com.google.gwt.event.shared.GwtEvent; import com.google.gwt.event.shared.HandlerManager; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.ui.Widget; import com.google.inject.Inject; import com.puzzlebazar.client.util.EdgeHitInfo; import com.puzzlebazar.client.util.SquareGridConverter; import com.puzzlebazar.client.util.SquareGridConverterImpl; import com.puzzlebazar.client.util.VertexHitInfo; import com.puzzlebazar.shared.util.SquareGridValidator; import com.puzzlebazar.shared.util.SquareGridValidatorImpl; import com.puzzlebazar.shared.util.Vec2i; /** * @author Philippe Beaudoin */ public class SquareGridManipulatorImpl implements MouseDownHandler, MouseUpHandler, MouseMoveHandler, MouseOutHandler, SquareGridManipulator { /** * The manipulator factory implementation. */ public static class FactoryImpl implements SquareGridManipulator.Factory { @Inject FactoryImpl() { } @Override public SquareGridManipulator create(SquareGridLayoutPanel gridPanel, Widget uiWidget) { return new SquareGridManipulatorImpl(new SquareGridValidatorImpl(gridPanel), new SquareGridConverterImpl(gridPanel, uiWidget), gridPanel, uiWidget); } } private final SquareGridValidator squareGridValidator; private final SquareGridConverter squareGridConverter; private final Widget uiWidget; private HandlerManager handlerManager; /** * Indicates which object the mouse is currently over. */ private static final int OVER_NOTHING = -1; private static final int OVER_CELL = 0; private static final int OVER_EDGE = 1; private static final int OVER_VERTEX = 2; private int current = OVER_NOTHING; private boolean currentVertical; private Vec2i currentLoc; private HandlerRegistration mouseDownHandlerRegistration; private HandlerRegistration mouseUpHandlerRegistration; private HandlerRegistration mouseMoveHandlerRegistration; private HandlerRegistration mouseOutHandlerRegistration; private int vertexClickDistance = -1; private int vertexMoveDistance = -1; private int edgeClickDistance = -1; private int edgeMoveDistance = -1; /** * Create a square grid manipulator. This constructor is called * from the {@link SquareGridManipulatorFactoryImpl}. */ private SquareGridManipulatorImpl(SquareGridValidator squareGridValidator, SquareGridConverter squareGridConverter, SquareGridLayoutPanel gridPanel, Widget uiWidget) { this.squareGridValidator = squareGridValidator; this.squareGridConverter = squareGridConverter; this.uiWidget = uiWidget; mouseDownHandlerRegistration = ((HasMouseDownHandlers) uiWidget).addMouseDownHandler(this); mouseUpHandlerRegistration = ((HasMouseUpHandlers) uiWidget).addMouseUpHandler(this); mouseMoveHandlerRegistration = ((HasMouseMoveHandlers) uiWidget).addMouseMoveHandler(this); mouseOutHandlerRegistration = ((HasMouseOutHandlers) uiWidget).addMouseOutHandler(this); } @Override public void unbind() { if (mouseDownHandlerRegistration != null) { mouseDownHandlerRegistration.removeHandler(); } if (mouseUpHandlerRegistration != null) { mouseUpHandlerRegistration.removeHandler(); } if (mouseMoveHandlerRegistration != null) { mouseMoveHandlerRegistration.removeHandler(); } if (mouseOutHandlerRegistration != null) { mouseOutHandlerRegistration.removeHandler(); } mouseDownHandlerRegistration = null; mouseUpHandlerRegistration = null; mouseMoveHandlerRegistration = null; mouseOutHandlerRegistration = null; } @Override public void onMouseDown(MouseDownEvent event) { event.preventDefault(); // Prevents undesired element selection DOM.setCapture(uiWidget.getElement()); int x = event.getX(); int y = event.getY(); VertexHitInfo vertexHitInfo = vertexHit(x, y, vertexClickDistance); if (vertexHitInfo != null) { fireEvent(new VertexMouseDownEvent(vertexHitInfo.getVertex())); return; } EdgeHitInfo edgeHitInfo = edgeHit(x, y, edgeClickDistance); if (edgeHitInfo != null) { fireEvent(new EdgeMouseDownEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge())); return; } Vec2i cell = cellHit(x, y); if (cell != null) { fireEvent(new CellMouseDownEvent(cell)); } } @Override public void onMouseUp(MouseUpEvent event) { event.preventDefault(); // Prevents undesired element selection DOM.releaseCapture(uiWidget.getElement()); int x = event.getX(); int y = event.getY(); VertexHitInfo vertexHitInfo = vertexHit(x, y, vertexClickDistance); if (vertexHitInfo != null) { fireEvent(new VertexMouseUpEvent(vertexHitInfo.getVertex())); return; } EdgeHitInfo edgeHitInfo = edgeHit(x, y, edgeClickDistance); if (edgeHitInfo != null) { fireEvent(new EdgeMouseUpEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge())); return; } Vec2i cell = cellHit(x, y); if (cell != null) { fireEvent(new CellMouseUpEvent(cell)); } } @Override public void onMouseMove(MouseMoveEvent event) { event.preventDefault(); // Prevents undesired element selection mouseMovedTo(event.getX(), event.getY()); } @Override public void onMouseOut(MouseOutEvent event) { event.preventDefault(); // Prevents undesired element selection mouseMovedTo(event.getX(), event.getY()); } /** * Call whenever the mouse move, wether because of a * {@link MouseMoveEvent} or a {@link MouseOutEvent}. * * @param x The x pixel coordinate within {@link #uiWidget}. * @param y The y pixel coordinate within {@link #uiWidget}. */ private void mouseMovedTo(int x, int y) { VertexHitInfo vertexHitInfo = vertexHit(x, y, vertexMoveDistance); if (vertexHitInfo != null) { if (current != OVER_VERTEX || !currentLoc.equals(vertexHitInfo.getVertex())) { fireOutEvent(); current = OVER_VERTEX; currentLoc = vertexHitInfo.getVertex(); fireEvent(new VertexMouseOverEvent(vertexHitInfo.getVertex())); } fireEvent(new VertexMouseMoveEvent(vertexHitInfo.getVertex())); return; } EdgeHitInfo edgeHitInfo = edgeHit(x, y, edgeMoveDistance); if (edgeHitInfo != null) { if (current != OVER_EDGE || currentVertical != edgeHitInfo.isVertical() || !currentLoc.equals(edgeHitInfo.getEdge())) { fireOutEvent(); current = OVER_EDGE; currentVertical = edgeHitInfo.isVertical(); currentLoc = edgeHitInfo.getEdge(); fireEvent(new EdgeMouseOverEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge())); } fireEvent(new EdgeMouseMoveEvent(edgeHitInfo.isVertical(), edgeHitInfo.getEdge())); return; } Vec2i cell = cellHit(x, y); if (cell != null) { if (current != OVER_CELL || !currentLoc.equals(cell)) { fireOutEvent(); current = OVER_CELL; currentLoc = cell; fireEvent(new CellMouseOverEvent(cell)); } fireEvent(new CellMouseMoveEvent(cell)); return; } fireOutEvent(); current = OVER_NOTHING; currentLoc = null; } /** * Fires the event indicating the the currently over * element has been left. */ private void fireOutEvent() { if (current == OVER_NOTHING) { return; } else if (current == OVER_CELL) { fireEvent(new CellMouseOutEvent(currentLoc)); } else if (current == OVER_EDGE) { fireEvent(new EdgeMouseOutEvent(currentVertical, currentLoc)); } else if (current == OVER_VERTEX) { fireEvent(new VertexMouseOutEvent(currentLoc)); } } @Override public void setVertexClickDistance(int vertexClickDistance) { this.vertexClickDistance = vertexClickDistance; } @Override public void setVertexMoveDistance(int vertexMoveDistance) { this.vertexMoveDistance = vertexMoveDistance; } @Override public void setVertexDistance(int vertexDistance) { setVertexClickDistance(vertexDistance); setVertexMoveDistance(vertexDistance); } @Override public void setEdgeClickDistance(int edgeClickDistance) { this.edgeClickDistance = edgeClickDistance; } @Override public void setEdgeMoveDistance(int edgeMoveDistance) { this.edgeMoveDistance = edgeMoveDistance; } @Override public void setEdgeDistance(int edgeDistance) { setEdgeClickDistance(edgeDistance); setEdgeMoveDistance(edgeDistance); } @Override public HandlerRegistration addCellMouseDownHandler(CellMouseDownHandler handler) { return ensureHandlers().addHandler(CellMouseDownEvent.getType(), handler); } @Override public HandlerRegistration addCellMouseUpHandler(CellMouseUpHandler handler) { return ensureHandlers().addHandler(CellMouseUpEvent.getType(), handler); } @Override public HandlerRegistration addCellMouseOverHandler(CellMouseOverHandler handler) { return ensureHandlers().addHandler(CellMouseOverEvent.getType(), handler); } @Override public HandlerRegistration addCellMouseOutHandler(CellMouseOutHandler handler) { return ensureHandlers().addHandler(CellMouseOutEvent.getType(), handler); } @Override public HandlerRegistration addCellMouseMoveHandler(CellMouseMoveHandler handler) { return ensureHandlers().addHandler(CellMouseMoveEvent.getType(), handler); } @Override public HandlerRegistration addEdgeMouseDownHandler(EdgeMouseDownHandler handler) { return ensureHandlers().addHandler(EdgeMouseDownEvent.getType(), handler); } @Override public HandlerRegistration addEdgeMouseUpHandler(EdgeMouseUpHandler handler) { return ensureHandlers().addHandler(EdgeMouseUpEvent.getType(), handler); } @Override public HandlerRegistration addEdgeMouseOverHandler(EdgeMouseOverHandler handler) { return ensureHandlers().addHandler(EdgeMouseOverEvent.getType(), handler); } @Override public HandlerRegistration addEdgeMouseOutHandler(EdgeMouseOutHandler handler) { return ensureHandlers().addHandler(EdgeMouseOutEvent.getType(), handler); } @Override public HandlerRegistration addEdgeMouseMoveHandler(EdgeMouseMoveHandler handler) { return ensureHandlers().addHandler(EdgeMouseMoveEvent.getType(), handler); } @Override public HandlerRegistration addVertexMouseDownHandler(VertexMouseDownHandler handler) { return ensureHandlers().addHandler(VertexMouseDownEvent.getType(), handler); } @Override public HandlerRegistration addVertexMouseUpHandler(VertexMouseUpHandler handler) { return ensureHandlers().addHandler(VertexMouseUpEvent.getType(), handler); } @Override public HandlerRegistration addVertexMouseOverHandler(VertexMouseOverHandler handler) { return ensureHandlers().addHandler(VertexMouseOverEvent.getType(), handler); } @Override public HandlerRegistration addVertexMouseOutHandler(VertexMouseOutHandler handler) { return ensureHandlers().addHandler(VertexMouseOutEvent.getType(), handler); } @Override public HandlerRegistration addVertexMouseMoveHandler(VertexMouseMoveHandler handler) { return ensureHandlers().addHandler(VertexMouseMoveEvent.getType(), handler); } /** * Fires an event that could be handled by the handlers registered * towards this object. * * @param event The {@link GwtEvent} to fire. */ private void fireEvent(GwtEvent<?> event) { if (handlerManager != null) { handlerManager.fireEvent(event); } } /** * Ensures the existence of the handler manager. * * @return the handler manager * */ HandlerManager ensureHandlers() { return handlerManager == null ? handlerManager = new HandlerManager(this) : handlerManager; } /** * Checks if the passed coordinate hits a vertex within the specified * threshold distance. * * @param x The x coordinate to test, in pixels. * @param y The y coordinate to test, in pixels. * @param distance The threshold distance. * @return The {@link VertexHitInfo} of the hit vertex, or {@code null} if no vertex is hit. */ private VertexHitInfo vertexHit(int x, int y, int distance) { if (distance < 0 || squareGridConverter == null || squareGridValidator == null) { return null; } VertexHitInfo vertexHitInfo = squareGridConverter.pixelToVertex(x, y); if (vertexHitInfo != null && squareGridValidator.isValidVertex(vertexHitInfo.getVertex()) && vertexHitInfo.getDist().max() <= distance) { return vertexHitInfo; } return null; } /** * Checks if the passed coordinate hits an edge within the specified * threshold distance. * * @param x The x coordinate to test, in pixels. * @param y The y coordinate to test, in pixels. * @param distance The threshold distance. * @return The {@link EdgeHitInfo} of the hit edge, or {@code null} if no edge is hit. */ private EdgeHitInfo edgeHit(int x, int y, int distance) { if (distance < 0 || squareGridConverter == null || squareGridValidator == null) { return null; } EdgeHitInfo edgeHitInfo = squareGridConverter.pixelToEdge(x, y); if (edgeHitInfo != null && edgeHitInfo.isVertical() && squareGridValidator.isValidVerticalEdge(edgeHitInfo.getEdge()) && edgeHitInfo.getDist() <= distance) { return edgeHitInfo; } if (edgeHitInfo != null && !edgeHitInfo.isVertical() && squareGridValidator.isValidHorizontalEdge(edgeHitInfo.getEdge()) && edgeHitInfo.getDist() <= 4) { return edgeHitInfo; } return null; } /** * Checks if the passed coordinate hits a cell. * * @param x The x coordinate to test, in pixels. * @param y The y coordinate to test, in pixels. * @return The {@link Vec2i} containing the coordinates of the hit cell, or {@code null} if no cell is hit. */ private Vec2i cellHit(int x, int y) { if (squareGridConverter == null || squareGridValidator == null) { return null; } Vec2i cell = squareGridConverter.pixelToCell(x, y); if (squareGridValidator.isValidCell(cell)) { return cell; } return null; } }