Работа с клавиатурой
Страница 6. Отображение ввода с клавиатуры


 

Отображение ввода с клавиатуры

Ниже представлен пример, который демонстрирует, как приложение может получать введённые символы с клавиатуры, отображать их в клиентской области окна, и обновлять положение текстового курсора с каждым введённым символом. Так же в примере показывается как перемещать каретку в ответ на следующие клавиши: СТРЕЛКА ВЛЕВО, СТРЕЛКА ВПРАВО, HOME, и END, а так же как подсветить выделенный текст в ответ на комбинацию клавиш SHIFT+СТРЕЛКА ВПРАВО.

В обработчике сообщения WM_CREATE оконная процедура выделяет буфер объёмом 64K для хранения ввода с клавиатуры. Так же, пример получает текущие настройки шрифта, сохраняя высоту и среднюю ширину символов. Высота и ширина используются в обработчике WM_SIZE, чтобы вычислить длину строки и максимальное количество строк в клиентской области.

В обработчике WM_SETFOCUS, оконная процедура создаёт и отображает каретку, а в обработчике WM_KILLFOCUS скрывает и удаляет её.

В обработчике WM_CHAR, отображаются символы из буфера ввода, и изменение координаты каретки. Так же оконная процедура преобразует символы табуляции в четыре последовательных пробела. Символы Backspace, перевод строки и escape генерируют звуковой сигнал (beep).

В обработчике сообщения WM_KEYDOWN обрабатываются перемещения каретки влево, вправо, в конец (end) и в начало (home). При обработке правой стрелки, оконная процедура проверяет состояние клавиши SHIFT и, если она нажата, то выделяет символ справа от кратки при её перемещении.

Обратите внимание, что приведённый код будет работать как в Unicode, так и в ANSI кодировках.

Пример:

#define BUFSIZE 65535
#define SHIFTED 0x8000

LONG APIENTRY MainWndProc(HWND hwndMain, UINT uMsg,
WPARAM wParam, LPARAM lParam)
{
HDC hdc; // дескриптор контекста устройства
TEXTMETRIC tm; // структура с параметрами текста
static DWORD dwCharX; // средняя ширина символов
static DWORD dwCharY; // высота символов
static DWORD dwClientX; // ширина клиентской области
static DWORD dwClientY; // Высота клиентской области
static DWORD dwLineLen; // длина строки
static DWORD dwLines; // кол-во строк в клиентской области
static int nCaretPosX = 0; // горизонтальная координата каретки
static int nCaretPosY = 0; // вертикальная координата каретки
static int nCharWidth = 0; // ширина символа
static int cch = 0; // кол-во символов в буфере
static int nCurChar = 0; // номер текущего символа
static PTCHAR pchInputBuf; // буфер ввода
int i, j; // счётчики цикла
int cCR = 0; // счётчик возвратов каретки
int nCRIndex = 0; // номер последнего возврата каретки
int nVirtKey; // код виртуальной клавиши
TCHAR szBuf[128]; // временный буфер
TCHAR ch; // текущий символ
PAINTSTRUCT ps; // требуется для BeginPaint
RECT rc; // прямоугольник для DrawText
SIZE sz; // размерность строки
COLORREF crPrevText; // предыдущий цвет текста
COLORREF crPrevBk; // предыдущий цвет фона
size_t * pcch;
HRESULT hResult;

switch (uMsg)
{
case WM_CREATE:

// Получаем параметры текущего шрифта.

hdc = GetDC(hwndMain);
GetTextMetrics(hdc, &tm);
ReleaseDC(hwndMain, hdc);

// Сохраняем среднюю ширину и высоту символа.

dwCharX = tm.tmAveCharWidth;
dwCharY = tm.tmHeight;

// Выделяем буфер для хранения ввода с клавиатуры.

pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,
BUFSIZE * sizeof(TCHAR));
return 0;

case WM_SIZE:

// Сохраняем новую ширину и высоту клиентской области.

dwClientX = LOWORD(lParam);
dwClientY = HIWORD(lParam);

// Вычисляем максимальную ширину строки и максимальное
// количество строк в клиентской области.

dwLineLen = dwClientX - dwCharX;
dwLines = dwClientY / dwCharY;
break;


case WM_SETFOCUS:

// Когда окно получает фокус, то создаём, позиционируем
// и отображаем каретку.

CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);
SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
ShowCaret(hwndMain);
break;

case WM_KILLFOCUS:

// Когда окно теряет фокус, то скрываем и
// уничтожаем каретку.

HideCaret(hwndMain);
DestroyCaret();
break;

