Обработка пользовательского ввода
При запуске программы Switch текст в нижней части меню подсказывает, какие клавиши управляют работой приложения. Клавиши со стрелками изменяют текущий выделенный видеорежим, клавиша Enter активизирует его (если он не является текущим), а клавиша Escape завершает работу программы. Все эти клавиши обрабатываются функцией OnKeyDown(), создаваемой ClassWizard. Ее код приведен в листинге 4.5.
Листинг 4.5. Функция SwitchWin::OnKeyDown()
void SwitchWin::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { int newindex; int nmodes=GetNumDisplayModes(); if (nmodes>maxmodes) nmodes=maxmodes; int rows=nmodes/menucols; if (nmodes%menucols) rows++;
switch (nChar) { case VK_ESCAPE: PostMessage( WM_CLOSE ); break; case VK_UP: newindex=selectmode-1; if (newindex>=0) { selectmode=newindex; UpdateMenuSurface(); } break; case VK_DOWN: newindex=selectmode+1; if (newindex<nmodes) { selectmode=newindex; UpdateMenuSurface(); } break; case VK_LEFT: newindex=selectmode-rows; if (newindex>=0) { selectmode=newindex; UpdateMenuSurface(); } break; case VK_RIGHT: newindex=selectmode+rows; if (newindex<nmodes) { selectmode=newindex; UpdateMenuSurface(); } break; case VK_RETURN: if (selectmode != GetCurDisplayMode()) { ActivateDisplayMode( selectmode ); x=y=0; } break; case 'S': SaveSurface( primsurf, "switch.bmp" ); break; case 'M': SaveSurface( menusurf, "menusurf.bmp" ); break; case 'F': SaveSurface( fpssurf, "fpssurf.bmp" ); break; case 'T': SaveSurface( bmpsurf, "trisurf.bmp" ); break; default: DirectDrawWin::OnKeyDown(nChar, nRepCnt, nFlags); } } |
Обработка нажатых клавиш происходит в различных секциях оператора switch. Клавиша Escape (код виртуальной клавиши VK_ESCAPE) приводит к посылке сообщения WM_CLOSE и последующему завершению приложения. При нажатии клавиш со стрелками изменяется индекс текущего видеорежима и вызывается функция UpdateMenuSurface(), которая перерисовывает menusurf в соответствии с произведенными изменениями. При нажатии клавиши Enter (VK_RETURN) вызывается функция ActivateDisplayMode(), которой в качестве аргумента передается индекс режима (при условии, что выбран видеорежим, отличный от текущего). Все остальные клавиши, нажатые пользователем, обрабатываются функцией OnKeyDown() базового класса.
Теперь в программу необходимо включить код для обработки пользовательского ввода при работе с меню частот. Мы воспользуемся функцией OnKeyDown() (листинг 4.7).
Листинг 4.7. Функция SuperSwitch::OnKeyDown()
void SuperSwitchWin::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) { int newindex; int nmodes=GetNumDisplayModes(); if (nmodes>maxmodes) nmodes=maxmodes; int rows=nmodes/menucols; if (nmodes%menucols) rows++;
switch (nChar) { case VK_ESCAPE: if (!include_refresh || !ratemenu_up) { PostMessage( WM_CLOSE ); break; } if (ratemenu_up) { ratemenu_up=FALSE; if (ratemenusurf) ratemenusurf->Release(), ratemenusurf=0; } break; case VK_UP: if ( include_refresh && ratemenu_up) { if (selectrate>0) { selectrate--; UpdateRateMenuSurface(); } } else { newindex=selectmode-1; if (newindex>=0) { selectmode=newindex; UpdateModeMenuSurface(); } } break; case VK_DOWN: if (include_refresh && ratemenu_up) { if (selectrate<numrates-1) { selectrate++; UpdateRateMenuSurface(); } } else { newindex=selectmode+1; if (newindex<nmodes) { selectmode=newindex; UpdateModeMenuSurface(); } } break; case VK_LEFT: if (include_refresh && ratemenu_up) break; newindex=selectmode-rows; if (newindex>=0) { selectmode=newindex; UpdateModeMenuSurface(); } break; case VK_RIGHT: if (include_refresh && ratemenu_up) break; newindex=selectmode+rows; if (newindex<nmodes) { selectmode=newindex; UpdateModeMenuSurface(); } break; case VK_RETURN: if (include_refresh) { if (ratemenu_up) { int rate=refresh_rates[selectmode][selectrate]; ActivateDisplayMode( selectmode, rate ); x=y=0; ratemenu_up=FALSE; } else { CreateRateMenuSurface(); UpdateRateMenuSurface(); ratemenu_up=TRUE; } } else { if (selectmode!=GetCurDisplayMode()) { ActivateDisplayMode( selectmode ); x=y=0; } } break; case 'S': SaveSurface( primsurf, "SuperSwitch.bmp" ); break; default: DirectDrawWin::OnKeyDown(nChar, nRepCnt, nFlags); } } |
Все case-секции оператора switch были изменены для работы с новым меню.
Давайте посмотрим, как в нашей программе организована обработка ввода. Нажатые клавиши обрабатываются функций OnKeyDown(), которая выглядит так:
void BmpViewWin::OnKeyDown(UINT key, UINT nRepCnt, UINT nFlags) { switch (key) { case VK_UP: Up(); break; case VK_DOWN: Down(); break; case VK_LEFT: Left(); break; case VK_RIGHT: Right(); break; case VK_HOME: Home(); break; case VK_END: End(); break; case VK_PRIOR: PageUp(); break; case VK_NEXT: PageDown(); break; case VK_ESCAPE: case VK_SPACE: case VK_RETURN: ShowDialog(); break; }
DirectDrawWin::OnKeyDown(key, nRepCnt, nFlags); } |
С первого взгляда на листинг OnKeyDown() можно разве что понять, какие клавиши обрабатываются программой, потому что вся содержательная работа поручается другим функциям. Обратите внимание — при нажатии клавиш Escape, пробел и Enter вызывается одна и та же функция ShowDialog(). Это облегчает вызов диалогового окна после вывода изображения.
Остальные восемь функций, вызываемых функцией OnKeyDown(), изменяют положение поверхности во время прокрутки:
- Up()
- Down()
- Left()
- Right()
- Home()
- End()
- PageUp()
- PageDown()
Каждая из этих функций определяет положение поверхности по значениям переменных x, y, xlimit, ylimit, xscroll и yscroll. Код всех восьми функций приведен в листинге 5.9.
Листинг 5.9. Функции смещения поверхности
void BmpViewWin::Up(int inc) { if (!yscroll) return;
if (y+inc<0) { y+=inc; update_screen=TRUE; } else { y=0; update_screen=TRUE; } } void BmpViewWin::Down(int inc) { if (!yscroll) return; if (y-inc>=ylimit) { y-=inc; update_screen=TRUE; } else { y=ylimit; update_screen=TRUE; } } void BmpViewWin::Left(int inc) { if (!xscroll) return; if (x+inc<0) { x+=inc; update_screen=TRUE; } else { x=0; update_screen=TRUE; } } void BmpViewWin::Right(int inc) { if (!xscroll) return; if (x-inc>=xlimit) { x-=inc; update_screen=TRUE; } else { x=xlimit; update_screen=TRUE; } } void BmpViewWin::Home() { if (xscroll && x!=0) { x=0; update_screen=TRUE; } if (yscroll && y!=0) { y=0; update_screen=TRUE; } } void BmpViewWin::End() { if (yscroll) { y=-(bmprect.Height()-displayrect.Height()); update_screen=TRUE; } if (xscroll) { x=-(bmprect.Width()-displayrect.Width()); update_screen=TRUE; } } void BmpViewWin::PageUp() { if (yscroll) { if (y-displayrect.Height()>0) { y-=displayrect.Height(); update_screen=TRUE; } else { y=0; update_screen=TRUE; } } } void BmpViewWin::PageDown() { if (yscroll) { if (y+displayrect.Height()<=ylimit) { y+=displayrect.Height(); update_screen=TRUE; } else { y=ylimit; update_screen=TRUE; } } } |
Обработчикам клавиш со стрелками (Up(), Down(), Left(), Right()) можно передать необязательный аргумент, который определяет шаг прокрутки. Как видно из определения класса BmpViewWin (см. листинг 5.5), по умолчанию шаг прокрутки равен 4.
В программе AviPlay ввод не играет особой роли. Программа реагирует всего на три клавиши, причем одинаково. Ввод с клавиатуры обрабатывается функцией OnKeyDown():
void AviPlayWin::OnKeyDown(UINT key, UINT nRepCnt, UINT nFlags) { switch (key) { case VK_ESCAPE: case VK_SPACE: case VK_RETURN: ShowDialog(); break; }
DirectDrawWin::OnKeyDown(key, nRepCnt, nFlags); } Все три клавиши вызывают функцию ShowDialog(). Аналогично обрабатывается и ввод от мыши, это происходит в функции OnRButtonDown(): void AviPlayWin::OnRButtonDown(UINT nFlags, CPoint point) { ShowDialog(); DirectDrawWin::OnRButtonDown(nFlags, point); } |
Все три клавиши вызывают функцию ShowDialog(). Аналогично обрабатывается и ввод от мыши, это происходит в функции OnRButtonDown():
void AviPlayWin::OnRButtonDown(UINT nFlags, CPoint point) { ShowDialog(); DirectDrawWin::OnRButtonDown(nFlags, point); } |
Когда пользователь закрывает диалоговое окно для выбора AVI-файла, функция ShowDialog() посылает сообщение WM_CLOSE, сигнализируя о завершении приложения.