Графика для Windows библиотека программиста средствами DirectDraw
bda5893f

Обработка пользовательского ввода



При запуске программы 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 были изменены для работы с новым меню. При нажатии клавиши Escape программа по-прежнему завершает работу, если меню частот в данный момент не отображается; тем не менее, если меню присутствует на экране, клавиша Escape просто скрывает его. Действие клавиш со стрелками также зависит от состояния меню. Если меню частот отображается, стрелки ­ и изменяют выделенную частоту, а если нет — выделенный пункт в меню видеорежимов.
Самые существенные различия связаны с обработкой клавиши Enter. Если во время нажатия клавиши Enter меню частот не отображается, мы вызываем функции CreateRateMenuSurface() и UpdateRateMenuSurface() и присваиваем флагу ratemenu_up значение TRUE. Давайте рассмотрим эти две функции. Функция CreateRateMenuSurface() выглядит так:

BOOL SuperSwitchWin::CreateRateMenuSurface() { if (ratemenusurf) ratemenusurf->Release(), ratemenusurf=0; int rates=refresh_rates[selectmode].GetSize(); ratemenusurf=CreateSurface( 80, rates*12+22 ); return TRUE; }

Сначала эта функция освобождает существующую поверхность (если таковая была создана ранее). Затем она определяет количество частот для выделенного в меню видеорежима и рассчитывает по ним размеры поверхности меню частот. Поверхность создается версией CreateSurface(), которой передаются ширина и высота новой поверхности.
Функция UpdateRateMenuSurface() отвечает за отображение текста меню. Выглядит она так:



BOOL SuperSwitchWin::UpdateRateMenuSurface() { RECT rect; GetSurfaceRect( ratemenusurf, rect ); rect.left++; rect.top++; rect.right--; rect.bottom--; if (!ClearSurface( ratemenusurf, 0, 200, 132 )) TRACE("first Clear failed\n"); if (!ClearSurface( ratemenusurf, 0,128,100, &rect )) TRACE("second Clear failed\n"); HDC hdc; ratemenusurf->GetDC( &hdc ); SelectObject( hdc, smallfont ); SetBkMode( hdc, TRANSPARENT ); SetTextColor( hdc, ratetextshadow ); ExtTextOut(hdc, 6, 4, 0, 0, rateheader, strlen(rateheader), 0 ); SetTextColor( hdc, ratetextcolor ); ExtTextOut(hdc, 5, 3, 0, 0, rateheader, strlen(rateheader), 0 ); CArray<DWORD,DWORD>& ratelist=refresh_rates[selectmode]; numrates=ratelist.GetSize(); for (int i=0; i<numrates; i++) { char buf[10]; int len=sprintf( buf, "%d hz", ratelist[i] ); SetTextColor( hdc, ratetextshadow ); ExtTextOut(hdc, 11, i*12+18, 0, 0, buf, len, 0 ); if (i==selectrate) SetTextColor( hdc, ratehighlightcolor ); else SetTextColor( hdc, ratetextcolor ); ExtTextOut(hdc, 10, i*12+17, 0, 0, buf, len, 0 ); } ratemenusurf->ReleaseDC( hdc ); return TRUE; }

Прежде всего функция очищает поверхность, вызывая ClearSurface(). Затем содержимое массива refresh_rates используется для вывода текстовых строк, связанных с каждым пунктом меню. Вывод текста, как обычно, осуществляется функцией GetDC() интерфейса DirectDrawSurface в сочетании с текстовыми функциями Win32. Перед выходом из функции UpdateRateMenuSurface() контекст устройства, полученный функцией GetDC(), освобождается с помощью функции ReleaseDC().




Давайте посмотрим, как в нашей программе организована обработка ввода. Нажатые клавиши обрабатываются функций 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, сигнализируя о завершении приложения.


Содержание раздела