Пример в этом разделе показывает, как прикладная программа может принимать символы от клавиатуры, отображать их в рабочей области окна и обновлять позицию каретки с каждым напечатанным символом. Он также демонстрирует, и как переместить каретку в ответ на нажатие клавиш 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; } |