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">
+
+