diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlay.java b/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlay.java index 9768803..fc140fc 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlay.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlay.java @@ -57,8 +57,8 @@ public class ChessBoardPlay extends ChessBoard { @Override protected XYCoord pixToSq(int xCrd, int yCrd) { - int x = (xCrd - x0) / sqSize; if (flipped) x = 7 - x; - int y = (yCrd - y0) / sqSize; if (!flipped) y = 7 - y; + int x = (int)Math.floor((xCrd - x0) / (double)sqSize); if (flipped) x = 7 - x; + int y = (int)Math.floor((yCrd - y0) / (double)sqSize); if (!flipped) y = 7 - y; return new XYCoord(x, y); } @@ -94,10 +94,6 @@ public class ChessBoardPlay extends ChessBoard { protected void drawExtraSquares(Canvas canvas) { } - private boolean myColor(int piece) { - return (piece != Piece.EMPTY) && (Piece.isWhite(piece) == pos.whiteMove); - } - public Move mousePressed(int sq) { if (sq < 0) return null; diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlayListener.java b/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlayListener.java index b0954c2..e7f09e8 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlayListener.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/ChessBoardPlayListener.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewConfiguration; import org.petero.droidfish.gamelogic.Move; +import org.petero.droidfish.gamelogic.Piece; public class ChessBoardPlayListener implements View.OnTouchListener { private DroidFish df; @@ -32,6 +33,8 @@ public class ChessBoardPlayListener implements View.OnTouchListener { private boolean pending = false; private boolean pendingClick = false; private int sq0 = -1; + private boolean isValidDragSquare; // True if dragging starting at "sq0" is valid + private int dragSquare = -1; private float scrollX = 0; private float scrollY = 0; private float prevX = 0; @@ -59,6 +62,8 @@ public class ChessBoardPlayListener implements View.OnTouchListener { pending = true; pendingClick = true; sq0 = cb.eventToSquare(event); + isValidDragSquare = cb.isValidDragSquare(sq0); + dragSquare = -1; scrollX = 0; scrollY = 0; prevX = event.getX(); @@ -73,7 +78,7 @@ public class ChessBoardPlayListener implements View.OnTouchListener { } float currX = event.getX(); float currY = event.getY(); - if (onScroll(currX - prevX, currY - prevY)) { + if (onMove(event)) { handler.removeCallbacks(runnable); pendingClick = false; } @@ -85,29 +90,59 @@ public class ChessBoardPlayListener implements View.OnTouchListener { if (pending) { pending = false; handler.removeCallbacks(runnable); - if (!pendingClick) - break; - int sq = cb.eventToSquare(event); - if (sq == sq0) { - if (df.ctrl.humansTurn()) { + if (df.ctrl.humansTurn()) { + int sq = cb.eventToSquare(event); + if (dragSquare != -1) { + if (sq != -1 && sq != sq0) { + cb.setSelection(cb.highlightLastMove ? sq : -1); + cb.userSelectedSquare = false; + Move m = new Move(sq0, sq, Piece.EMPTY); + df.setAutoMode(DroidFish.AutoMode.OFF); + df.ctrl.makeHumanMove(m, false); + } + } else if (pendingClick && (sq == sq0)) { Move m = cb.mousePressed(sq); if (m != null) { df.setAutoMode(DroidFish.AutoMode.OFF); - df.ctrl.makeHumanMove(m); + df.ctrl.makeHumanMove(m, true); } df.setEgtbHints(cb.getSelectedSquare()); } } + cb.setDragState(-1, 0, 0); } break; case MotionEvent.ACTION_CANCEL: pending = false; + cb.setDragState(-1, 0, 0); handler.removeCallbacks(runnable); break; } return true; } + /** Process an ACTION_MOVE event. Return true if a gesture is detected, + * which means that a click will not happen when ACTION_UP is received. */ + private boolean onMove(MotionEvent event) { + if (df.dragMoveEnabled && isValidDragSquare) { + return onDrag(event); + } else { + return onScroll(event.getX() - prevX, event.getY() - prevY); + } + } + + private boolean onDrag(MotionEvent event) { + if (dragSquare == -1) { + int sq = cb.eventToSquare(event); + if (sq != sq0) + dragSquare = sq0; + } + if (dragSquare != -1) + if (!cb.setDragState(dragSquare, (int)event.getX(), (int)event.getY())) + dragSquare = -1; + return false; + } + private boolean onScroll(float distanceX, float distanceY) { if (df.invertScrollDirection) { distanceX = -distanceX; diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java b/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java index de7c4fb..4e167bc 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/DroidFish.java @@ -211,6 +211,7 @@ public class DroidFish extends Activity private SharedPreferences settings; private ObjectCache cache; + boolean dragMoveEnabled; float scrollSensitivity; boolean invertScrollDirection; boolean scrollGames; @@ -1112,6 +1113,7 @@ public class DroidFish extends Activity autoMoveDelay = getIntSetting("autoDelay", 5000); + dragMoveEnabled = settings.getBoolean("dragMoveEnabled", true); scrollSensitivity = Float.parseFloat(settings.getString("scrollSensitivity", "2")); invertScrollDirection = settings.getBoolean("invertScrollDirection", false); scrollGames = settings.getBoolean("scrollGames", false); diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/gamelogic/DroidChessController.java b/DroidFishApp/src/main/java/org/petero/droidfish/gamelogic/DroidChessController.java index 27eae2b..909c93e 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/gamelogic/DroidChessController.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/gamelogic/DroidChessController.java @@ -291,7 +291,7 @@ public class DroidChessController { } /** Make a move for a human player. */ - public final synchronized void makeHumanMove(Move m) { + public final synchronized void makeHumanMove(Move m, boolean animate) { if (!humansTurn()) return; Position oldPos = new Position(game.currPos()); @@ -318,7 +318,8 @@ public class DroidChessController { abortSearch(); updateComputeThreads(); } - setAnimMove(oldPos, m, true); + if (animate) + setAnimMove(oldPos, m, true); updateGUI(); } else { gui.setSelection(-1); @@ -349,7 +350,7 @@ public class DroidChessController { promoteMove.promoteTo = promoteTo; Move m = promoteMove; promoteMove = null; - makeHumanMove(m); + makeHumanMove(m, true); } /** Add a null-move to the game tree. */ diff --git a/DroidFishApp/src/main/java/org/petero/droidfish/view/ChessBoard.java b/DroidFishApp/src/main/java/org/petero/droidfish/view/ChessBoard.java index 4cbb2fe..5bf7c1b 100644 --- a/DroidFishApp/src/main/java/org/petero/droidfish/view/ChessBoard.java +++ b/DroidFishApp/src/main/java/org/petero/droidfish/view/ChessBoard.java @@ -48,7 +48,12 @@ public abstract class ChessBoard extends View { public int selectedSquare; public boolean userSelectedSquare; // True if selectedSquare was set by user tap/click, // false if selectedSquare used to highlight last move - protected int x0, y0; + + private int dragSquare = -1; // Square of currently dragged piece, or -1 when no draggning + private int dragXCrd = -1; // Current drag X pixel coordinate + private int dragYCrd = -1; // Current drag Y pixel coordinate + + protected int x0, y0; // Upper left corner of board in pixel coordinates public int sqSize; public boolean flipped; public boolean drawSquareLabels; @@ -189,6 +194,11 @@ public abstract class ChessBoard extends View { } private AnimInfo anim = new AnimInfo(); + /** Return true if piece has the same color as the side to move. */ + protected boolean myColor(int piece) { + return (piece != Piece.EMPTY) && (Piece.isWhite(piece) == pos.whiteMove); + } + /** * Set up move animation. The animation will start the next time setPosition is called. * @param sourcePos The source position for the animation. @@ -294,10 +304,48 @@ public abstract class ChessBoard extends View { this.pos = new Position(pos); doInvalidate = true; } + if (setDragStateInternal(-1, 0, 0)) + doInvalidate = true; if (doInvalidate) invalidate(); } + /** Set the pixel position (xCrd,yCrd) of the currently dragged piece. + * "sq" is the original square of the piece being dragged, or -1 to + * cancel the current dragging. + * @return true if dragging is active. */ + public final boolean setDragState(int sq, int xCrd, int yCrd) { + if (setDragStateInternal(sq, xCrd, yCrd)) + invalidate(); + return dragSquare != -1; + } + + /** Return true if the piece at "sq" can be dragged. */ + public final boolean isValidDragSquare(int sq) { + return sq != -1 && myColor(pos.getPiece(sq)); + } + + /** Set drag state. Return true if any changes were made. */ + private final boolean setDragStateInternal(int sq, int xCrd, int yCrd) { + boolean modified = false; + if (!isValidDragSquare(sq)) { + if (dragSquare != -1) { + dragSquare = -1; + modified = true; + } + } else { + int newX = xCrd - sqSize / 2; + int newY = yCrd - sqSize / 2; + if (dragSquare != sq || dragXCrd != newX || dragYCrd != newY) { + dragSquare = sq; + dragXCrd = newX; + dragYCrd = newY; + modified = true; + } + } + return modified; + } + /** Set/clear the board flipped status. */ final public void setFlipped(boolean flipped) { if (this.flipped != flipped) { @@ -360,7 +408,9 @@ public abstract class ChessBoard extends View { setMeasuredDimension(width, height); } + /** Compute pixel coordinates (x0,y0) for upper left corner of board. */ protected abstract void computeOrigin(int width, int height); + protected abstract int getXFromSq(int sq); protected abstract int getYFromSq(int sq); @@ -387,7 +437,7 @@ public abstract class ChessBoard extends View { canvas.drawRect(xCrd, yCrd, xCrd+sqSize, yCrd+sqSize, paint); int sq = Position.getSquare(x, y); - if (!animActive || !anim.squareHidden(sq)) { + if (!(animActive && anim.squareHidden(sq) || sq == dragSquare)) { int p = pos.getPiece(sq); drawPiece(canvas, xCrd, yCrd, p); } @@ -417,6 +467,15 @@ public abstract class ChessBoard extends View { } anim.draw(canvas); + + if (dragSquare != -1) { + drawPiece(canvas, dragXCrd, dragYCrd, pos.getPiece(dragSquare)); + float h = (float)(sqSize / 2.0); + float xCrd = dragXCrd + h; + float yCrd = dragYCrd + h; + drawDragMarker(canvas, xCrd, yCrd); + } + // long t1 = System.currentTimeMillis(); // System.out.printf("draw: %d\n", t1-t0); } @@ -514,6 +573,27 @@ public abstract class ChessBoard extends View { canvas.drawText(s, xCrd, yCrd, labelPaint); } + private void drawDragMarker(Canvas canvas, float x0, float y0) { + float L = sqSize * 2.0f; + float d1 = sqSize * 0.03f; + float d2 = sqSize * 0.06f; + Path path = new Path(); + path.moveTo(x0 - L, y0 - d1); + path.lineTo(x0 - d2, y0 - d2); + path.lineTo(x0 - d1, y0 - L ); + path.lineTo(x0 + d1, y0 - L ); + path.lineTo(x0 + d2, y0 - d2); + path.lineTo(x0 + L, y0 - d1); + path.lineTo(x0 + L, y0 + d1); + path.lineTo(x0 + d2, y0 + d2); + path.lineTo(x0 + d1, y0 + L ); + path.lineTo(x0 - d1, y0 + L ); + path.lineTo(x0 - d2, y0 + d2); + path.lineTo(x0 - L, y0 + d1); + path.close(); + canvas.drawPath(path, moveMarkPaint.get(2)); + } + protected static class XYCoord { public int x; public int y; diff --git a/DroidFishApp/src/main/res/values/strings.xml b/DroidFishApp/src/main/res/values/strings.xml index b1b20a9..d50e5dc 100644 --- a/DroidFishApp/src/main/res/values/strings.xml +++ b/DroidFishApp/src/main/res/values/strings.xml @@ -314,6 +314,8 @@ If you are running on battery power, it is recommended that you change settings Disable Screen Timeout Square Labels Display square labels: a–h and 1–8 + Drag Pieces + Drag a piece to make a move Scrolling Speed Scrolling speed for game navigation Invert Scroll Direction diff --git a/DroidFishApp/src/main/res/xml/preferences.xml b/DroidFishApp/src/main/res/xml/preferences.xml index d0e167c..b154930 100644 --- a/DroidFishApp/src/main/res/xml/preferences.xml +++ b/DroidFishApp/src/main/res/xml/preferences.xml @@ -525,6 +525,12 @@ android:entries="@array/squareSelectType_texts" android:defaultValue="@string/squareSelectType_default"> + +