Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
Окно получает ввод клавиатуры в виде сообщений нажатия клавиш и символьных сообщений. Цикл сообщений, подключенный к окну, должен содержать код для перевода сообщений нажатия клавиш в соответствующие символьные сообщения. Если в окне отображаются входные данные клавиатуры в клиентской области, он должен создать и отобразить курсор, чтобы указать положение, в котором будет введен следующий символ. В следующих разделах описывается код, участвующий в получении, обработке и отображении ввода клавиатуры:
- Обработка сообщений о нажатии клавиш
- перевод символьных сообщений
- обработка символьных сообщений
- Использование каретки
- отображение ввода клавиатуры
Обработка сообщений нажатия клавиш
Процедура окна, имеющего фокус клавиатуры, получает сообщения о нажатии клавиш, когда пользователь вводит данные с клавиатуры. Сообщения нажатия клавиш WM_KEYDOWN, WM_KEYUP, WM_SYSKEYDOWNи WM_SYSKEYUP. Типичная процедура окна игнорирует все сообщения нажатия клавиш, кроме WM_KEYDOWN. Система публикует сообщение WM_KEYDOWN, когда пользователь нажимает клавишу.
Когда процедура окна получает сообщение WM_KEYDOWN, он должен проверить код виртуального ключа, который сопровождает сообщение, чтобы определить способ обработки нажатия клавиш. Код виртуального ключа находится в параметре wParam сообщения. Как правило, приложение обрабатывает только нажатия клавиш, созданные нехарактерными ключами, включая ключи функции, ключи перемещения курсора и ключи специального назначения, такие как INS, DEL, HOME и END.
В следующем примере показана платформа процедур окна, которую обычно приложение использует для получения и обработки сообщений нажатия клавиш.
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT:
// Process the LEFT ARROW key.
break;
case VK_RIGHT:
// Process the RIGHT ARROW key.
break;
case VK_UP:
// Process the UP ARROW key.
break;
case VK_DOWN:
// Process the DOWN ARROW key.
break;
case VK_HOME:
// Process the HOME key.
break;
case VK_END:
// Process the END key.
break;
case VK_INSERT:
// Process the INS key.
break;
case VK_DELETE:
// Process the DEL key.
break;
case VK_F2:
// Process the F2 key.
break;
default:
// Process other non-character keystrokes.
break;
}
Преобразование символьных сообщений
Любой поток, получающий входные данные символов от пользователя, должен включать функцию TranslateMessage в цикл сообщений. Эта функция проверяет код виртуального ключа сообщения нажатия клавиш и, если код соответствует символу, помещает символьное сообщение в очередь сообщений. Символьное сообщение удаляется и отправляется на следующую итерацию цикла сообщений; параметр wParam сообщения содержит код символа.
Как правило, цикл сообщений потока должен использовать функцию TranslateMessage для перевода каждого сообщения, а не только сообщений виртуального ключа. Хотя TranslateMessage не влияет на другие типы сообщений, это гарантирует правильность преобразования ввода клавиатуры. В следующем примере показано, как включить функцию TranslateMessage в типичный цикл сообщений потока.
MSG msg;
BOOL bRet;
while ((bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
if (bRet == -1);
{
// handle the error and possibly exit
}
else
{
if (TranslateAccelerator(hwndMain, haccl, &msg) == 0)
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
Обработка символьных сообщений
Процедура окна получает символьное сообщение, когда функция TranslateMessage преобразует код виртуального ключа, соответствующий символьной клавише. Символьные сообщения WM_CHAR, WM_DEADCHAR, WM_SYSCHARи WM_SYSDEADCHAR. Типичная процедура окна игнорирует все символьные сообщения, кроме WM_CHAR. Функция TranslateMessage создает сообщение WM_CHAR, когда пользователь нажимает любой из следующих клавиш:
- Любой символьный ключ
- БЭКСПЕЙС
- ВВОД (возврат каретки)
- ESC
- SHIFT+ВВОД (linefeed)
- ВКЛАДКА
Когда процедура окна получает сообщение WM_CHAR, он должен проверить код символа, который сопровождает сообщение, чтобы определить способ обработки символа. Код символа находится в параметре wParam сообщения.
В следующем примере показана платформа процедур окна, которую обычно приложение использует для получения и обработки символьных сообщений.
case WM_CHAR:
switch (wParam)
{
case 0x08: // or '\b'
// Process a backspace.
break;
case 0x09: // or '\t'
// Process a tab.
break;
case 0x0A: // or '\n'
// Process a linefeed.
break;
case 0x0D:
// Process a carriage return.
break;
case 0x1B:
// Process an escape.
break;
default:
// Process displayable characters.
break;
}
Использование Caret
Окно, которое получает ввод с клавиатуры, обычно отображает символы, которые пользователь вводит в области клиента окна. Окно должно использовать курсор, чтобы указать положение в клиентской области, где появится следующий символ. Окно также должно создать и отобразить курсор, когда он получает фокус клавиатуры, и скрыть и уничтожить курсор, когда он теряет фокус. Окно может выполнять эти операции в обработке WM_SETFOCUS и WM_KILLFOCUS сообщений. Дополнительные сведения о каретах см. в разделе .
Отображение ввода клавиатуры
В этом разделе показано, как приложение может получать символы с клавиатуры, отображать их в клиентской области окна и обновлять положение курсора с каждым типизированным символом. В нем также показано, как перемещать курсор в ответ на нажатия клавиш СТРЕЛКА ВЛЕВО, СТРЕЛКА ВПРАВО, ГЛАВНАЯ и КОНЕЦ, а также как выделить текст в ответ на сочетание клавиш SHIFT+СТРЕЛКА ВПРАВО.
Во время обработки сообщения WM_CREATE процедура окна, показанная в примере, выделяет буфер 64K для хранения ввода клавиатуры. Он также извлекает метрики загруженного шрифта, сохраняя высоту и среднюю ширину символов в шрифте. Высота и ширина используются для обработки сообщения WM_SIZE для вычисления длины строки и максимального количества строк на основе размера клиентской области.
Процедура окна создает и отображает курсор при обработке сообщения WM_SETFOCUS. При обработке сообщения WM_KILLFOCUS она скрывает и удаляет подсказку.
При обработке сообщения WM_CHAR процедура окна отображает символы, сохраняет их в входном буфере и обновляет положение курсора. Процедура окна также преобразует символы табуляции в четыре последовательных пробела. Backspace, linefeed и escape-символы создают сигнал, но не обрабатываются в противном случае.
Процедура окна выполняет движения каретки влево, вправо, в конец и в начало при обработке сообщения WM_KEYDOWN. При обработке действия клавиши СТРЕЛКА ВПРАВО процедура окна проверяет состояние клавиши SHIFT, и если она удерживается, выбирается символ справа от курсора при его перемещении.
Обратите внимание, что следующий код написан таким образом, чтобы его можно было скомпилировать как Юникод, так и как ANSI. Если исходный код определяет ЮНИКОД, строки обрабатываются как символы Юникода; в противном случае они обрабатываются как символы ANSI.
#define BUFSIZE 65535
#define SHIFTED 0x8000
LONG APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HDC hdc; // handle to device context
TEXTMETRIC tm; // structure for text metrics
static DWORD dwCharX; // average width of characters
static DWORD dwCharY; // height of characters
static DWORD dwClientX; // width of client area
static DWORD dwClientY; // height of client area
static DWORD dwLineLen; // line length
static DWORD dwLines; // text lines in client area
static int nCaretPosX = 0; // horizontal position of caret
static int nCaretPosY = 0; // vertical position of caret
static int nCharWidth = 0; // width of a character
static int cch = 0; // characters in buffer
static int nCurChar = 0; // index of current character
static PTCHAR pchInputBuf; // input buffer
int i, j; // loop counters
int cCR = 0; // count of carriage returns
int nCRIndex = 0; // index of last carriage return
int nVirtKey; // virtual-key code
TCHAR szBuf[128]; // temporary buffer
TCHAR ch; // current character
PAINTSTRUCT ps; // required by BeginPaint
RECT rc; // output rectangle for DrawText
SIZE sz; // string dimensions
COLORREF crPrevText; // previous text color
COLORREF crPrevBk; // previous background color
size_t * pcch;
HRESULT hResult;
switch (uMsg)
{
case WM_CREATE:
// Get the metrics of the current font.
hdc = GetDC(hwndMain);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwndMain, hdc);
// Save the average character width and height.
dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;
// Allocate a buffer to store keyboard input.
pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,
BUFSIZE * sizeof(TCHAR));
return 0;
case WM_SIZE:
// Save the new width and height of the client area.
dwClientX = LOWORD(lParam);
dwClientY = HIWORD(lParam);
// Calculate the maximum width of a line and the
// maximum number of lines in the client area.
dwLineLen = dwClientX - dwCharX;
dwLines = dwClientY / dwCharY;
break;
case WM_SETFOCUS:
// Create, position, and display the caret when the
// window receives the keyboard focus.
CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
ShowCaret(hwndMain);
break;
case WM_KILLFOCUS:
// Hide and destroy the caret when the window loses the
// keyboard focus.
HideCaret(hwndMain);
DestroyCaret();
break;
case WM_CHAR:
// check if current location is close enough to the
// end of the buffer that a buffer overflow may
// occur. If so, add null and display contents.
if (cch > BUFSIZE-5)
{
pchInputBuf[cch] = 0x00;
SendMessage(hwndMain, WM_PAINT, 0, 0);
}
switch (wParam)
{
case 0x08: // backspace
case 0x0A: // linefeed
case 0x1B: // escape
MessageBeep((UINT) -1);
return 0;
case 0x09: // tab
// Convert tabs to four consecutive spaces.
for (i = 0; i < 4; i++)
SendMessage(hwndMain, WM_CHAR, 0x20, 0);
return 0;
case 0x0D: // carriage return
// Record the carriage return and position the
// caret at the beginning of the new line.
pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;
default: // displayable character
ch = (TCHAR) wParam;
HideCaret(hwndMain);
// Retrieve the character's width and output
// the character.
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam,
&nCharWidth);
TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY,
&ch, 1);
ReleaseDC(hwndMain, hdc);
// Store the character in the buffer.
pchInputBuf[cch++] = ch;
// Calculate the new horizontal position of the
// caret. If the position exceeds the maximum,
// insert a carriage return and move the caret
// to the beginning of the next line.
nCaretPosX += nCharWidth;
if ((DWORD) nCaretPosX > dwLineLen)
{
nCaretPosX = 0;
pchInputBuf[cch++] = 0x0D;
++nCaretPosY;
}
nCurChar = cch;
ShowCaret(hwndMain);
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_KEYDOWN:
switch (wParam)
{
case VK_LEFT: // LEFT ARROW
// The caret can move only to the beginning of
// the current line.
if (nCaretPosX > 0)
{
HideCaret(hwndMain);
// Retrieve the character to the left of
// the caret, calculate the character's
// width, then subtract the width from the
// current horizontal position of the caret
// to obtain the new position.
ch = pchInputBuf[--nCurChar];
hdc = GetDC(hwndMain);
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = max(nCaretPosX - nCharWidth,
0);
ShowCaret(hwndMain);
}
break;
case VK_RIGHT: // RIGHT ARROW
// Caret moves to the right or, when a carriage
// return is encountered, to the beginning of
// the next line.
if (nCurChar < cch)
{
HideCaret(hwndMain);
// Retrieve the character to the right of
// the caret. If it's a carriage return,
// position the caret at the beginning of
// the next line.
ch = pchInputBuf[nCurChar];
if (ch == 0x0D)
{
nCaretPosX = 0;
nCaretPosY++;
}
// If the character isn't a carriage
// return, check to see whether the SHIFT
// key is down. If it is, invert the text
// colors and output the character.
else
{
hdc = GetDC(hwndMain);
nVirtKey = GetKeyState(VK_SHIFT);
if (nVirtKey & SHIFTED)
{
crPrevText = SetTextColor(hdc,
RGB(255, 255, 255));
crPrevBk = SetBkColor(hdc,
RGB(0,0,0));
TextOut(hdc, nCaretPosX,
nCaretPosY * dwCharY,
&ch, 1);
SetTextColor(hdc, crPrevText);
SetBkColor(hdc, crPrevBk);
}
// Get the width of the character and
// calculate the new horizontal
// position of the caret.
GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwndMain);
break;
}
break;
case VK_UP: // UP ARROW
case VK_DOWN: // DOWN ARROW
MessageBeep((UINT) -1);
return 0;
case VK_HOME: // HOME
// Set the caret's position to the upper left
// corner of the client area.
nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;
case VK_END: // END
// Move the caret to the end of the text.
for (i=0; i < cch; i++)
{
// Count the carriage returns and save the
// index of the last one.
if (pchInputBuf[i] == 0x0D)
{
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;
// Copy all text between the last carriage
// return and the end of the keyboard input
// buffer to a temporary buffer.
for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');
// Retrieve the text extent and use it
// to set the horizontal position of the
// caret.
hdc = GetDC(hwndMain);
hResult = StringCchLength(szBuf, 128, pcch);
if (FAILED(hResult))
{
// TODO: write error handler
}
GetTextExtentPoint32(hdc, szBuf, *pcch,
&sz);
nCaretPosX = sz.cx;
ReleaseDC(hwndMain, hdc);
nCurChar = cch;
break;
default:
break;
}
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
break;
case WM_PAINT:
if (cch == 0) // nothing in input buffer
break;
hdc = BeginPaint(hwndMain, &ps);
HideCaret(hwndMain);
// Set the clipping rectangle, and then draw the text
// into it.
SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);
ShowCaret(hwndMain);
EndPaint(hwndMain, &ps);
break;
// Process other messages.
case WM_DESTROY:
PostQuitMessage(0);
// Free the input buffer.
GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwndMain, 0xAAAA);
break;
default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
}