One obvious way to create a keyboard interface is to add some WM_KEYDOWN logic to the window procedure that parallels the WM_VSCROLL and WM_HSCROLL logic:
case WM_KEYDOWN :
nVscrollInc = nHscrollInc = 0 ;
switch (wParam)
{
case VK_HOME : // same as WM_VSCROLL, SB_TOP
nVscrollInc = -nVscrollPos ;
break ;
case VK_END : // same as WM_VSCROLL, SB_BOTTOM
nVscrollInc = nVscrollMax - nVscrollPos ;
break ;
case VK_UP : // same as WM_VSCROLL, SB_LINEUP
nVscrollInc = -1 ;
break ;
case VK_DOWN : // same as WM_VSCROLL, SB_LINEDOWN
nVscrollInc = 1 ;
break ;
case VK_PRIOR : // same as WM_VSCROLL, SB_PAGEUP
nVscrollInc = min (-1, -cyClient / cyChar) ;
break ;
case VK_NEXT : // same as WM_VSCROLL, SB_PAGEDOWN
nVscrollInc = max (1, cyClient / cyChar) ;
break ;
case VK_LEFT : // same as WM_HSCROLL, SB_PAGEUP
nHscrollInc = -8 ;
break ;
case VK_RIGHT : // same as WM_HSCROLL, SB_PAGEDOWN
nHscrollInc = 8 ;
break ;
default :
break ;
}
if (nVscrollInc = max (-nVscrollPos,
min (nVscrollInc, nVscrollMax - nVscrollPos)))
{
nVscrollPos += nVscrollInc ;
ScrollWindow (hwnd, 0, -cyChar * nVscrollInc, NULL, NULL) ;
SetScrollPos (hwnd, SB_VERT, nVscrollPos, TRUE) ;
UpdateWindow (hwnd) ;
}
if (nHscrollInc = max (-nHscrollPos,
min (nHscrollInc, nHscrollMax - nHscrollPos)))
{
nHscrollPos += nHscrollInc ;
ScrollWindow (hwnd, -cxChar * nHscrollInc, 0, NULL, NULL) ;
SetScrollPos (hwnd, SB_HORZ, nHscrollPos, TRUE) ;
}
return 0 ;
Do you dislike this code as much as I do? Simply duplicating all the scroll bar code is unwise, because if we ever wanted to change the scroll bar logic, we'd have to make parallel changes in WM_KEYDOWN. There has to be a better way. And there is.