add ability to scroll the board by dragging it. add new method

util_altKeyDown to allow user to choose between scrolling board and
dragging the hint rect when both are possible.  add adjustYOffset;
make it and board_setYOffset more tolerant of out-of-bounds inputs and
use that to simplify calling code.
This commit is contained in:
ehouse 2008-04-08 03:55:53 +00:00
parent fd4cc02c13
commit ff254fcef4
11 changed files with 160 additions and 52 deletions

View file

@ -417,17 +417,38 @@ board_prefsChanged( BoardCtxt* board, CommonPrefs* cp )
return showArrowChanged || hideValChanged;
} /* board_prefsChanged */
XP_Bool
adjustYOffset( BoardCtxt* board, XP_S16 moveBy )
{
XP_U16 nVisible = board->lastVisibleRow - board->yOffset + 1;
XP_U16 nRows = model_numRows(board->model);
XP_S16 newOffset = board->yOffset - moveBy;
if ( newOffset < 0 ) {
newOffset = 0;
} else if ( newOffset + nVisible > nRows ) {
newOffset = nRows - nVisible;
}
return board_setYOffset( board, newOffset );
} /* adjustYOffset */
XP_Bool
board_setYOffset( BoardCtxt* board, XP_U16 offset )
{
XP_U16 oldOffset = board->yOffset;
XP_Bool result = oldOffset != offset;
if ( result ) {
XP_U16 nVisible = board->lastVisibleRow - board->yOffset + 1;
XP_U16 nRows = model_numRows(board->model);
result = offset <= nRows - nVisible;
if ( result ) {
/* check if scrolling makes sense for this board in its current
state. */
XP_U16 visibleHeight = board->boardBounds.height;
XP_U16 fullHeight = model_numRows(board->model) * board->boardVScale;
XP_U16 fullHeight = nRows * board->boardVScale;
result = visibleHeight < fullHeight;
if ( result ) {
@ -439,6 +460,7 @@ board_setYOffset( BoardCtxt* board, XP_U16 offset )
board->needsDrawing = XP_TRUE;
}
}
}
return result;
} /* board_setYOffset */
@ -1031,17 +1053,17 @@ checkScrollCell( BoardCtxt* board, XP_U16 col, XP_U16 row )
if ( board->boardObscuresTray && board->trayVisState != TRAY_HIDDEN ) {
/* call getCellRect until the cell's on the board. */
while ( !getCellRect( board, col, row, &rect ) ) {
XP_U16 oldOffset = board_getYOffset( board );
XP_S16 moveBy;
if ( rect.top < board->boardBounds.top ) {
--oldOffset;
moveBy = 1;
} else if ( rect.top + rect.height >
board->boardBounds.top + board->boardBounds.height ) {
++oldOffset;
moveBy = -1;
} else {
XP_ASSERT( 0 );
}
board_setYOffset( board, oldOffset );
moved = XP_TRUE;
moved = adjustYOffset( board, moveBy );
XP_ASSERT( moved );
}
}
return moved;
@ -2148,7 +2170,6 @@ static XP_Bool
handlePenDownOnBoard( BoardCtxt* board, XP_U16 xx, XP_U16 yy )
{
XP_Bool result = XP_FALSE;
XP_U16 col, row;
/* Start a timer no matter what. After it fires we'll decide whether it's
appropriate to handle it. No. That's too expensive */
if ( TRADE_IN_PROGRESS(board) && ptOnTradeWindow( board, xx, yy ) ) {
@ -2159,9 +2180,7 @@ handlePenDownOnBoard( BoardCtxt* board, XP_U16 xx, XP_U16 yy )
/* As a first cut, you start a hint-region drag unless the cell is
occupied by a non-committed cell. */
coordToCell( board, xx, yy, &col, &row );
if ( (board->trayVisState == TRAY_REVEALED)
&& !board->tradeInProgress[board->selPlayer] ) {
if ( !board->tradeInProgress[board->selPlayer] ) {
result = dragDropStart( board, OBJ_BOARD, xx, yy );
}

View file

@ -50,6 +50,7 @@ typedef enum {
#ifdef XWFEATURE_SEARCHLIMIT
,DT_HINTRGN
#endif
,DT_BOARD
} DragType;
@ -244,6 +245,7 @@ XP_Bool dividerMoved( BoardCtxt* board, XP_U8 newLoc );
XP_Bool checkScrollCell( BoardCtxt* board, XP_U16 col, XP_U16 row );
XP_Bool onBorderCanScroll( const BoardCtxt* board, XP_U16 row, XP_S16* change );
XP_Bool adjustYOffset( BoardCtxt* board, XP_S16 moveBy );
#ifdef KEYBOARD_NAV
XP_Bool tray_moveCursor( BoardCtxt* board, XP_Key cursorKey,

View file

@ -25,6 +25,9 @@ extern "C" {
#include "dragdrpp.h"
#include "game.h"
/* How many squares must scroll gesture take in to be recognized. */
#define SCROLL_DRAG_THRESHHOLD 3
static XP_Bool dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
BoardObjectType* onWhichP );
static void invalDragObjRange( BoardCtxt* board, const DragObjInfo* from,
@ -54,12 +57,14 @@ ddStartBoard( BoardCtxt* board, XP_U16 xx, XP_U16 yy )
{
DragState* ds = &board->dragState;
XP_Bool found;
XP_Bool trayVisible;
XP_U16 col, row;
found = coordToCell( board, xx, yy, &col, &row );
XP_ASSERT( found );
if ( holdsPendingTile( board, col, row ) ) {
trayVisible = board->trayVisState == TRAY_REVEALED;
if ( trayVisible && holdsPendingTile( board, col, row ) ) {
XP_U16 modelc, modelr;
XP_Bool ignore;
@ -70,10 +75,26 @@ ddStartBoard( BoardCtxt* board, XP_U16 xx, XP_U16 yy )
board->selPlayer, &ds->tile, &ds->isBlank,
&ignore, &ignore );
XP_ASSERT( found );
} else {
/* If we're not dragging a tile, we can either drag the board (scroll)
or work on hint regions. Sometimes scrolling isn't possible.
Sometimes hint dragging is disabled. But if both are possible,
then the alt key determines it. I figure scrolling will be more
common than hint dragging when both are possible, but you can turn
hint dragging off, so if it's on that's probably what you want. */
XP_Bool canScroll = board->lastVisibleRow < model_numRows(board->model);
if ( 0 ) {
#ifdef XWFEATURE_SEARCHLIMIT
} else if ( board->gi->allowHintRect ) {
} else if ( board->gi->allowHintRect && trayVisible ) {
if ( !util_altKeyDown(board->util) ) {
ds->dtype = DT_HINTRGN;
} else if ( canScroll ) {
ds->dtype = DT_BOARD;
}
#endif
} else if ( canScroll ) {
ds->dtype = DT_BOARD;
}
}
ds->start.u.board.col = col;
ds->start.u.board.row = row;
@ -379,13 +400,18 @@ dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
}
*onWhichP = newInfo.obj;
if ( newInfo.obj == OBJ_BOARD ) {
(void)coordToCell( board, xx, yy, &newInfo.u.board.col,
&newInfo.u.board.row );
}
if ( ds->dtype == DT_DIVIDER ) {
if ( OBJ_TRAY == newInfo.obj ) {
XP_U16 newloc;
XP_U16 scale = board->trayScaleH;
xx -= board->trayBounds.left;
newloc = xx / scale;
if ( (xx % scale) > (scale/2)) {
if ( (xx % scale) > ((scale+board->dividerWidth)/2)) {
++newloc;
}
moving = dividerMoved( board, newloc );
@ -394,10 +420,14 @@ dragDropContinueImpl( BoardCtxt* board, XP_U16 xx, XP_U16 yy,
} else if ( ds->dtype == DT_HINTRGN && newInfo.obj != OBJ_BOARD ) {
/* do nothing */
#endif
} else if ( ds->dtype == DT_BOARD ) {
if ( newInfo.obj == OBJ_BOARD ) {
XP_S16 diff = newInfo.u.board.row - ds->cur.u.board.row;
diff /= SCROLL_DRAG_THRESHHOLD;
moving = adjustYOffset( board, diff );
}
} 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);
@ -509,7 +539,7 @@ startScrollTimerIf( BoardCtxt* board )
{
DragState* ds = &board->dragState;
if ( ds->cur.obj == OBJ_BOARD ) {
if ( (ds->dtype == DT_TILE) && (ds->cur.obj == OBJ_BOARD) ) {
XP_S16 ignore;
if ( onBorderCanScroll( board, ds->cur.u.board.row, &ignore ) ) {
util_setTimer( board->util, TIMER_PENDOWN, 0,

View file

@ -138,6 +138,8 @@ typedef struct UtilVtable {
void (*m_util_requestTime)( XW_UtilCtxt* uc );
XP_Bool (*m_util_altKeyDown)( XW_UtilCtxt* uc );
XP_U32 (*m_util_getCurSeconds)( XW_UtilCtxt* uc );
DictionaryCtxt* (*m_util_makeEmptyDict)( XW_UtilCtxt* uc );
@ -222,6 +224,9 @@ struct XW_UtilCtxt {
#define util_requestTime( uc ) \
(uc)->vtable->m_util_requestTime((uc))
#define util_altKeyDown( uc ) \
(uc)->vtable->m_util_altKeyDown((uc))
#define util_getCurSeconds(uc) \
(uc)->vtable->m_util_getCurSeconds((uc))

View file

@ -102,12 +102,21 @@ rememberClient( GtkAppGlobals* globals, guint key, int sock,
} /* rememberClient */
#endif
static void
gtkSetAltState( GtkAppGlobals* globals, guint state )
{
globals->altKeyDown
= (state & (GDK_MOD1_MASK|GDK_SHIFT_MASK|GDK_CONTROL_MASK)) != 0;
}
static gint
button_press_event( GtkWidget* XP_UNUSED(widget), GdkEventButton *event,
GtkAppGlobals* globals )
{
XP_Bool redraw, handled;
gtkSetAltState( globals, event->state );
if ( !globals->mouseDown ) {
globals->mouseDown = XP_TRUE;
redraw = board_handlePenDown( globals->cGlobals.game.board,
@ -125,6 +134,8 @@ motion_notify_event( GtkWidget* XP_UNUSED(widget), GdkEventMotion *event,
{
XP_Bool handled;
gtkSetAltState( globals, event->state );
if ( globals->mouseDown ) {
handled = board_handlePenMove( globals->cGlobals.game.board, event->x,
event->y );
@ -144,6 +155,8 @@ button_release_event( GtkWidget* XP_UNUSED(widget), GdkEventMotion *event,
{
XP_Bool redraw;
gtkSetAltState( globals, event->state );
if ( globals->mouseDown ) {
redraw = board_handlePenUp( globals->cGlobals.game.board,
event->x,
@ -214,6 +227,8 @@ key_press_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event,
XP_Bool movesCursor;
XP_Key xpkey = evtToXPKey( event, &movesCursor );
gtkSetAltState( globals, event->state );
if ( xpkey != XP_KEY_NONE ) {
XP_Bool draw = globals->keyDown ?
board_handleKeyRepeat( globals->cGlobals.game.board, xpkey,
@ -237,6 +252,8 @@ key_release_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event,
XP_Bool movesCursor;
XP_Key xpkey = evtToXPKey( event, &movesCursor );
gtkSetAltState( globals, event->state );
if ( xpkey != XP_KEY_NONE ) {
XP_Bool draw;
draw = board_handleKeyUp( globals->cGlobals.game.board, xpkey,
@ -379,7 +396,7 @@ createOrLoadObjects( GtkAppGlobals* globals )
/* params->gi.phoniesAction = PHONIES_DISALLOW; */
#ifdef XWFEATURE_SEARCHLIMIT
params->gi.allowHintRect = XP_TRUE;
params->gi.allowHintRect = params->allowHintRect;
#endif
#ifndef XWFEATURE_STANDALONE_ONLY
@ -969,16 +986,14 @@ handle_done_button( GtkWidget* XP_UNUSED(widget), GtkAppGlobals* globals )
static void
scroll_value_changed( GtkAdjustment *adj, GtkAppGlobals* globals )
{
XP_U16 curYOffset, newValue;
XP_U16 newValue;
gfloat newValueF = adj->value;
XP_ASSERT( newValueF >= 0.0
&& newValueF <= globals->cGlobals.params->nHidden );
curYOffset = board_getYOffset( globals->cGlobals.game.board );
newValue = (XP_U16)newValueF;
if ( newValue != curYOffset ) {
board_setYOffset( globals->cGlobals.game.board, newValue );
if ( board_setYOffset( globals->cGlobals.game.board, newValue ) ) {
board_draw( globals->cGlobals.game.board );
}
} /* scroll_value_changed */
@ -1185,6 +1200,13 @@ gtk_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col, XP_U16 row )
#endif
} /* gtk_util_hiliteCell */
static XP_Bool
gtk_util_altKeyDown( XW_UtilCtxt* uc )
{
GtkAppGlobals* globals = (GtkAppGlobals*)uc->closure;
return globals->altKeyDown;
}
static XP_Bool
gtk_util_engineProgressCallback( XW_UtilCtxt* XP_UNUSED(uc) )
{
@ -1578,6 +1600,7 @@ setupGtkUtilCallbacks( GtkAppGlobals* globals, XW_UtilCtxt* util )
util->vtable->m_util_yOffsetChange = gtk_util_yOffsetChange;
util->vtable->m_util_notifyGameOver = gtk_util_notifyGameOver;
util->vtable->m_util_hiliteCell = gtk_util_hiliteCell;
util->vtable->m_util_altKeyDown = gtk_util_altKeyDown;
util->vtable->m_util_engineProgressCallback =
gtk_util_engineProgressCallback;
util->vtable->m_util_setTimer = gtk_util_setTimer;

View file

@ -94,6 +94,7 @@ typedef struct GtkAppGlobals {
XP_Bool gridOn;
XP_Bool dropIncommingMsgs;
XP_Bool mouseDown;
XP_Bool altKeyDown;
#ifdef KEYBOARD_NAV
XP_Bool keyDown;
#endif

View file

@ -241,6 +241,9 @@ usage( char* appName, char* msg )
#if defined PLATFORM_GTK
"\t [-k] # ask for parameters via \"new games\" dlg\n"
"\t [-h numRowsHidded] \n"
# ifdef XWFEATURE_SEARCHLIMIT
"\t [-I] # don't support hint rect dragging\n"
# endif
#endif
"\t [-f file] # use this file to save/load game\n"
"\t [-q] # quit when game over (useful for robot-only)\n"
@ -866,6 +869,9 @@ main( int argc, char** argv )
mainParams.gi.robotSmartness = SMART_ROBOT;
mainParams.noHeartbeat = XP_FALSE;
mainParams.nHidden = 0;
#ifdef XWFEATURE_SEARCHLIMIT
mainParams.allowHintRect = XP_TRUE;
#endif
/* serverName = mainParams.info.clientInfo.serverName = "localhost"; */
@ -886,7 +892,7 @@ main( int argc, char** argv )
"gu"
#endif
#if defined PLATFORM_GTK
"h:"
"h:I"
#endif
"kKf:l:n:Nsd:e:r:b:qw:Sit:Umvc"
#ifdef XWFEATURE_RELAY
@ -936,6 +942,11 @@ main( int argc, char** argv )
case 'i':
mainParams.printHistory = 1;
break;
#ifdef XWFEATURE_SEARCHLIMIT
case 'I':
mainParams.allowHintRect = XP_FALSE;
break;
#endif
case 'K':
mainParams.skipWarnings = 1;
break;

View file

@ -61,6 +61,9 @@ typedef struct LaunchParams {
XP_Bool skipWarnings;
XP_Bool showRobotScores;
XP_Bool noHeartbeat;
#ifdef XWFEATURE_SEARCHLIMIT
XP_Bool allowHintRect;
#endif
DeviceRole serverRole;

View file

@ -122,6 +122,7 @@ static XP_Bool palm_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col,
static XP_Bool palm_util_engineProgressCallback( XW_UtilCtxt* uc );
static void palm_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
XWTimerProc proc, void* closure );
static XP_Bool palm_util_altKeyDown( XW_UtilCtxt* uc );
static XP_U32 palm_util_getCurSeconds( XW_UtilCtxt* uc );
static void palm_util_requestTime( XW_UtilCtxt* uc );
static DictionaryCtxt* palm_util_makeEmptyDict( XW_UtilCtxt* uc );
@ -648,6 +649,7 @@ initUtilFuncs( PalmAppGlobals* globals )
vtable->m_util_engineProgressCallback = palm_util_engineProgressCallback;
vtable->m_util_setTimer = palm_util_setTimer;
vtable->m_util_requestTime = palm_util_requestTime;
vtable->m_util_altKeyDown = palm_util_altKeyDown;
vtable->m_util_getCurSeconds = palm_util_getCurSeconds;
vtable->m_util_makeEmptyDict = palm_util_makeEmptyDict;
#ifndef XWFEATURE_STANDALONE_ONLY
@ -1965,12 +1967,8 @@ scrollBoard( PalmAppGlobals* globals, Int16 newValue, Boolean fromBar )
XP_U16 curYOffset;
XP_ASSERT( !!globals->game.board );
curYOffset = board_getYOffset( globals->game.board );
result = curYOffset != newValue;
if ( result ) {
result = board_setYOffset( globals->game.board, newValue );
}
if ( !fromBar ) {
updateScrollbar( globals, newValue );
@ -3882,6 +3880,13 @@ palm_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why,
postEmptyEvent( noopEvent );
} /* palm_util_setTimer */
static XP_Bool
palm_util_altKeyDown( XW_UtilCtxt* XP_UNUSED(uc) )
{
XP_LOGF( "%s unimplemented", __func__ );
return XP_FALSE;
}
static void
palm_util_requestTime( XW_UtilCtxt* uc )
{

View file

@ -1,6 +1,6 @@
/* -*- fill-column: 77; c-basic-offset: 4; compile-command: "make TARGET_OS=wince DEBUG=TRUE" -*- */
/*
* Copyright 2002-2007 by Eric House (xwords@eehouse.org). All rights reserved.
* Copyright 2002-2008 by Eric House (xwords@eehouse.org). All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@ -111,6 +111,7 @@ static XP_Bool ce_util_hiliteCell( XW_UtilCtxt* uc, XP_U16 col,
static XP_Bool ce_util_engineProgressCallback( XW_UtilCtxt* uc );
static void ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why, XP_U16 when,
XWTimerProc proc, void* closure);
static XP_Bool ce_util_altKeyDown( XW_UtilCtxt* uc );
static void ce_util_requestTime( XW_UtilCtxt* uc );
static XP_U32 ce_util_getCurSeconds( XW_UtilCtxt* uc );
static DictionaryCtxt* ce_util_makeEmptyDict( XW_UtilCtxt* uc );
@ -335,6 +336,7 @@ ceInitUtilFuncs( CEAppGlobals* globals )
vtable->m_util_hiliteCell = ce_util_hiliteCell;
vtable->m_util_engineProgressCallback = ce_util_engineProgressCallback;
vtable->m_util_setTimer = ce_util_setTimer;
vtable->m_util_altKeyDown = ce_util_altKeyDown;
vtable->m_util_requestTime = ce_util_requestTime;
vtable->m_util_getCurSeconds = ce_util_getCurSeconds;
vtable->m_util_makeEmptyDict = ce_util_makeEmptyDict;
@ -563,15 +565,14 @@ figureBoardParms( CEAppGlobals* globals, XP_U16 nRows, CEBoardParms* bparms )
bparms->horiz = horiz;
#ifdef CEFEATURE_CANSCROLL
globals->nHiddenRows = nRows - nVisibleRows;
bparms->needsScroller = nVisibleRows < nRows;
if ( bparms->needsScroller && !IS_SMARTPHONE(globals) ) {
XP_U16 boardRight = boardLeft + (nRows * hScale);
showScroller( globals, globals->nHiddenRows,
showScroller( globals, nRows - nVisibleRows,
boardRight, boardTop,
scrollWidth, boardHt );
XP_LOGF( "NEEDING SCROLLBAR!!!!" );
XP_LOGF( "%d rows hidden", globals->nHiddenRows );
XP_LOGF( "%d rows hidden", nRows - nVisibleRows );
} else {
hideScroller( globals );
}
@ -1343,12 +1344,17 @@ handleJuggleCmd( CEAppGlobals* globals )
static XP_Bool
handleHidetrayCmd( CEAppGlobals* globals )
{
XP_Bool result;
XW_TrayVisState curState = board_getTrayVisState( globals->game.board );
if ( curState == TRAY_REVEALED ) {
return board_hideTray( globals->game.board );
result = board_hideTray( globals->game.board );
} else {
return board_showTray( globals->game.board );
result = board_showTray( globals->game.board );
}
ceSetLeftSoftkey( globals, ID_MOVE_HIDETRAY );
return result;
} /* handleHidetrayCmd */
static XP_Bool
@ -1774,11 +1780,9 @@ handleScroll( CEAppGlobals* globals, XP_S16 pos, /* only valid for THUMB* */
/* do nothing */
}
if ( newOffset >= 0 && newOffset <= globals->nHiddenRows ) {
result = curYOffset != newOffset
&& board_setYOffset( globals->game.board, newOffset );
}
}
return result;
} /* handleScroll */
#endif
@ -2821,7 +2825,6 @@ ce_util_trayHiddenChange( XW_UtilCtxt* uc, XW_TrayVisState XP_UNUSED(newState),
if ( !!globals->scrollHandle ) {
nHiddenRows = model_numRows( globals->game.model ) - nVisibleRows;
updateScrollInfo( globals->scrollHandle, nHiddenRows );
globals->nHiddenRows = nHiddenRows;
}
#endif
@ -2907,6 +2910,13 @@ ce_util_setTimer( XW_UtilCtxt* uc, XWTimerReason why,
globals->timerIDs[why] = timerID;
} /* ce_util_setTimer */
static XP_Bool
ce_util_altKeyDown( XW_UtilCtxt* XP_UNUSED(uc) )
{
return GetKeyState(VK_LSHIFT) < 0
|| GetKeyState(VK_RSHIFT) < 0;
}
static void
ce_util_requestTime( XW_UtilCtxt* uc )
{

View file

@ -129,7 +129,6 @@ typedef struct CEAppGlobals {
XP_U16 flags; /* bits defined below */
#ifdef CEFEATURE_CANSCROLL
XP_U16 nHiddenRows;
HWND scrollHandle;
#endif