Implement scrolling during drag via timer rather than by counting on

getting notified when pen is dragged off the board.  The latter won't
happen if the board is up against the edge of the screen.
This commit is contained in:
ehouse 2008-03-29 20:23:27 +00:00
parent 1172502b04
commit 1e7bc3839b
4 changed files with 128 additions and 90 deletions

View file

@ -444,7 +444,7 @@ board_setYOffset( BoardCtxt* board, XP_U16 offset )
} /* board_setYOffset */
XP_U16
board_getYOffset( BoardCtxt* board )
board_getYOffset( const BoardCtxt* board )
{
return board->yOffset;
} /* board_getYOffset */
@ -911,7 +911,7 @@ invalOldPerimeter( BoardCtxt* board )
firstRow = board->yOffset + 1;
lastRow = board->prevYScrollOffset;
} else {
XP_U16 nVisible = board->lastVisibleRow - board->yOffset;
XP_U16 nVisible = board->lastVisibleRow - board->yOffset + 1;
lastRow = board->prevYScrollOffset + nVisible - 1;
firstRow = lastRow - diff + 1;
}
@ -928,7 +928,7 @@ invalPerimeter( BoardCtxt* board )
XP_U16 lastCol = model_numCols( board->model ) - 1;
XP_U16 firstAndLast = (1 << lastCol) | 1;
XP_U16 firstRow = board->yOffset;
XP_U16 lastRow = board->lastVisibleRow - 1;
XP_U16 lastRow = board->lastVisibleRow;
/* top and bottom rows */
board->redrawFlags[firstRow] = ~0;
@ -1023,9 +1023,8 @@ invalCellsWithTiles( BoardCtxt* board )
} /* invalCellsWithTiles */
XP_Bool
checkScrollCell( void* p_board, XP_U16 col, XP_U16 row )
checkScrollCell( BoardCtxt* board, XP_U16 col, XP_U16 row )
{
BoardCtxt* board = (BoardCtxt*)p_board;
XP_Rect rect;
XP_Bool moved = XP_FALSE;
@ -1048,6 +1047,27 @@ checkScrollCell( void* p_board, XP_U16 col, XP_U16 row )
return moved;
} /* checkScrollCell */
XP_Bool
onBorderCanScroll( const BoardCtxt* board, XP_U16 row, XP_S16* changeP )
{
XP_Bool result;
XP_S16 change = 0;
XP_U16 yOffset = board_getYOffset( board );
if ( yOffset > 0 && row == yOffset ) {
change = -yOffset;
} else if ( row == board->lastVisibleRow ) {
XP_U16 lastRow = model_numRows(board->model) - 1;
change = lastRow - row;
}
result = change != 0;
if ( result ) {
*changeP = change;
}
return result;
}
/* if any of a blank's neighbors is invalid, so must the blank become (since
* they share a border and drawing the neighbor will redraw the blank's border
* too) We'll want to redraw only those blanks that are themselves already
@ -1172,7 +1192,7 @@ cellFocused( const BoardCtxt* board, XP_U16 col, XP_U16 row )
focussed = (col == 0)
|| (col == model_numCols(board->model) - 1)
|| (row == board->yOffset)
|| (row == board->lastVisibleRow - 1);
|| (row == board->lastVisibleRow);
#else
focussed = XP_TRUE;
#endif
@ -1208,7 +1228,7 @@ drawBoard( BoardCtxt* board )
board->trayVisState == TRAY_REVEALED, &bq );
invalBlanksWithNeighbors( board, &bq );
for ( row = board->yOffset; row < board->lastVisibleRow; ++row ) {
for ( row = board->yOffset; row <= board->lastVisibleRow; ++row ) {
XP_U16 rowFlags = board->redrawFlags[row];
if ( rowFlags != 0 ) {
XP_U16 colMask;
@ -1501,7 +1521,7 @@ setTrayVisState( BoardCtxt* board, XW_TrayVisState newState )
invalCurHintRect( board, selPlayer );
#endif
nVisible = board->lastVisibleRow - board->yOffset;
nVisible = board->lastVisibleRow - board->yOffset + 1;
util_trayHiddenChange( board->util, board->trayVisState, nVisible );
}
return changed;
@ -1842,7 +1862,7 @@ figureBoardRect( BoardCtxt* board )
/* round down */
nVisible = boardBounds.height / boardVScale;
boardBounds.height = nVisible * boardVScale;
board->lastVisibleRow = nVisible + board->yOffset;
board->lastVisibleRow = nVisible + board->yOffset - 1;
board->boardBounds = boardBounds;
}

View file