case WM_CHAR:
// Проверяем, не находится ли текущее положение близко
// к концу буфера, чтобы не произошло его переполнение.
// Если так, то добавляем ноль и отображаем содержимое.
if (cch > BUFSIZE-5)
{
pchInputBuf[cch] = 0x00;
SendMessage(hwndMain, WM_PAINT, 0, 0);
}
switch (wParam)
{
case 0x08: // backspace
case 0x0A: // перевод строки
case 0x1B: // esc
MessageBeep((UINT) -1);
return 0;

case 0x09: // tab

// Преобразуем табы в четыре последовательных пробела.

for (i = 0; i < 4; i++)
SendMessage(hwndMain, WM_CHAR, 0x20, 0);
return 0;

case 0x0D: // возврат каретки

// Записываем возврат каретки и её позицию
// в начале новой строки.

pchInputBuf[cch++] = 0x0D;
nCaretPosX = 0;
nCaretPosY += 1;
break;

default: // отображаемые символы

ch = (TCHAR) wParam;
HideCaret(hwndMain);

// Получаем ширину сиволов и выводим
// символ.

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;

// Вычисляем новую горизонтальную координат каретки.
// Если координата достигла максимума, то вставляем
// перевод каретки и перемещаем каретку
// в начало следующей строки.

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: // стрелка влево

// Каретка может быть перемещена только
// в начало текущей строки.

if (nCaretPosX > 0)
{
HideCaret(hwndMain);

// Получаем символ слева от каретки, вычисляем
// ширину символов, а затем вычитаем ширину
// из текущей горизонтальной координаты
// каретки, чтобы получить новую координату

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: // стрелка вправо

// Перемещаем каретку вправо или, если достугнут
// перевод каретки, в начало следующей строки.

if (nCurChar < cch)
{
HideCaret(hwndMain);

// Получаем символ справа от каретки.
// Если это перевод каретки, то перемещаем
// курсор в начало следующей строки.

ch = pchInputBuf[nCurChar];
if (ch == 0x0D)
{
nCaretPosX = 0;
nCaretPosY++;
}

// Если символ не является переводом каретки,
// то проверяем, не нажата ли клавиша SHIFT.
// Если так, то инвертируем цвета текста
// и выводим символ.

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);
}

// Получаем ширину символа и вычисляем
// новую горизонтальную координату каретки.

GetCharWidth32(hdc, ch, ch, &nCharWidth);
ReleaseDC(hwndMain, hdc);
nCaretPosX = nCaretPosX + nCharWidth;
}
nCurChar++;
ShowCaret(hwndMain);
break;
}
break;

case VK_UP: // стрелка вверх
case VK_DOWN: // стрелка вниз
MessageBeep((UINT) -1);
return 0;

case VK_HOME: // HOME

// Устанавливаем каретку в верхний левый
// угол клиентской области.

nCaretPosX = nCaretPosY = 0;
nCurChar = 0;
break;

case VK_END: // END

// Перемещаем каретку в конец текста.

for (i=0; i < cch; i++)
{
// Считаем возвраты каретки и сохраняем
// их номера.

if (pchInputBuf[i] == 0x0D)
{
cCR++;
nCRIndex = i + 1;
}
}
nCaretPosY = cCR;

// Копируем весь текст между последним возвратом
// каретки и окончанием ввода с клавиатуры
// во временный буфер.

for (i = nCRIndex, j = 0; i < cch; i++, j++)
szBuf[j] = pchInputBuf[i];
szBuf[j] = TEXT('\0');

// Устанавливаем горизонтальную координату каретки.

hdc = GetDC(hwndMain);
hResult = StringCchLength(szBuf, 128, pcch);
if (FAILED(hResult))
{
// обработчик ошибки
}
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) // в буфере ввода ничего нет
break;

hdc = BeginPaint(hwndMain, &ps);
HideCaret(hwndMain);

// Устанавливаем границы прямоугольника, а затем
// рисуем в нём текст.

SetRect(&rc, 0, 0, dwLineLen, dwClientY);
DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);

ShowCaret(hwndMain);
EndPaint(hwndMain, &ps);
break;

// Обрабатываем другие сообщения.

case WM_DESTROY:
PostQuitMessage(0);

// Освобождаем буфер ввода.

GlobalFree((HGLOBAL) pchInputBuf);
UnregisterHotKey(hwndMain, 0xAAAA);
break;

default:
return DefWindowProc(hwndMain, uMsg, wParam, lParam);
}
return NULL;
 
« Предыдущая статья   Следующая статья »