Пример в этом разделе показывает, как прикладная программа может принимать символы от клавиатуры, отображать их в рабочей области окна и обновлять позицию каретки с каждым напечатанным символом. Он также демонстрирует, и как переместить каретку в ответ на нажатие клавиш LEFT ARROW (стрелка влево), RIGHT ARROW (стрелка вправо), HOME (вверх документа) и END (в конец документа), и показывает, как выделить выбранный текст в ответ на нажатие комбинации клавиш SHIFT+RIGHT ARROW.
В ходе обработки сообщения WM_CREATE, оконная процедура, показанная в примере, назначает буфер 64КБ для сохранения ввода данных с клавиатуры. Она также извлекает данные о метрике в настоящее время загруженного шрифта, сохраняя высоту и среднюю ширину символов в шрифте. Высота и ширина используются при обработке сообщения WM_SIZE, чтобы вычислить длину строки и максимальное число строк, основываясь на размере рабочей области.
Оконная процедура создает и показывает на экране каретку при обработке сообщения WM_SETFOCUS. Она скрывает и удаляет каретку при обработке сообщения WM_KILLFOCUS.
При обработке сообщения WM_CHAR, оконная процедура отображает символы, сохраненные ею в буфере вводимой информации, и обновляет позицию каретки. Оконная процедура также преобразует и символы табуляции в четыре последовательных пробела. Возврат на один символ, перевод строки и знаки перехода генерируют звуковой сигнал, в противном случае они не обрабатываются.
Оконная процедура исполняет движения каретки влево, вправо, в конец и в исходную позицию при обработке сообщения WM_KEYDOWN. При обработке действия клавиши RIGHT ARROW (стрелки "вправо"), оконная процедура проверяет состояние клавиши SHIFT и, если она нажата, выбирает символ справа от каретки, поскольку каретка перемещается.
|
Обратите внимание! на то, что нижеследующий код написан так, чтобы его можно было откомпилировать или как Уникод ™ или как ANSI. Если исходный текст определяет Уникод, строки обрабатываются как символы Уникода; иначе, они обрабатываются как символы 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: // возврат каретки
case 0x0A: // перевод строки
case 0x1B: // escape
MessageBeep((UINT) -1);
return 0;
case 0x09: // табуляция
// Преобразование табуляции в четыре последовательных пробела.
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);
// Сохранение символа в буфере.
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: // LEFT ARROW
// Каретка может перемещаться только в начало
// текущей строки.
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: // Клавиша RIGHT ARROW (стрелка вправо)
// Каретка перемещается вправо или, когда
// определяется перевод каретки, чтобы
// начать следующую строку.
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: // Клавиша UP ARROW
case VK_DOWN: // Клавиша DOWN ARROW
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))
{
// Добавим код, который завершается ошибкой.
// безопасно насколько возможно.
return;
}
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;
}
|