mirror of
https://github.com/peterosterlund2/droidfish.git
synced 2025-12-08 15:12:40 +01:00
Make it possible to enter moves by dragging
Add new setting (default true) to allow input of moves by dragging a piece from its source square to the target square. This setting partially overlaps with scroll gestures on the chess board, so when this setting is enabled, scroll gestures will only happen if the starting square is empty or contains a piece of the color not to make the next move.
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,6 +318,7 @@ public class DroidChessController {
|
||||
abortSearch();
|
||||
updateComputeThreads();
|
||||
}
|
||||
if (animate)
|
||||
setAnimMove(oldPos, m, true);
|
||||
updateGUI();
|
||||
} else {
|
||||
@@ -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. */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -314,6 +314,8 @@ If you are running on battery power, it is recommended that you change settings
|
||||
<string name="prefs_wakeLock_title">Disable Screen Timeout</string>
|
||||
<string name="prefs_drawSquareLabels_title">Square Labels</string>
|
||||
<string name="prefs_drawSquareLabels_summary">Display square labels: a–h and 1–8</string>
|
||||
<string name="prefs_dragMoveEnabled_title">Drag Pieces</string>
|
||||
<string name="prefs_dragMoveEnabled_summary">Drag a piece to make a move</string>
|
||||
<string name="prefs_scrollSensitivity_title">Scrolling Speed</string>
|
||||
<string name="prefs_scrollSensitivity_summary">Scrolling speed for game navigation</string>
|
||||
<string name="prefs_invertScrollDirection_title">Invert Scroll Direction</string>
|
||||
|
||||
@@ -525,6 +525,12 @@
|
||||
android:entries="@array/squareSelectType_texts"
|
||||
android:defaultValue="@string/squareSelectType_default">
|
||||
</ListPreference>
|
||||
<CheckBoxPreference
|
||||
android:key="dragMoveEnabled"
|
||||
android:title="@string/prefs_dragMoveEnabled_title"
|
||||
android:summary="@string/prefs_dragMoveEnabled_summary"
|
||||
android:defaultValue="true">
|
||||
</CheckBoxPreference>
|
||||
<ListPreference
|
||||
android:key="scrollSensitivity"
|
||||
android:title="@string/prefs_scrollSensitivity_title"
|
||||
|
||||
@@ -34,10 +34,11 @@ that even beginners have a reasonable chance to beat it.
|
||||
left/right half of the app title bar.
|
||||
|
||||
* To play a move on the board first tap the piece to move, then tap the
|
||||
destination square. For pawn promotion moves a context menu is opened that
|
||||
lets the user select the piece to promote to. For castling moves, first tap on
|
||||
the king and then tap on the king destination square. The rook is moved
|
||||
automatically.
|
||||
destination square. Alternatively, touch the piece to move, drag it to the
|
||||
destination square and release the piece. For pawn promotion moves a context
|
||||
menu is opened that lets the user select the piece to promote to. For castling
|
||||
moves, first tap on the king and then tap on the king destination square. The
|
||||
rook is moved automatically.
|
||||
**Note!** Castling is only allowed if the king and rook have not previously
|
||||
been moved. When setting up a position manually (see below), make sure to also
|
||||
set the castling right flags appropriately.
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user