Использование курсоров
В этом разделе приведены примеры кода для следующих задач:
- Создание и отображение курсора
- Скрытие курсора
- Уничтожение курсора
- Настройка времени мигания
- Обработка ввода с клавиатуры
Создание и отображение курсора
После получения фокуса клавиатуры окно должно создать и отобразить курсор. Используйте функцию CreateCaret для создания курсора в заданном окне. Затем можно вызвать Метод SetCaretPos , чтобы задать текущее положение курсора, и ShowCaret , чтобы сделать курсор видимым.
Система отправляет WM_SETFOCUS сообщение в окно, получающее фокус клавиатуры; Поэтому приложение должно создавать и отображать курсор при обработке этого сообщения.
HWND hwnd, // window handle
int x; // horizontal coordinate of cursor
int y; // vertical coordinate of cursor
int nWidth; // width of cursor
int nHeight; // height of cursor
char *lpszChar; // pointer to character
case WM_SETFOCUS:
// Create a solid black caret.
CreateCaret(hwnd, (HBITMAP) NULL, nWidth, nHeight);
// Adjust the caret position, in client coordinates.
SetCaretPos(x, y);
// Display the caret.
ShowCaret(hwnd);
break;
Чтобы создать курсор на основе точечных рисунков, необходимо указать дескриптор растрового изображения при использовании CreateCaret. Вы можете использовать графическое приложение для создания растрового рисунка и компилятор ресурсов для добавления растрового рисунка в ресурсы приложения. Затем приложение может использовать функцию LoadBitmap для загрузки дескриптора растрового рисунка. Например, можно заменить строку CreateCaret в предыдущем примере следующими строками, чтобы создать курсор точечного рисунка.
// Load the application-defined caret resource.
hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120));
// Create a bitmap caret.
CreateCaret(hwnd, hCaret, 0, 0);
Кроме того, можно использовать функцию CreateBitmap или CreateDIBitmap , чтобы получить дескриптор растрового изображения с курсором. Дополнительные сведения о растровых изображениях см. в разделе Растровые рисунки.
Если приложение указывает дескриптор растрового изображения, CreateCaret игнорирует параметры ширины и высоты. Растровое изображение определяет размер курсора.
Скрытие курсора
Всякий раз, когда приложение перерисовывает экран при обработке сообщения, отличного от WM_PAINT, оно должно сделать курсор невидимым с помощью функции HideCaret . Когда приложение завершит рисование, переименуйте курсор с помощью функции ShowCaret . Если приложение обрабатывает сообщение WM_PAINT , нет необходимости скрывать и повторно отображать курсор, так как эта функция выполняет это автоматически.
В следующем примере кода показано, как приложение скрывает курсор при рисовании символа на экране и при обработке сообщения WM_CHAR .
HWND hwnd, // window handle
HDC hdc; // device context
case WM_CHAR:
switch (wParam)
{
case 0x08:
// Process a backspace.
break;
case 0x09:
// Process a tab.
break;
case 0x0D:
// Process a carriage return.
break;
case 0x1B:
// Process an escape.
break;
case 0x0A:
// Process a linefeed.
break;
default:
// Hide the caret.
HideCaret(hwnd);
// Draw the character on the screen.
hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, x, y, lpszChar, 1);
ReleaseDC(hwnd, hdc);
// Display the caret.
ShowCaret(hwnd);
}
Если приложение несколько раз вызывает функцию HideCaret без вызова ShowCaret, курсор не будет отображаться, пока приложение не вызовет ShowCaret одинаковое количество раз.
Уничтожение курсора
Когда окно теряет фокус клавиатуры, система отправляет WM_KILLFOCUS сообщение в окно. Приложение должно уничтожить курсор при обработке этого сообщения с помощью функции DestroyCaret . В следующем коде показано, как уничтожить курсор в окне, в которое больше нет фокуса клавиатуры.
case WM_KILLFOCUS:
// The window is losing the keyboard focus, so destroy the caret.
DestroyCaret();
break;
Настройка времени мигания
В 16-разрядной версии Windows приложение на основе Windows может вызвать функцию GetCaretBlinkTime , чтобы сохранить текущее время мигания, а затем вызвать функцию SetCaretBlinkTime , чтобы настроить время мигания во время обработки сообщения WM_SETFOCUS . Приложение восстановит сохраненное время мигания для использования других приложений, вызвав SetCaretBlinkTime во время обработки сообщения WM_KILLFOCUS . Однако этот метод не работает в многопоточных средах. В частности, деактивация одного приложения не синхронизируется с активацией другого приложения, поэтому, если одно приложение зависает, другое приложение по-прежнему может быть активировано.
Приложения должны учитывать время мигания, выбранное пользователем. Функция SetCaretBlinkTime должна вызываться только приложением, которое позволяет пользователю задать время мигания.
Обработка ввода с клавиатуры
В следующем примере показано, как использовать курсор в простом текстовом редакторе. В примере обновляется положение курсора, когда пользователь вводит печатные символы и использует различные клавиши для перемещения по клиентской области.
#define TEXTMATRIX(x, y) *(pTextMatrix + (y * nWindowCharsX) + x)
// Global variables.
HINSTANCE hinst; // current instance
HBITMAP hCaret; // caret bitmap
HDC hdc; // device context
PAINTSTRUCT ps; // client area paint info
static char *pTextMatrix = NULL; // points to text matrix
static int nCharX, // width of char. in logical units
nCharY, // height of char. in logical units
nWindowX, // width of client area
nWindowY, // height of client area
nWindowCharsX, // width of client area
nWindowCharsY, // height of client area
nCaretPosX, // x-position of caret
nCaretPosY; // y-position of caret
static UINT uOldBlink; // previous blink rate
int x, y; // coordinates for text matrix
TEXTMETRIC tm; // font information
LONG APIENTRY MainWndProc(
HWND hwnd, // window handle
UINT message, // type of message
UINT wParam, // additional information
LONG lParam) // additional information
{
switch (message)
{
case WM_CREATE:
// Select a fixed-width system font, and get its text metrics.
hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
GetTextMetrics(hdc, &tm);
ReleaseDC(hwnd, hdc);
// Save the avg. width and height of characters.
nCharX = tm.tmAveCharWidth;
nCharY = tm.tmHeight;
return 0;
case WM_SIZE:
// Determine the width of the client area, in pixels
// and in number of characters.
nWindowX = LOWORD(lParam);
nWindowCharsX = max(1, nWindowX/nCharX);
// Determine the height of the client area, in
// pixels and in number of characters.
nWindowY = HIWORD(lParam);
nWindowCharsY = max(1, nWindowY/nCharY);
// Clear the buffer that holds the text input.
if (pTextMatrix != NULL)
free(pTextMatrix);
// If there is enough memory, allocate space for the
// text input buffer.
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) = ' ';
// Move the caret to the origin.
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
// Move all the characters that followed the
// deleted character (on the same line) one
// space back (to the left) in the matrix.
for (x = nCaretPosX; x < nWindowCharsX; x++)
TEXTMATRIX(x, nCaretPosY) =
TEXTMATRIX(x + 1, nCaretPosY);
// Replace the last character on the
// line with a space.
TEXTMATRIX(nWindowCharsX - 1,
nCaretPosY) = ' ';
// The application will draw outside the
// WM_PAINT message processing, so hide the caret.
HideCaret(hwnd);
// Redraw the line, adjusted for the
// deleted character.
hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, nCaretPosX * nCharX,
nCaretPosY * nCharY,
&TEXTMATRIX(nCaretPosX, nCaretPosY),
nWindowCharsX - nCaretPosX);
ReleaseDC(hwnd, hdc);
// Display the caret.
ShowCaret(hwnd);
break;
}
// Adjust the caret position based on the
// virtual-key processing.
SetCaretPos(nCaretPosX * nCharX,
nCaretPosY * nCharY);
return 0;
case WM_CHAR:
switch (wParam)
{
case 0x08: // Backspace
// Move the caret back one space, and then
// process this like the DEL key.
if (nCaretPosX > 0)
{
nCaretPosX--;
SendMessage(hwnd, WM_KEYDOWN,
VK_DELETE, 1L);
}
break;
case 0x09: // Tab
// Tab stops exist every four spaces, so add
// spaces until the user hits the next tab.
do
{
SendMessage(hwnd, WM_CHAR, ' ', 1L);
} while (nCaretPosX % 4 != 0);
break;
case 0x0D: // Carriage return
// Go to the beginning of the next line.
// The bottom line wraps around to the top.
nCaretPosX = 0;
if (++nCaretPosY == nWindowCharsY)
nCaretPosY = 0;
break;
case 0x1B: // Escape
case 0x0A: // Linefeed
MessageBeep((UINT) -1);
break;
default:
// Add the character to the text buffer.
TEXTMATRIX(nCaretPosX, nCaretPosY) =
(char) wParam;
// The application will draw outside the
// WM_PAINT message processing, so hide the caret.
HideCaret(hwnd);
// Draw the character on the screen.
hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, nCaretPosX * nCharX,
nCaretPosY * nCharY,
&TEXTMATRIX(nCaretPosX, nCaretPosY), 1);
ReleaseDC(hwnd, hdc);
// Display the caret.
ShowCaret(hwnd);
// Prepare to wrap around if you reached the
// end of the line.
if (++nCaretPosX == nWindowCharsX)
{
nCaretPosX = 0;
if (++nCaretPosY == nWindowCharsY)
nCaretPosY = 0;
}
break;
}
// Adjust the caret position based on the
// character processing.
SetCaretPos(nCaretPosX * nCharX,
nCaretPosY * nCharY);
return 0;
case WM_PAINT:
// Draw all the characters in the buffer, line by line.
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:
// The window has the input focus. Load the
// application-defined caret resource.
hCaret = LoadBitmap(hinst, MAKEINTRESOURCE(120));
// Create the caret.
CreateCaret(hwnd, hCaret, 0, 0);
// Adjust the caret position.
SetCaretPos(nCaretPosX * nCharX,
nCaretPosY * nCharY);
// Display the caret.
ShowCaret(hwnd);
break;
case WM_KILLFOCUS:
// The window is losing the input focus,
// so destroy the caret.
DestroyCaret();
break;
default:
return DefWindowProc(hwnd, message, wParam, lParam);
}
return NULL;
}