@ -75,7 +75,7 @@ void board_reset( BoardCtxt* board );
/* Vertical scroll support; offset is in rows, not pixels */
XP_Bool board_setYOffset( BoardCtxt* board, XP_U16 newOffset );
XP_U16 board_getYOffset( BoardCtxt* board );
XP_U16 board_getYOffset( const BoardCtxt* board );
void board_setScoreboardLoc( BoardCtxt* board,
XP_U16 scoreLeft, XP_U16 scoreTop,

View file

@ -57,6 +57,7 @@ typedef struct DragState {
DragType dtype;
XP_Bool didMove; /* there was change during the drag; not a
tap */
XP_Bool scrollTimerSet;
XP_Bool isBlank; /* cache rather than lookup in model */
Tile tile; /* cache rather than lookup in model */
DragObjInfo start;
@ -241,7 +242,8 @@ XP_UCHAR* getTileDrawInfo( const BoardCtxt* board, Tile tile, XP_Bool isBlank,
XP_UCHAR* buf, XP_U16 len );
XP_Bool dividerMoved( BoardCtxt* board, XP_U8 newLoc );
XP_Bool checkScrollCell( void* p_board, XP_U16 col, XP_U16 row );
XP_Bool checkScrollCell( BoardCtxt* board, XP_U16 col, XP_U16 row );
XP_Bool onBorderCanScroll( const BoardCtxt* board, XP_U16 row, XP_S16* change );
#ifdef KEYBOARD_NAV
XP_Bool tray_moveCursor( BoardCtxt* board, XP_Key cursorKey,

View file

@ -33,6 +33,8 @@ static void invalHintRectDiffs( BoardCtxt* board, const DragObjInfo* cur,
const DragObjInfo* nxt );
static void setLimitsFrom( const BoardCtxt* board, BdHintLimits* limits );
static void startScrollTimerIf( BoardCtxt* board );
XP_Bool
dragDropInProgress( const BoardCtxt* board )
{
@ -140,6 +142,7 @@ dragDropStart( BoardCtxt* board, BoardObjectType obj, XP_U16 x, XP_U16 y )
if ( result ) {
ds->cur = ds->start;
invalDragObj( board, &ds->start );
startScrollTimerIf( board );
}
return result;
@ -219,24 +222,23 @@ dragDropEnd( BoardCtxt* board, XP_U16 xx, XP_U16 yy, XP_Bool* dragged )
ds->start.u.tray.index,
ds->cur.u.tray.index );
}
} else if ( newObj == OBJ_BOARD ) {
if ( !cellOccupied( board, ds->cur.u.board.col,
ds->cur.u.board.row, XP_TRUE ) ) {
if ( ds->start.obj == OBJ_TRAY ) {
/* moveTileToBoard flips its inputs */
(void)moveTileToBoard( board, ds->cur.u.board.col,
ds->cur.u.board.row,
ds->start.u.tray.index, EMPTY_TILE );
} else if ( ds->start.obj == OBJ_BOARD ) {
XP_U16 mod_curc, mod_curr;
flipIf( board, ds->cur.u.board.col, ds->cur.u.board.row,
&mod_curc, &mod_curr );
model_moveTileOnBoard( board->model, board->selPlayer,
mod_startc, mod_startr, mod_curc,
mod_curr );
/* inval points tile in case score changed */
board_invalTrayTiles( board, 1 << (MAX_TRAY_TILES-1) );
}
} else if ( (newObj == OBJ_BOARD) &&
!cellOccupied( board, ds->cur.u.board.col,
ds->cur.u.board.row, XP_TRUE ) ) {
if ( ds->start.obj == OBJ_TRAY ) {
/* moveTileToBoard flips its inputs */
(void)moveTileToBoard( board, ds->cur.u.board.col,
ds->cur.u.board.row,
ds->start.u.tray.index, EMPTY_TILE );
} else if ( ds->start.obj == OBJ_BOARD ) {
XP_U16 mod_curc, mod_curr;
flipIf( board, ds->cur.u.board.col, ds->cur.u.board.row,
&mod_curc, &mod_curr );
model_moveTileOnBoard( board->model, board->selPlayer,
mod_startc, mod_startr, mod_curc,
mod_curr );
/* inval points tile in case score changed */
board_invalTrayTiles( board, 1 << (MAX_TRAY_TILES-1) );
}
} else {
/* We're returning it to start, so will be re-inserted in tray */
@ -371,7 +373,6 @@ dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
XP_Bool moving = XP_FALSE;
DragObjInfo newInfo;
DragState* ds = &board->dragState;
XP_Bool doMore = XP_FALSE;
if ( !pointOnSomething( board, xx, yy, &newInfo.obj ) ) {
newInfo.obj = OBJ_NONE;
@ -389,76 +390,51 @@ dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
}
moving = dividerMoved( board, newloc );
}
} else {
/* If scrolling is possible, we can't trust pointOnSomething. So
check coordToCell. */
if( coordToCell( board, xx, yy, &newInfo.u.board.col,
&newInfo.u.board.row ) ) {
newInfo.obj = OBJ_BOARD;
doMore = XP_TRUE;
} else {
doMore = OBJ_TRAY == newInfo.obj;
}
}
if ( doMore ) {
#ifdef XWFEATURE_SEARCHLIMIT
if ( ds->dtype == DT_HINTRGN && newInfo.obj != OBJ_BOARD ) {
} else if ( ds->dtype == DT_HINTRGN && newInfo.obj != OBJ_BOARD ) {
/* do nothing */
#endif
} else {
if ( newInfo.obj == OBJ_BOARD ) {
(void)coordToCell( board, xx, yy, &newInfo.u.board.col,
&newInfo.u.board.row );
moving = (newInfo.u.board.col != ds->cur.u.board.col)
|| (newInfo.u.board.row != ds->cur.u.board.row)
|| (OBJ_TRAY == ds->cur.obj);
} else if ( newInfo.obj == OBJ_TRAY ) {
XP_Bool onDivider;
XP_S16 index = pointToTileIndex( board, xx, yy, &onDivider );
if ( !onDivider ) {
if ( index < 0 ) { /* negative means onto empty part of
tray. Force left. */
index = model_getNumTilesInTray( board->model,
board->selPlayer );
if ( OBJ_TRAY == ds->start.obj ) {
--index; /* dragging right into space */
}
}
moving = (OBJ_BOARD == ds->cur.obj)
|| (index != ds->cur.u.tray.index);
if ( moving ) {
newInfo.u.tray.index = index;
} else {
if ( newInfo.obj == OBJ_BOARD ) {
(void)coordToCell( board, xx, yy, &newInfo.u.board.col,
&newInfo.u.board.row );
moving = (newInfo.u.board.col != ds->cur.u.board.col)
|| (newInfo.u.board.row != ds->cur.u.board.row)
|| (OBJ_TRAY == ds->cur.obj);
} else if ( newInfo.obj == OBJ_TRAY ) {
XP_Bool onDivider;
XP_S16 index = pointToTileIndex( board, xx, yy, &onDivider );
if ( !onDivider ) {
if ( index < 0 ) { /* negative means onto empty part of
tray. Force left. */
index = model_getNumTilesInTray( board->model,
board->selPlayer );
if ( OBJ_TRAY == ds->start.obj ) {
--index; /* dragging right into space */
}
}
moving = (OBJ_BOARD == ds->cur.obj)
|| (index != ds->cur.u.tray.index);
if ( moving ) {
newInfo.u.tray.index = index;
}
}
}
if ( moving ) {
/* This little hack lets us inval twice using the same code but
only in the case where scrolling moves tiles. At a minimum
it's necessary to inval the old position before a scroll and
the new after. Otherwise if the platform scrolls by
bit-blitting the dragged object will be scrolled before it's
invalidated. */
do {
if ( ds->dtype == DT_TILE ) {
invalDragObjRange( board, &ds->cur, &newInfo );
if ( moving ) {
if ( ds->dtype == DT_TILE ) {
invalDragObjRange( board, &ds->cur, &newInfo );
#ifdef XWFEATURE_SEARCHLIMIT
} else if ( ds->dtype == DT_HINTRGN ) {
invalHintRectDiffs( board, &ds->cur, &newInfo );
if ( !ds->didMove ) { /* first time through */
invalCurHintRect( board, board->selPlayer );
}
} else if ( ds->dtype == DT_HINTRGN ) {
invalHintRectDiffs( board, &ds->cur, &newInfo );
if ( !ds->didMove ) { /* first time through */
invalCurHintRect( board, board->selPlayer );
}
#endif
}
} while ( (newInfo.obj == OBJ_BOARD)
&& checkScrollCell( board, newInfo.u.board.col,
newInfo.u.board.row ) );
XP_MEMCPY( &ds->cur, &newInfo, sizeof(ds->cur) );
}
XP_MEMCPY( &ds->cur, &newInfo, sizeof(ds->cur) );
startScrollTimerIf( board );
}
}
@ -506,6 +482,46 @@ setLimitsFrom( const BoardCtxt* board, BdHintLimits* limits )
limits->bottom = XP_MAX( ds->start.u.board.row, ds->cur.u.board.row );
}
static void
scrollTimerProc( void* closure, XWTimerReason why )
{
BoardCtxt* board = (BoardCtxt*)closure;
DragState* ds = &board->dragState;
XP_ASSERT( why == TIMER_PENDOWN );
if ( ds->scrollTimerSet ) {
XP_S16 change;
ds->scrollTimerSet = XP_FALSE;
if ( onBorderCanScroll( board, ds->cur.u.board.row, &change ) ) {
invalDragObj( board, &ds->cur );
ds->cur.u.board.row += (change >0 ? 1 : -1);
if ( checkScrollCell( board, ds->cur.u.board.col,
ds->cur.u.board.row ) ) {
board_draw( board ); /* may fail, e.g. on wince */
startScrollTimerIf( board );
}
}
}
} /* scrollTimerProc */
static void
startScrollTimerIf( BoardCtxt* board )
{
DragState* ds = &board->dragState;
if ( ds->cur.obj == OBJ_BOARD ) {
XP_S16 ignore;
if ( onBorderCanScroll( board, ds->cur.u.board.row, &ignore ) ) {
util_setTimer( board->util, TIMER_PENDOWN, 0,
scrollTimerProc, (void*) board );
ds->scrollTimerSet = XP_TRUE;
} else {
/* ignore if we've moved off */
ds->scrollTimerSet = XP_FALSE;
}
}
} /* startScrollTimerIf */
#ifdef CPLUS
}
#endif