diff --git a/xwords4/linux/cursesmain.c b/xwords4/linux/cursesmain.c index 3e5f82409..11d535735 100644 --- a/xwords4/linux/cursesmain.c +++ b/xwords4/linux/cursesmain.c @@ -340,8 +340,8 @@ handleSpace( CursesAppGlobals* globals ) XP_Bool handled; checkAssignFocus( globals->cGlobals.game.board ); - globals->doDraw = board_handleKey( globals->cGlobals.game.board, - XP_RAISEFOCUS_KEY, &handled ); + globals->doDraw = board_handleKeyUp( globals->cGlobals.game.board, + XP_RAISEFOCUS_KEY, &handled ); return XP_TRUE; } /* handleSpace */ @@ -349,8 +349,8 @@ static XP_Bool handleRet( CursesAppGlobals* globals ) { XP_Bool handled; - globals->doDraw = board_handleKey( globals->cGlobals.game.board, - XP_RETURN_KEY, &handled ); + globals->doDraw = board_handleKeyUp( globals->cGlobals.game.board, + XP_RETURN_KEY, &handled ); return XP_TRUE; } /* handleRet */ @@ -437,8 +437,8 @@ static XP_Bool handleBackspace( CursesAppGlobals* globals ) { XP_Bool handled; - globals->doDraw = board_handleKey( globals->cGlobals.game.board, - XP_CURSOR_KEY_DEL, &handled ); + globals->doDraw = board_handleKeyUp( globals->cGlobals.game.board, + XP_CURSOR_KEY_DEL, &handled ); return XP_TRUE; } /* handleBackspace */ @@ -482,46 +482,6 @@ MenuList sharedMenuList[] = { }; #ifdef KEYBOARD_NAV -static XP_Bool -shiftFocus( CursesAppGlobals* globals, XP_Key key ) -{ - BoardCtxt* board = globals->cGlobals.game.board; - XP_Bool handled = XP_FALSE; - - do { /* allow break */ - XP_Bool forward; - BoardObjectType nxt; - - if ( key == XP_CURSOR_KEY_DOWN || key == XP_CURSOR_KEY_RIGHT ) { - forward = XP_TRUE; - } else if ( key == XP_CURSOR_KEY_UP || key == XP_CURSOR_KEY_LEFT ) { - forward = XP_FALSE; - } else { - break; - } - - switch( board_getFocusOwner( board ) ) { - case OBJ_NONE: - XP_ASSERT( 0 ); /* not in curses anyway */ - break; - case OBJ_SCORE: - nxt = forward ? OBJ_TRAY : OBJ_BOARD; - break; - case OBJ_BOARD: - nxt = forward ? OBJ_SCORE : OBJ_TRAY; - break; - case OBJ_TRAY: - nxt = forward ? OBJ_BOARD : OBJ_SCORE; - break; - } - handled = board_focusChanged( board, nxt, XP_TRUE ); - if ( handled ) { - changeMenuForFocus( globals, nxt ); - } - } while ( 0 ); - return handled; -} - static XP_Bool handleFocusKey( CursesAppGlobals* globals, XP_Key key ) { @@ -530,9 +490,14 @@ handleFocusKey( CursesAppGlobals* globals, XP_Key key ) checkAssignFocus( globals->cGlobals.game.board ); - draw = board_handleKey( globals->cGlobals.game.board, key, &handled ); + draw = board_handleKeyUp( globals->cGlobals.game.board, key, &handled ); if ( !handled ) { - draw = shiftFocus( globals, key ) || draw; + BoardObjectType nxt; + BoardObjectType order[] = { OBJ_BOARD, OBJ_SCORE, OBJ_TRAY }; + draw = linShiftFocus( &globals->cGlobals, key, order, &nxt ) || draw; + if ( nxt != OBJ_NONE ) { + changeMenuForFocus( globals, nxt ); + } } globals->doDraw = draw || globals->doDraw; @@ -1037,8 +1002,8 @@ passKeyToBoard( CursesAppGlobals* globals, char ch ) XP_Bool handled = ch >= 'a' && ch <= 'z'; if ( handled ) { ch += 'A' - 'a'; - globals->doDraw = board_handleKey( globals->cGlobals.game.board, - ch, NULL ); + globals->doDraw = board_handleKeyUp( globals->cGlobals.game.board, + ch, NULL ); } return handled; } /* passKeyToBoard */ diff --git a/xwords4/linux/gtkdraw.c b/xwords4/linux/gtkdraw.c index 6292c1878..62ae8dd1a 100644 --- a/xwords4/linux/gtkdraw.c +++ b/xwords4/linux/gtkdraw.c @@ -1,6 +1,6 @@ -/* -*- mode: C; fill-column: 78; c-basic-offset: 4; -*- */ +/* -*- mode: C; fill-column: 78; c-basic-offset: 4; compile-command: "make MEMDEBUG=TRUE"; -*- */ /* - * Copyright 1997-2005 by Eric House (xwords@eehouse.org). All rights reserved. + * Copyright 1997-2007 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 @@ -256,12 +256,38 @@ gtk_draw_boardBegin( DrawCtx* p_dctx, const DictionaryCtxt* XP_UNUSED(dict), } /* draw_finish */ static void -gtk_draw_objFinished( DrawCtx* XP_UNUSED(p_dctx), - BoardObjectType XP_UNUSED(typ), - const XP_Rect* XP_UNUSED(rect), - DrawFocusState XP_UNUSED(dfs) ) +drawFocusFrame( GtkDrawCtx* dctx, const XP_Rect* r ) { - // GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; + XP_Rect rectInset = *r; + XP_U16 i; + XP_U16 targetDim; + + targetDim = XP_MIN( rectInset.width, rectInset.height ); + targetDim >>= 1; + + gdk_gc_set_foreground( dctx->drawGC, &dctx->black ); + + for ( i = 0; i < 5; ++i ) { + insetRect( &rectInset, 1 ); + if ( rectInset.width < targetDim || rectInset.height < targetDim ) { + break; + } + gdk_draw_rectangle( DRAW_WHAT(dctx), + dctx->drawGC, + FALSE, + rectInset.left, rectInset.top, + rectInset.width+1, rectInset.height+1 ); + } +} + +static void +gtk_draw_objFinished( DrawCtx* p_dctx, BoardObjectType XP_UNUSED(typ), + const XP_Rect* rect, DrawFocusState dfs ) +{ + if ( dfs == DFS_TOP ) { + GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; + drawFocusFrame( dctx, rect ); + } } /* draw_finished */ static void @@ -378,6 +404,10 @@ gtk_draw_drawCell( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* letter, drawHintBorders( dctx, rect, hintAtts ); + if ( (flags & CELL_ISCURSOR) != 0 ) { + drawFocusFrame( dctx, rect ); + } + return XP_TRUE; } /* gtk_draw_drawCell */ @@ -404,12 +434,14 @@ gtk_draw_invertCell( DrawCtx* XP_UNUSED(p_dctx), static XP_Bool gtk_draw_trayBegin( DrawCtx* p_dctx, const XP_Rect* rect, XP_U16 owner, - DrawFocusState XP_UNUSED(dfs) ) + DrawFocusState dfs ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; XP_Rect clip = *rect; insetRect( &clip, -1 ); dctx->trayOwner = owner; + dctx->topFocus = dfs == DFS_TOP; + /* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)&clip ); */ return XP_TRUE; } /* gtk_draw_trayBegin */ @@ -476,11 +508,16 @@ gtk_draw_drawTile( DrawCtx* p_dctx, const XP_Rect* rect, const XP_UCHAR* textP, insetR.width, insetR.height); } } + + if ( !dctx->topFocus && (flags & CELL_ISCURSOR) != 0 ) { + drawFocusFrame( dctx, rect ); + } + } /* gtk_draw_drawTile */ static void gtk_draw_drawTileBack( DrawCtx* p_dctx, const XP_Rect* rect, - CellFlags XP_UNUSED(flags) ) + CellFlags flags ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; XP_Rect r = *rect; @@ -502,6 +539,10 @@ gtk_draw_drawTileBack( DrawCtx* p_dctx, const XP_Rect* rect, draw_string_at( dctx, dctx->layout[LAYOUT_LARGE], "?", &r, XP_GTK_JUST_CENTER, &dctx->playerColors[dctx->trayOwner], NULL ); + + if ( !dctx->topFocus && (flags & CELL_ISCURSOR) != 0 ) { + drawFocusFrame( dctx, rect ); + } } /* gtk_draw_drawTileBack */ static void @@ -577,12 +618,13 @@ gtk_draw_drawBoardArrow( DrawCtx* p_dctx, const XP_Rect* rectP, static void gtk_draw_scoreBegin( DrawCtx* p_dctx, const XP_Rect* rect, XP_U16 XP_UNUSED(numPlayers), - DrawFocusState XP_UNUSED(dfs) ) + DrawFocusState dfs ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; /* gdk_gc_set_clip_rectangle( dctx->drawGC, (GdkRectangle*)rect ); */ eraseRect( dctx, rect ); + dctx->topFocus = dfs == DFS_TOP; } /* gtk_draw_scoreBegin */ static void @@ -702,12 +744,16 @@ gtk_draw_score_drawPlayer( DrawCtx* p_dctx, const XP_Rect* rInner, draw_string_at( dctx, dctx->layout[LAYOUT_SMALL], scoreBuf, rInner, XP_GTK_JUST_CENTER, &dctx->playerColors[dsi->playerNum], NULL ); + + if ( !dctx->topFocus && ((dsi->flags & CELL_ISCURSOR) != 0) ) { + drawFocusFrame( dctx, rOuter ); + } } /* gtk_draw_score_drawPlayer */ static void gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect, XP_S16 score, XP_U16 XP_UNUSED(playerNum), - CellFlags XP_UNUSED(flags) ) + CellFlags flags ) { GtkDrawCtx* dctx = (GtkDrawCtx*)p_dctx; char buf[5]; @@ -733,6 +779,10 @@ gtk_draw_score_pendingScore( DrawCtx* p_dctx, const XP_Rect* rect, draw_string_at( dctx, dctx->layout[LAYOUT_SMALL], buf, &localR, XP_GTK_JUST_BOTTOMRIGHT, &dctx->black, NULL ); + + if ( !dctx->topFocus && (flags & CELL_ISCURSOR) != 0 ) { + drawFocusFrame( dctx, rect ); + } } /* gtk_draw_score_pendingScore */ static void diff --git a/xwords4/linux/gtkmain.c b/xwords4/linux/gtkmain.c index ee6825aae..b250f5143 100644 --- a/xwords4/linux/gtkmain.c +++ b/xwords4/linux/gtkmain.c @@ -153,17 +153,20 @@ button_release_event( GtkWidget* XP_UNUSED(widget), GdkEventMotion *event, return 1; } /* button_release_event */ -static gint -key_release_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event, - GtkAppGlobals* globals ) +static XP_Key +evtToXPKey( GdkEventKey* event, XP_Bool* movesCursorP ) { XP_Key xpkey = XP_KEY_NONE; XP_Bool movesCursor = XP_FALSE; guint keyval = event->keyval; - XP_LOGF( "got key 0x%x", keyval ); - - switch( keyval ) { + switch( keyval ) { + case GDK_Return: + xpkey = XP_RETURN_KEY; + break; + case GDK_space: + xpkey = XP_RAISEFOCUS_KEY; + break; case GDK_Left: xpkey = XP_CURSOR_KEY_LEFT; @@ -191,16 +194,43 @@ key_release_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event, xpkey = toupper(keyval); break; } - return FALSE; } + *movesCursorP = movesCursor; + return xpkey; +} /* evtToXPKey */ + +static gint +key_press_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event, + GtkAppGlobals* globals ) +{ + XP_Bool handled = XP_FALSE; + XP_Bool movesCursor; + XP_Key xpkey = evtToXPKey( event, &movesCursor ); + if ( xpkey != XP_KEY_NONE ) { + if ( board_handleKeyDown( globals->cGlobals.game.board, xpkey, + &handled ) ) { + board_draw( globals->cGlobals.game.board ); + } + } + return 1; +} + +static gint +key_release_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event, + GtkAppGlobals* globals ) +{ + XP_Bool handled = XP_FALSE; + XP_Bool movesCursor; + XP_Key xpkey = evtToXPKey( event, &movesCursor ); if ( xpkey != XP_KEY_NONE ) { - XP_Bool handled; XP_Bool draw; - draw = board_handleKey( globals->cGlobals.game.board, xpkey, &handled ); + draw = board_handleKeyUp( globals->cGlobals.game.board, xpkey, &handled ); - if ( movesCursor || !handled ) { - XP_LOGF( "need to handle focus shift" ); + if ( movesCursor && !handled ) { + BoardObjectType order[] = { OBJ_SCORE, OBJ_BOARD, OBJ_TRAY }; + draw = linShiftFocus( &globals->cGlobals, xpkey, order, + NULL ) || draw; } if ( draw ) { @@ -208,7 +238,7 @@ key_release_event( GtkWidget* XP_UNUSED(widget), GdkEventKey* event, } } - return 0; + return handled? 1 : 0; /* gtk will do something with the key if 0 returned */ } /* key_release_event */ #ifdef MEM_DEBUG @@ -1134,7 +1164,7 @@ pentimer_idle_func( gpointer data ) GtkAppGlobals* globals = (GtkAppGlobals*)data; struct timeval tv; XP_Bool callAgain = XP_TRUE; - + gettimeofday( &tv, NULL ); if ( (tv.tv_usec - globals->penTv.tv_usec) >= globals->penTimerInterval) { @@ -1788,6 +1818,8 @@ gtkmain( LaunchParams* params, int argc, char *argv[] ) g_signal_connect( GTK_OBJECT(drawing_area), "button_release_event", G_CALLBACK(button_release_event), &globals ); + g_signal_connect( GTK_OBJECT(window), "key_press_event", + G_CALLBACK(key_press_event), &globals ); g_signal_connect( GTK_OBJECT(window), "key_release_event", G_CALLBACK(key_release_event), &globals ); diff --git a/xwords4/linux/gtkmain.h b/xwords4/linux/gtkmain.h index 4ab87b63e..425552607 100644 --- a/xwords4/linux/gtkmain.h +++ b/xwords4/linux/gtkmain.h @@ -57,6 +57,7 @@ typedef struct GtkDrawCtx { PangoLayout* layout[LAYOUT_NLAYOUTS]; XP_U16 trayOwner; + XP_Bool topFocus; } GtkDrawCtx; typedef struct ClientStreamRec { diff --git a/xwords4/linux/linuxmain.c b/xwords4/linux/linuxmain.c index 3a0bc2bb9..570945a97 100644 --- a/xwords4/linux/linuxmain.c +++ b/xwords4/linux/linuxmain.c @@ -286,6 +286,50 @@ usage( char* appName, char* msg ) exit(1); } /* usage */ +#ifdef KEYBOARD_NAV +XP_Bool +linShiftFocus( CommonGlobals* cGlobals, XP_Key key, const BoardObjectType* order, + BoardObjectType* nxtP ) +{ + BoardCtxt* board = cGlobals->game.board; + XP_Bool handled = XP_FALSE; + BoardObjectType nxt = OBJ_NONE; + BoardObjectType cur; + XP_U16 i, curIndex; + + cur = board_getFocusOwner( board ); + if ( cur == OBJ_NONE ) { + cur = order[0]; + } + for ( i = 0; i < 3; ++i ) { + if ( cur == order[i] ) { + curIndex = i; + break; + } + } + XP_ASSERT( curIndex < 3 ); + + curIndex += 3; + if ( key == XP_CURSOR_KEY_DOWN || key == XP_CURSOR_KEY_RIGHT ) { + ++curIndex; + } else if ( key == XP_CURSOR_KEY_UP || key == XP_CURSOR_KEY_LEFT ) { + --curIndex; + } else { + XP_ASSERT(0); + } + curIndex %= 3; + + nxt = order[curIndex]; + handled = board_focusChanged( board, nxt, XP_TRUE ); + + if ( !!nxtP ) { + *nxtP = nxt; + } + + return handled; +} /* linShiftFocus */ +#endif + #ifdef XWFEATURE_RELAY static int linux_init_relay_socket( CommonGlobals* cGlobals ) diff --git a/xwords4/linux/linuxmain.h b/xwords4/linux/linuxmain.h index 256e1ca87..b9c38f9a5 100644 --- a/xwords4/linux/linuxmain.h +++ b/xwords4/linux/linuxmain.h @@ -53,4 +53,10 @@ XP_UCHAR* strFromStream( XWStreamCtxt* stream ); void catGameHistory( CommonGlobals* cGlobals ); void catOnClose( XWStreamCtxt* stream, void* closure ); +#ifdef KEYBOARD_NAV +XP_Bool linShiftFocus( CommonGlobals* cGlobals, XP_Key key, + const BoardObjectType* order, + BoardObjectType* nxtP ); +#endif + #endif