Пример ниже демонстрирует, как использовать каретку в простом редакторе текста. Пример обновляет позицию каретки, поскольку пользователь вводит с клавиатуры печатный знаки и использует различные клавиши, чтобы перемещаться по рабочей области.
Демонстрационный пример
#define TEXTMATRIX(x, y) *(pTextMatrix + (y * nWindowCharsX) + x) // Глобальные переменные. HINSTANCE hinst; // текущий экземпляр HBITMAP hCaret; // точечный рисунок каретки HDC hdc; // контекст устройства PAINTSTRUCT ps; // рабочая область для рисования static char *pTextMatrix = NULL; // указатель на матрицу текста static int nCharX, // ширина знака в логических ед. nCharY, // высота знака в логических ед. nWindowX, // ширина рабочей области nWindowY, // высота рабочей области nWindowCharsX, // ширина знакоместа nWindowCharsY, // высота знакоместа nCaretPosX, // x-позиция каретки nCaretPosY; // y-позиция каретки static UINT uOldBlink; // предыдущая частота мерцания int x, y; // координаты матрицы текста TEXTMETRIC tm; // информация о шрифте LONG APIENTRY MainWndProc( HWND hwnd, // дескриптор окна UINT message, // тип сообщения UINT wParam, // дополнительная информация LONG lParam) // дополнительная информация { switch (message) { case WM_CREATE: // Выбираем моноширинный системный шрифт, и получаем его матрицы. hdc = GetDC(hwnd); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); GetTextMetrics(hdc, &tm); ReleaseDC(hwnd, hdc); // Сохраняем среднюю ширину и высоту знаков. nCharX = tm.tmAveCharWidth; nCharY = tm.tmHeight; return 0; case WM_SIZE: // Выясняем ширину рабочей области, в пикселях // и в количестве знаков. nWindowX = LOWORD(lParam); nWindowCharsX = max(1, nWindowX/nCharX); // Выясняем ширину рабочей области, в пикселях // и в количестве знаков. nWindowY = HIWORD(lParam); nWindowCharsY = max(1, nWindowY/nCharY); // Очищаем буфер, который накапливает вводимый текст. if (pTextMatrix != NULL) free(pTextMatrix); // Если имеется достаточно памяти, выделяем част ее // для текста в буфере ввода. pTextMatrix = malloc(nWindowCharsX * nWindowCharsY); if (pTextMatrix == NULL) ErrorHandler("Not enough memory."); else for (y = 0; y < nWindowCharsY; y++) for (x = 0; x < nWindowCharsX; x++) TEXTMATRIX(x, y) = ' '; // Передвигаем каретку в начало координат. SetCaretPos(0, 0); return 0; case WM_KEYDOWN: switch (wParam) { case VK_HOME: // В начало документа (Home) nCaretPosX = 0; break; case VK_END: // В конец документа (End) nCaretPosX = nWindowCharsX - 1; break; case VK_PRIOR: // На страницу вверх (Page Up) nCaretPosY = 0; break; case VK_NEXT: // На страницу вниз (Page Down) nCaretPosY = nWindowCharsY -1; break; case VK_LEFT: // Стрелка влево (Left arrow) nCaretPosX = max(nCaretPosX - 1, 0); break; case VK_RIGHT: // Стрелка вправо (Right arrow) nCaretPosX = min(nCaretPosX + 1, nWindowCharsX - 1); break; case VK_UP: // Стрелка вверх (Up arrow) nCaretPosY = max(nCaretPosY - 1, 0); break; case VK_DOWN: // Стрелка вниз (Down arrow) nCaretPosY = min(nCaretPosY + 1, nWindowCharsY - 1); break; case VK_DELETE: // Удаление (Delete) // Перемещаем все символы, которые следуют за // удаленным символом (на той же самой строке) на один // пробел назад (влево) в матрице. for (x = nCaretPosX; x < nWindowCharsX; x++) TEXTMATRIX(x, nCaretPosY) = TEXTMATRIX(x + 1, nCaretPosY); // Заменяем последний символ в // строке пробелом. TEXTMATRIX(nWindowCharsX - 1, nCaretPosY) = ' '; // Приложение будет рисовать не в ответ на сообщение // WM_PAINT, так что каретку скроем. HideCaret(hwnd); // Перерисовываем строку, откорректировав на // удаленный символ. hdc = GetDC(hwnd); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); TextOut(hdc, nCaretPosX * nCharX, nCaretPosY * nCharY, &TEXTMATRIX(nCaretPosX, nCaretPosY), nWindowCharsX - nCaretPosX); ReleaseDC(hwnd, hdc); // Показываем на экране каретку. ShowCaret(hwnd); break; } // Корректируем позицию каретки опираясь на обработку // кода виртуальной клавиши. SetCaretPos(nCaretPosX * nCharX, nCaretPosY * nCharY); return 0; case WM_CHAR: switch (wParam) { case 0x08: // Возврат на один знак (Backspace) // Перемещаем каретку назад на один пробел, а затем // обрабатываем аналогично клавише DEL. if (nCaretPosX > 0) { nCaretPosX--; SendMessage(hwnd, WM_KEYDOWN, VK_DELETE, 1L); } break; case 0x09: // Табуляция (Tab) // Существующий шаг табуляции равен четырем пробелам, так что // добавим пробелы, пока пользователь не нажал след. Tab. do { SendMessage(hwnd, WM_CHAR, ' ', 1L); } while (nCaretPosX % 4 != 0); break; case 0x0D: // Возврат каретки // Перейдем в начало следующей строки. // Нижняя строка переносит по словам обратно вверх. nCaretPosX = 0; if (++nCaretPosY == nWindowCharsY) nCaretPosY = 0; break; case 0x1B: // Переход case 0x0A: // Перевод строки MessageBeep((UINT) -1); break; default: // Добавим символ в буфер текста. TEXTMATRIX(nCaretPosX, nCaretPosY) = (char) wParam; // Приложение будет рисовать не в ответ на сообщение // WM_PAINT, так что каретку скроем. HideCaret(hwnd); // Рисуем символ на экране. hdc = GetDC(hwnd); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); TextOut(hdc, nCaretPosX * nCharX, nCaretPosY * nCharY, &TEXTMATRIX(nCaretPosX, nCaretPosY), 1); ReleaseDC(hwnd, hdc); // Показываем на экране каретку. ShowCaret(hwnd); // Подготовим перенос по словам, если вы достигли // конца строки. if (++nCaretPosX == nWindowCharsX) { nCaretPosX = 0; if (++nCaretPosY == nWindowCharsY) nCaretPosY = 0; } break; } // Корректируем позицию каретки, опираясь // на обработку кода символа. SetCaretPos(nCaretPosX * nCharX, nCaretPosY * nCharY); return 0; case WM_PAINT: // Рисуем все символы в буфере, строка за строкой. hdc = BeginPaint(hwnd, &ps); SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT)); for (y = 0; y < nWindowCharsY; y++) TextOut(hdc, 0, y * nCharY, &TEXTMATRIX(0, y), nWindowCharsX); EndPaint(hwnd, &ps); case WM_SETFOCUS: // Окно имеет фокус ввода. Загрузим определяемый программой // ресурс каретки. hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120)); // Создадим каретку. CreateCaret(hwnd, hCaret, 0, 0); // Откорректируем позицию каретки. SetCaretPos(nCaretPosX * nCharX, nCaretPosY * nCharY); // Покажем на экране позицию каретки. ShowCaret(hwnd); break; case WM_KILLFOCUS: // Окно потеряло фокус ввода, // так что уничтожим каретку. DestroyCaret(); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return NULL; } |