var
hOldFont: HFONT;
strBuf: String;
hEditDC: HDC;
begin
case msg of
WM_CLOSE:
if (hWnd = hMainWnd) then PostQuitMessage(0);
WM_CTLCOLOREDIT: //Сообщения от Edit перед перерисовкой
begin
//Зададим тексту Edit выбранный цвет
hEditDC:= HDC(wParam);
SetTextColor(hEditDC, curColor);
GetCurrentObject(hEditDC, OBJ_BRUSH);
end;
WM_COMMAND:
if (HIWORD(wParam) = BN_CLICKED) then
begin
//Определим, какая кнопка нажата
case LOWORD(wParam) of
1001: //Открытие файла
begin
SetText(2 001, 'Открыт файл:'+ #13 + #10 +
ShowOpen('Все файлы|*.*||'));
end;
1002: //Сохранение файла
begin
SetText(2001, 'Путь для сохранения:'+ #13 + #10 +
ShowSave('Все файлы|*.*||'));
end;
1003: //Выбор цвета
begin
curColor:= ShowChooseColor(curColor);
Str(curColor, strBuf);
SetText(2001, 'Выбранный цвет:'+ #13 + #10 + strBuf);
end;
1004: //Выбор шрифта
begin
if (ShowChooseFont(font) = True) then
begin
//Замена шрифта в Edit
hOldFont:= HFONT(
SendDlgItemMessage(hMainWnd,2001,WM_GETFONT, 0,0));
hCurFont:= CreateFontIndirect(font);
SendDlgItemMessage(hMainWnd, 2001, WM_SETFONT,
Integer(hCurFont), Integer(True));
SetText(2001, 'Текст, записанный выбранным шрифтом');
if (hOldFont <> 0) then DeleteObject(hOldFont);
end;
end;
1010: //Выбор папки
begin
SetText(2 001, 'Выбранная папка:'+ #13 + #10 +
ShowChooseFolder());
end;
1011: //Подключение сетевого ресурса
begin
ShowConnection();
end;
1012: //Отключение сетевого ресурса
begin
ShowDisconnect();
end;
1013: //Окно "О программе"
begin
ShowAbout('Standart windows',
'Демонстрация использования стандартных '+
'окон из чистого API-приложения');
end;
end;
end;
else
begin
//Обработка по умолчанию
WindowFunc:= DefWindowProc(hWnd, msg, wParam, lParam);
Exit;
end;
end;
WindowFunc:= S_OK; //Сообщение обработано
end;
Обработка сообщений здесь довольно проста, за исключением изменения шрифта текстового поля. Обратите внимание на следующий отрывок листинга 2.34:
//Замена шрифта в Edit
hOldFont:= HFONT(SendDlgItemMessage(hMainWnd,2001,WM_GETFONT, 0,0));
hCurFont:= CreateFontIndirect(font);
SendDlgItemMessage(hMainWnd, 2001, WM_SETFONT,
Integer(hCurFont), Integer(True));
SetEdit Text(2001, 'Текст, записанный выбранным шрифтом');
if (hOldFont <> 0) then DeleteObject(hOldFont);
Этот довольно объемный фрагмент кода всего лишь заменяет шрифт в текстовом поле. Подобную операцию можно использовать для задания шрифта любого элемента управления. В частности, в приведенных в этой главе примерах текст на кнопках, надписях и т. д. выглядит довольно невзрачно потому, что используется системный шрифт, установленный по умолчанию.
Способ, которым можно установить шрифт всех элементов управления окна, рассмотрен ниже. Остался еще один существенный момент: не забывайте удалять объекты GDI (в данном случае – шрифт) после того, как они стали не нужны. Дело в том, что приложение может содержать не более 65 ООО объектов GDI, и при наличии так называемой «утечки» ресурсов GDI может произойти аварийное завершение приложения из-за исчерпания лимита ресурсов GDI.
Установка шрифта элементов управления
Глава 3
Мышь
Координаты и указатель мыши
Захват указателя мыши
Ограничение области перемещения указателя
Изменение назначений кнопок мыши
Подсчет расстояния, пройденного указателем мыши
Подсвечивание элементов управления
Клавиатура
Получение информации о клавиатуре
Опрос клавиатуры
hOldFont: HFONT;
strBuf: String;
hEditDC: HDC;
begin
case msg of
WM_CLOSE:
if (hWnd = hMainWnd) then PostQuitMessage(0);
WM_CTLCOLOREDIT: //Сообщения от Edit перед перерисовкой
begin
//Зададим тексту Edit выбранный цвет
hEditDC:= HDC(wParam);
SetTextColor(hEditDC, curColor);
GetCurrentObject(hEditDC, OBJ_BRUSH);
end;
WM_COMMAND:
if (HIWORD(wParam) = BN_CLICKED) then
begin
//Определим, какая кнопка нажата
case LOWORD(wParam) of
1001: //Открытие файла
begin
SetText(2 001, 'Открыт файл:'+ #13 + #10 +
ShowOpen('Все файлы|*.*||'));
end;
1002: //Сохранение файла
begin
SetText(2001, 'Путь для сохранения:'+ #13 + #10 +
ShowSave('Все файлы|*.*||'));
end;
1003: //Выбор цвета
begin
curColor:= ShowChooseColor(curColor);
Str(curColor, strBuf);
SetText(2001, 'Выбранный цвет:'+ #13 + #10 + strBuf);
end;
1004: //Выбор шрифта
begin
if (ShowChooseFont(font) = True) then
begin
//Замена шрифта в Edit
hOldFont:= HFONT(
SendDlgItemMessage(hMainWnd,2001,WM_GETFONT, 0,0));
hCurFont:= CreateFontIndirect(font);
SendDlgItemMessage(hMainWnd, 2001, WM_SETFONT,
Integer(hCurFont), Integer(True));
SetText(2001, 'Текст, записанный выбранным шрифтом');
if (hOldFont <> 0) then DeleteObject(hOldFont);
end;
end;
1010: //Выбор папки
begin
SetText(2 001, 'Выбранная папка:'+ #13 + #10 +
ShowChooseFolder());
end;
1011: //Подключение сетевого ресурса
begin
ShowConnection();
end;
1012: //Отключение сетевого ресурса
begin
ShowDisconnect();
end;
1013: //Окно "О программе"
begin
ShowAbout('Standart windows',
'Демонстрация использования стандартных '+
'окон из чистого API-приложения');
end;
end;
end;
else
begin
//Обработка по умолчанию
WindowFunc:= DefWindowProc(hWnd, msg, wParam, lParam);
Exit;
end;
end;
WindowFunc:= S_OK; //Сообщение обработано
end;
Обработка сообщений здесь довольно проста, за исключением изменения шрифта текстового поля. Обратите внимание на следующий отрывок листинга 2.34:
//Замена шрифта в Edit
hOldFont:= HFONT(SendDlgItemMessage(hMainWnd,2001,WM_GETFONT, 0,0));
hCurFont:= CreateFontIndirect(font);
SendDlgItemMessage(hMainWnd, 2001, WM_SETFONT,
Integer(hCurFont), Integer(True));
SetEdit Text(2001, 'Текст, записанный выбранным шрифтом');
if (hOldFont <> 0) then DeleteObject(hOldFont);
Этот довольно объемный фрагмент кода всего лишь заменяет шрифт в текстовом поле. Подобную операцию можно использовать для задания шрифта любого элемента управления. В частности, в приведенных в этой главе примерах текст на кнопках, надписях и т. д. выглядит довольно невзрачно потому, что используется системный шрифт, установленный по умолчанию.
Способ, которым можно установить шрифт всех элементов управления окна, рассмотрен ниже. Остался еще один существенный момент: не забывайте удалять объекты GDI (в данном случае – шрифт) после того, как они стали не нужны. Дело в том, что приложение может содержать не более 65 ООО объектов GDI, и при наличии так называемой «утечки» ресурсов GDI может произойти аварийное завершение приложения из-за исчерпания лимита ресурсов GDI.
Установка шрифта элементов управления
Существует множество способов установки шрифта текста, отображаемого в элементах управления. Можно, например, при создании элемента управления посылать ему сообщение WM_SETFONT, передавая дескриптор (HFONT) созданного ранее объекта шрифта. В таком случае код создания и установки шрифта элементов управления (с использованием рассмотренных в этой главе функций) может выглядеть, как показано в листинге 2.35.
font:= CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, 'Courier new');
//Создание элементов управления
ctrl:= CreateButt on(20, 30, 70, 30, 1001, 'Кнопка 1');
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateButt on(100, 30, 70, 30, 1002,'Кнопка 2');
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateCheck(210, 30, 180, 20, 2001, 'Флажок 1');
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateCheck(210, 60, 180, 20, 2001, 'Флажок 2', True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateOpti on(210, 100, 180, 20, 3001, 'Переключатель 1', True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateOpti on(210,130,180,20,3002, 'Переключатель 2', False, True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateOpti on(210, 160, 180, 20, 3003, 'Переключатель 3', True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
//Запуск цикла обработки сообщений
while (Longint(GetMessage(mess, 0, 0, 0)) <> 0)
do begin
TranslateMessage(mess);
DispatchMessage(mess);
end;
//Удаление шрифта
DeleteObject(font);
Вид окна с элементами управления, шрифт которых установлен любым из рассмотренных способов, показан на рис. 2.9.
Рис. 2.9. Шрифт элементов управления, отличный от системного
Способ задания шрифта, приведенный в листинге 2.35, легко реализовать. Его существенным недостатком является двукратное увеличение количества строк кода, выполняющих создание элементов управления. Для окон, содержащих большое количество элементов управления, можно предложить более универсальный способ (листинг 2.36).
font:= CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, 'Courier new');
//Создание элементов управления
CreateButton(20, 30, 70, 30, 1001, 'Кнопка 1');
CreateButton(100, 30, 70, 30, 1002,'Кнопка 2');
CreateCheck(210, 30, 180, 20, 2001, 'Флажок 1');
CreateCheck(210, 60, 180, 20, 2001, 'Флажок 2', True);
CreateOption(210, 100, 180, 20, 3001, 'Переключатель 1', True);
CreateOption(210, 130, 180, 20, 3002, 'Переключатель 2', False, True);
CreateOption(210, 160, 180, 20, 3003, 'Переключатель 3', True);
//Установка шрифта элементов управления
EnumChildWindows(hMainWnd, Addr(EnumFunc), font);
//Запуск цикла обработки сообщений
while (Longint(GetMessage(mess, 0, 0, 0)) <> 0)
do begin
TranslateMessage(mess);
DispatchMessage(mess);
end;
DeleteObject(font);
Собственно, за установление шрифта отвечает в приведенном листинге только одна строка:
EnumChildWindows(hMainWnd, Addr(EnumFunc), font);
При этом также нужно определить функцию обратного вызова (в данном случае это функция EnumFunc), которая будет вызываться по одному разу для каждого дочернего окна. В данном примере функция EnumFunc имеет следующий вид (листинг 2.37).
begin
SendMessage(wnd, WM_SETFONT, WPARAM(param), LPARAM(True));
EnumFunc:= True; //Продолжать перечисление
end;
В принципе, имя этой функции и названия параметров могут быть любыми, а вот типы параметров, возвращаемого значения и способ вызова функции должны быть именно такими, какие представлены в листинге 2.37. Функция должна возвращать True, если нужно продолжать перечисление окон, и False – в противном случае. Значение, которое было передано в качестве третьего параметра API-функции EnumChildWindows, передается в функцию обратного вызова. В данном случае этим параметром является дескриптор шрифта.
Листинг 2.35. Установка шрифта во время создания элементов управления
//Шрифт для элементов управленияfont:= CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, 'Courier new');
//Создание элементов управления
ctrl:= CreateButt on(20, 30, 70, 30, 1001, 'Кнопка 1');
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateButt on(100, 30, 70, 30, 1002,'Кнопка 2');
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateCheck(210, 30, 180, 20, 2001, 'Флажок 1');
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateCheck(210, 60, 180, 20, 2001, 'Флажок 2', True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateOpti on(210, 100, 180, 20, 3001, 'Переключатель 1', True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateOpti on(210,130,180,20,3002, 'Переключатель 2', False, True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
ctrl:= CreateOpti on(210, 160, 180, 20, 3003, 'Переключатель 3', True);
SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
//Запуск цикла обработки сообщений
while (Longint(GetMessage(mess, 0, 0, 0)) <> 0)
do begin
TranslateMessage(mess);
DispatchMessage(mess);
end;
//Удаление шрифта
DeleteObject(font);
Вид окна с элементами управления, шрифт которых установлен любым из рассмотренных способов, показан на рис. 2.9.
Рис. 2.9. Шрифт элементов управления, отличный от системного
Способ задания шрифта, приведенный в листинге 2.35, легко реализовать. Его существенным недостатком является двукратное увеличение количества строк кода, выполняющих создание элементов управления. Для окон, содержащих большое количество элементов управления, можно предложить более универсальный способ (листинг 2.36).
Листинг 2.36. Установка шрифта перебором элементов управления
//Шрифт для элементов управленияfont:= CreateFont(16, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
OUT_CHARACTER_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH, 'Courier new');
//Создание элементов управления
CreateButton(20, 30, 70, 30, 1001, 'Кнопка 1');
CreateButton(100, 30, 70, 30, 1002,'Кнопка 2');
CreateCheck(210, 30, 180, 20, 2001, 'Флажок 1');
CreateCheck(210, 60, 180, 20, 2001, 'Флажок 2', True);
CreateOption(210, 100, 180, 20, 3001, 'Переключатель 1', True);
CreateOption(210, 130, 180, 20, 3002, 'Переключатель 2', False, True);
CreateOption(210, 160, 180, 20, 3003, 'Переключатель 3', True);
//Установка шрифта элементов управления
EnumChildWindows(hMainWnd, Addr(EnumFunc), font);
//Запуск цикла обработки сообщений
while (Longint(GetMessage(mess, 0, 0, 0)) <> 0)
do begin
TranslateMessage(mess);
DispatchMessage(mess);
end;
DeleteObject(font);
Собственно, за установление шрифта отвечает в приведенном листинге только одна строка:
EnumChildWindows(hMainWnd, Addr(EnumFunc), font);
При этом также нужно определить функцию обратного вызова (в данном случае это функция EnumFunc), которая будет вызываться по одному разу для каждого дочернего окна. В данном примере функция EnumFunc имеет следующий вид (листинг 2.37).
Листинг 2.37. Реализация функции EnumFunc
function EnumFunc(wnd: HWND; param: LPARAM): BOOL; stdcall;begin
SendMessage(wnd, WM_SETFONT, WPARAM(param), LPARAM(True));
EnumFunc:= True; //Продолжать перечисление
end;
В принципе, имя этой функции и названия параметров могут быть любыми, а вот типы параметров, возвращаемого значения и способ вызова функции должны быть именно такими, какие представлены в листинге 2.37. Функция должна возвращать True, если нужно продолжать перечисление окон, и False – в противном случае. Значение, которое было передано в качестве третьего параметра API-функции EnumChildWindows, передается в функцию обратного вызова. В данном случае этим параметром является дескриптор шрифта.
Глава 3
Мышь и клавиатура
• Мышь
• Клавиатура
Самыми распространенными устройствами управления персональным компьютером являются мышь и клавиатура. Клавиатура обеспечивает полноценный ввод текстовой информации, а мышь является наиболее простым, интуитивно понятным средством работы с графическим интерфейсом. Поэтому существует масса возможностей по созданию различного рода хитростей и трюков, связанных с мышью и клавиатурой.
• Клавиатура
Самыми распространенными устройствами управления персональным компьютером являются мышь и клавиатура. Клавиатура обеспечивает полноценный ввод текстовой информации, а мышь является наиболее простым, интуитивно понятным средством работы с графическим интерфейсом. Поэтому существует масса возможностей по созданию различного рода хитростей и трюков, связанных с мышью и клавиатурой.
Мышь
Кто из нас не пользовался мышью? Это незамысловатое устройство помогает быстро, а главное просто работать с программами. Начнем с простых операций с мышью. Научимся определять координаты, а также программно перемещать и делать невидимым указатель мыши. Кроме того, вы узнаете, как «захватить» указатель мыши, ограничить область его перемещения и вычислить расстояние, проходимое указателем мыши на экране монитора.
Координаты и указатель мыши
Для начала программным путем определим наличие мыши в системе, а именно выясним, что мышь подключена к компьютеру и правильно настроена (то есть устройство используется). Один из способов определения наличия мыши демонстрирует следующий пример (листинг 3.1).
begin
//С помощью вызова GetSystemMetrics определяем наличие мыши в системе
if GetSystemMetrics(SM_MOUSEPRESENT) <> 0 then
Result:= True
else
Result:= False;
end;
Описанная выше функция MousePresent позволяет проверить наличие мыши. Если мышь подключена к компьютеру и ее можно использовать, то MousePresent возвращает значение True, в противном случае – False.
После того как мышь обнаружена, можно приступать к определению координат ее указателя на экране монитора (листинг 3.2).
var
pt: TPoint;
begin
//Получаем координаты указателя мыши
GetCursorPos(pt);
ShowMessage('('+ IntToStr(pt.X) + ','+ IntToStr(pt.Y) +')');
end;
Здесь для определения координат указателя мыши использовалась API-функция GetCursorPos. Передав в эту функцию переменную pt типа TPoint, вы получите текущие координаты указателя мыши на экране монитора.
В следующем примере демонстрируется, как программным путем скрыть указатель мыши. При нажатии кнопки Button2 указатель будет исчезать, а при нажатии кнопки Button3 (например, с помощью клавиатуры) – появляться (листинг 3.3).
begin
//Прячем указатель
ShowCursor(False);
end;
procedure MouseForm.Button3Click(Sender: TObject);
begin
//Показываем указатель
ShowCursor(True);
end;
В приведенном примере для управления отображением указателя мыши используется функция ShowCursor, которая либо скрывает его (принимая значение False), либо снова показывает (принимая значение True). По причине того, что управление мышью при скрытом указателе невозможно, исходный текст, который управляет видимостью указателя, помещен в обработчики нажатия кнопок формы. Когда указатель скрыт, можно использовать клавишу Tab для выбора и нажатия кнопки формы, используя клавиатуру.
Существуют и другие способы скрыть указатель: рассмотрим пример управления его видимостью посредством установки свойства Cursor компонента:
TempForm.Cursor:= crNone;
В данном случае указатель становится невидимым только для формы, за ее же пределами он видим. Если на форме присутствуют компоненты (элементы управления), то при наведении на них указатель мыши также становится видимым. Если вы хотите сделать указатель невидимым на всей области экрана, то следует применить следующий текст:
Screen.Cursor:= crNone;
Изменять положение указателя на экране можно не только физически передвигая мышь, но и программно задавая новые экранные координаты. Следующий код демонстрирует, каким образом это можно сделать (листинг 3.4).
var
pt: TPoint;
begin
Application.ProcessMessages;
Screen.Cursor:= CrHourglass;
GetCursorPos(pt);
SetCursorPos(pt.x + 1, pt.y + 1);
Application.ProcessMessages;
SetCursorPos(pt.x – 1, pt.y – 1);
end;
Листинг 3.1. Определение присутствия в системе мыши
function MousePresent: Boolean;begin
//С помощью вызова GetSystemMetrics определяем наличие мыши в системе
if GetSystemMetrics(SM_MOUSEPRESENT) <> 0 then
Result:= True
else
Result:= False;
end;
Описанная выше функция MousePresent позволяет проверить наличие мыши. Если мышь подключена к компьютеру и ее можно использовать, то MousePresent возвращает значение True, в противном случае – False.
После того как мышь обнаружена, можно приступать к определению координат ее указателя на экране монитора (листинг 3.2).
Листинг 3.2. Определение координат указателя мыши
procedure MouseForm.Button1Click(Sender: TObject);var
pt: TPoint;
begin
//Получаем координаты указателя мыши
GetCursorPos(pt);
ShowMessage('('+ IntToStr(pt.X) + ','+ IntToStr(pt.Y) +')');
end;
Здесь для определения координат указателя мыши использовалась API-функция GetCursorPos. Передав в эту функцию переменную pt типа TPoint, вы получите текущие координаты указателя мыши на экране монитора.
В следующем примере демонстрируется, как программным путем скрыть указатель мыши. При нажатии кнопки Button2 указатель будет исчезать, а при нажатии кнопки Button3 (например, с помощью клавиатуры) – появляться (листинг 3.3).
Листинг 3.3. Скрытие указателя мыши
procedure MouseForm.Button2Click(Sender: TObject);begin
//Прячем указатель
ShowCursor(False);
end;
procedure MouseForm.Button3Click(Sender: TObject);
begin
//Показываем указатель
ShowCursor(True);
end;
В приведенном примере для управления отображением указателя мыши используется функция ShowCursor, которая либо скрывает его (принимая значение False), либо снова показывает (принимая значение True). По причине того, что управление мышью при скрытом указателе невозможно, исходный текст, который управляет видимостью указателя, помещен в обработчики нажатия кнопок формы. Когда указатель скрыт, можно использовать клавишу Tab для выбора и нажатия кнопки формы, используя клавиатуру.
Существуют и другие способы скрыть указатель: рассмотрим пример управления его видимостью посредством установки свойства Cursor компонента:
TempForm.Cursor:= crNone;
В данном случае указатель становится невидимым только для формы, за ее же пределами он видим. Если на форме присутствуют компоненты (элементы управления), то при наведении на них указатель мыши также становится видимым. Если вы хотите сделать указатель невидимым на всей области экрана, то следует применить следующий текст:
Screen.Cursor:= crNone;
Изменять положение указателя на экране можно не только физически передвигая мышь, но и программно задавая новые экранные координаты. Следующий код демонстрирует, каким образом это можно сделать (листинг 3.4).
Листинг 3.4. Изменение координат мыши
procedure TForm1.Button1Click(Sender: TObject);var
pt: TPoint;
begin
Application.ProcessMessages;
Screen.Cursor:= CrHourglass;
GetCursorPos(pt);
SetCursorPos(pt.x + 1, pt.y + 1);
Application.ProcessMessages;
SetCursorPos(pt.x – 1, pt.y – 1);
end;
Захват указателя мыши
Существует ряд задач, для выполнения которых полезно иметь возможность получать сообщения от мыши даже тогда, когда указатель находится за пределами формы. Обычно рабочая область программы ограничена размерами окна приложения. Это значит, что действия, выполняемые пользователем за пределами рабочей области, попросту не воспринимаются приложением. Попробуйте сделать следующее: откройте редактор Paint, сделайте размер его окна меньше размера холста, затем, зажав кнопку мыши, нарисуйте линию так, чтобы в ходе рисования указатель вышел за пределы окна редактора. Если теперь развернуть окно редактора на весь экран, то можно увидеть, что рисунок содержит часть линии, которую вы рисовали, перемещая указатель за пределами окна редактора. Так происходит, потому что редактор Paint «захватывает» указатель, как только пользователь начинает рисовать линию, и «освобождает» указатель, когда пользователь отпускает кнопку мыши.
Захват указателя полезен и в других случаях, потому стоит рассмотреть способ его реализации (а сделать это действительно просто). В листинге 3.5 приведены обработчики нажатия и отпускания кнопки мыши, которые реализуют захват указателя на время от нажатия до отпускания кнопки.
Shift: TShiftState; X, Y: Integer);
begin
//Захватываем указатель мыши
SetCapture(Handle);
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
//Отменяем захват указателя
ReleaseCapture();
end;
Вся хитрость состоит в использовании API-функций захвата SetCapture и ReleaseCapture. При вызове первой функции происходит регистрация окна, которое захватывает указатель мыши: это окно будет получать сообщения от мыши даже тогда, когда указатель переместится за его пределы. Функция возвращает дескриптор окна, которое захватило указатель ранее, либо 0, если такого окна нет. Соответственно, функция ReleaseCapture используется для освобождения указателя.
Захват указателя полезен и в других случаях, потому стоит рассмотреть способ его реализации (а сделать это действительно просто). В листинге 3.5 приведены обработчики нажатия и отпускания кнопки мыши, которые реализуют захват указателя на время от нажатия до отпускания кнопки.
Листинг 3.5. Захват и освобождение указателя мыши
procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;Shift: TShiftState; X, Y: Integer);
begin
//Захватываем указатель мыши
SetCapture(Handle);
end;
procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
//Отменяем захват указателя
ReleaseCapture();
end;
Вся хитрость состоит в использовании API-функций захвата SetCapture и ReleaseCapture. При вызове первой функции происходит регистрация окна, которое захватывает указатель мыши: это окно будет получать сообщения от мыши даже тогда, когда указатель переместится за его пределы. Функция возвращает дескриптор окна, которое захватило указатель ранее, либо 0, если такого окна нет. Соответственно, функция ReleaseCapture используется для освобождения указателя.
ПримечаниеМожно также упомянуть об API-функции GetCapture. Эта функция не принимает аргументов и возвращает дескриптор окна, захватившего указатель ранее. С помощью этой функции можно, например, удостовериться, что захватом указателя мыши не нарушается работа другого приложения (что маловероятно).
При использовании SetCapture окно получает сообщения, когда указатель находится не над окном, только в том случае, если кнопка мыши нажата либо указатель находится над одним из окон, созданных тем же потоком (независимо от того, нажата ли кнопка мыши).
Ограничение области перемещения указателя
С помощью несложных манипуляций можно ограничить перемещение указателя мыши определенной областью экрана (прямоугольником). Для этого используется API-функция ClipCursor. Она принимает в качестве параметра структуру TRect с координатами прямоугольника, в пределах которого может перемещаться указатель. Если ограничивающая область движения курсора заданна успешно, то функция возвращает ненулевое значение.
С функцией ClipCursor тесно связана функция GetClipCursor, позволяющая получить координаты прямоугольника, которым ограничено перемещение указателя в данный момент.
Способ использования функций ClipCursor и GetClipCursor показан в листинге 3.6.
lastRect: TRect;
cursorClipped: Boolean = False;
procedure SetCursorRect(newRect: TRect);
begin
if not cursorClipped then
begin
//Сохраняем старую область перемещения указателя
GetClipCursor(lastRect);
//Устанавливаем ограничение на перемещения указателя
cursorClipped:= ClipCursor(Addr(newRect)) <> False;
end;
end;
procedure RestoreCursorRect();
begin
if cursorClipped then
begin
//Восстанавливаем область перемещения указателя
cursorClipped:= ClipCursor(Addr(lastRect)) = False;
end;
end;
В данном примере реализованы две функции. Первая (SetCursorRect) ограничивает перемещение указателя мыши заданной областью экрана (параметр newRect). Перед ограничением на перемещения указателя в процедуре SetCursorRect происходит сохранение области перемещения, установленной ранее, чтобы действие процедуры можно было отменить. Для отмены ограничения перемещения указателя служит вторая функция – RestoreCursorRect.
С функцией ClipCursor тесно связана функция GetClipCursor, позволяющая получить координаты прямоугольника, которым ограничено перемещение указателя в данный момент.
Способ использования функций ClipCursor и GetClipCursor показан в листинге 3.6.
Листинг 3.6. Ограничение области перемещения указателя
varlastRect: TRect;
cursorClipped: Boolean = False;
procedure SetCursorRect(newRect: TRect);
begin
if not cursorClipped then
begin
//Сохраняем старую область перемещения указателя
GetClipCursor(lastRect);
//Устанавливаем ограничение на перемещения указателя
cursorClipped:= ClipCursor(Addr(newRect)) <> False;
end;
end;
procedure RestoreCursorRect();
begin
if cursorClipped then
begin
//Восстанавливаем область перемещения указателя
cursorClipped:= ClipCursor(Addr(lastRect)) = False;
end;
end;
В данном примере реализованы две функции. Первая (SetCursorRect) ограничивает перемещение указателя мыши заданной областью экрана (параметр newRect). Перед ограничением на перемещения указателя в процедуре SetCursorRect происходит сохранение области перемещения, установленной ранее, чтобы действие процедуры можно было отменить. Для отмены ограничения перемещения указателя служит вторая функция – RestoreCursorRect.
Примечание
Вообще, задание ограничения на перемещение указателя мыши не считается хорошим тоном. Потому для использования такой возможности в реальном приложении должны быть действительно веские причины.
Изменение назначений кнопок мыши
Как известно, операционная система Windows предоставляет возможность работать с компьютером широкому кругу людей. Большинство производителей манипуляторов типа «мышь» предусматривают возможность простой адаптации манипулятора под правшу или левшу. К тому же, мышь адаптировать к таким различиям намного проще, нежели выпускать манипуляторы различных типов: конструкцию изменять не надо, достаточно программно поменять функции кнопок мыши.
Способ программного изменения функций левой и правой кнопок мыши продемонстрирован в листинге 3.7.
begin
//Меняем местами функции левой и правой кнопок мыши
SwapMouseButton(True);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
//Восстанавливаем функции кнопок мыши
SwapMouseButton(False);
end;
В коде листинга 3.7 не учтен тот факт, что инверсия кнопок мыши может быть изначально установлена при запуске программы (например, если за компьютером работает левша). Чтобы точно знать, была ли ранее применена инверсия к кнопкам мыши, можно использовать значение, возвращаемое функцией SwapMouseButton. Если это значение отлично от нуля, то функции кнопок мыши ранее были инвертированы.
Способ программного изменения функций левой и правой кнопок мыши продемонстрирован в листинге 3.7.
Листинг 3.7. Изменение назначений кнопок мыши
procedure TForm1.Button1Click(Sender: TObject);begin
//Меняем местами функции левой и правой кнопок мыши
SwapMouseButton(True);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
//Восстанавливаем функции кнопок мыши
SwapMouseButton(False);
end;
В коде листинга 3.7 не учтен тот факт, что инверсия кнопок мыши может быть изначально установлена при запуске программы (например, если за компьютером работает левша). Чтобы точно знать, была ли ранее применена инверсия к кнопкам мыши, можно использовать значение, возвращаемое функцией SwapMouseButton. Если это значение отлично от нуля, то функции кнопок мыши ранее были инвертированы.
Подсчет расстояния, пройденного указателем мыши
Далее будет рассмотрена небольшая программа, которая носит скорее познавательный, нежели практический характер. Она подсчитывает количество метров (в буквальном смысле), пройденное указателем мыши за время ее работы. Внешний вид формы приложения показан на рис. 3.1.
Рис. 3.1. Программа для измерения пробега указателя мыши
Использование этой программы крайне просто: сразу после запуска она начинает измерять пройденное указателем мыши расстояние в пикселах. Нижняя группа элементов управления формы нужна для правильного вывода пройденного расстояния в метрах. При нажатии кнопки Изменить масштаб становятся активными два текстовых поля (предназначенных для ввода ширины и высоты прямоугольника). Чтобы программа правильно преобразовывала пройденное расстояние, нужно линейкой измерить ширину белого прямоугольника и ввести полученное значение (в миллиметрах) в текстовое поле. При повторном нажатии кнопки Изменить масштаб введенные значения принимаются, и с этого момента показания пройденного расстояния начинают переводиться в метры с учетом текущего разрешения и размера монитора.
Теперь перейдем к рассмотрению способа реализации этого приложения. В табл. 3.1 приведены сведения о настройке элементов управления, не являющихся рамками или статическими надписями.
TForm1 = class(TForm)
...
private
isUp dating: Boolean; //Если равен False, то показания в txtDistance
//не обновляются
lastPos: TPoint; //Координаты указателя во время прошлого замера
distance: Real; //Пройденное расстояние в пикселах
procedure StartUpdating();
procedure StopUpdating();
procedure ShowDistance();
end;
Суммарное расстояние в пикселах, пройденное указателем, сохраняется в переменной distance. Ниже представлен способ перевода этого расстояния в метры (листинг 3.9).
var
scale: Real;
distanceMetters: Real;
begin
//Пересчитываем текущий пробег в метры и показываем его
//в текстовом поле
//..определяем масштаб для перевода измерений в метры
scale:= 0.001 * StrToInt(txtWidth.Text) / Shape1.Width;
//..подсчитываем расстояние с учетом масштаба
distanceMetters:= scale * distance;
//..округляем до трех знаков (мм) и показываем
distanceMetters:= Int(distanceMetters * 1000) * 0.001;
txtDistance.Text:= FloatToStr(distanceMetters);
end;
Главная процедура приложения – обработчик для таймера Timer1. Таймер срабатывает с максимальной для него частотой (около 18 раз в секунду). Текст обработчика Timer1Timer приведен в листинге 3.10.
var
curPos: TPoint;
delta: Real;
begin
if (curPos.X <> lastPos.X) or (curPos.Y <> lastPos.Y) then
begin
GetCursorPos(curPos);
//Вычисляем разницу между текущим и прошлым положением мыши
delta:= Sqrt(Sqr(curPos.X – lastPos.X) + Sqr(curPos.Y – lastPos.Y));
distance:= distance + delta;
//Не забываем сохранить новые координаты указателя
lastPos:= curPos;
if isUpdating then
begin
//Обновим показания в текстовом поле
ShowDistance();
end;
end;
end;
Из данного листинга видно, что обновление показаний происходит при истинном (True) значении переменной isUpdating. Значение этой переменной устанавливается в False во время изменения масштаба, чтобы во время ввода значений в текстовые поля не выводились неправильные цифры (листинг 3.11).
begin
if cmbScale.Caption = 'Изменить масштаб' then
begin
//Начинаем изменение масштаба
StopUpdating();
cmbScale.Caption:= 'Принять масштаб';
txtWidth.Enabled:= True;
end
else
begin
//Заканчиваем изменение масштаба
txtWidth.Enabled:= False;
cmbScale.Caption:= 'Изменить масштаб';
StartUpdating();
end;
end;
Процедуры StartUpdating и StopUpdating скрывают действия, которые необходимо произвести для остановки и возобновления отображения пройденного в текстовом поле указателем мыши расстояния. В данном примере они выглядят достаточно просто (листинг 3.12).
begin
//Включаем обновление показаний в текстовом поле
isUpdating:= True;
end;
procedure TForm1.StopUpdating();
begin
//Отключаем обновление показаний в текстовом поле
isUpdating:= False;
end;
В завершение остается реализовать код инициализации координат указателя мыши при запуске программы и обработчик события Click для кнопки cmbClear (листинг 3.13).
begin
//Инициализируем координаты мыши
GetCursorPos(lastPos);
StartUpdating();
end;
procedure TForm1.cmbClearClick(Sender: TObject);
begin
//Сбрасываем счетчик пройденного расстояния
distance:= 0;
GetCursorPos(lastPos); //Начинаем отсчет с текущей позиции указателя
ShowDistance();
end;
Вот, собственно, и все, что нужно для работы рассматриваемой программы. Остается лишь уточнить, что способ установки масштаба, используемый в программе, предназначен для таких разрешений мониторов, при которых нет искажений по горизонтали или вертикали. Чаще всего это такие разрешения, при которых размеры изображения по горизонтали и вертикали подчиняются пропорции 4:3 (640 х 480, 800 х 600 и т. д.). При этом такими же пропорциями должен обладать и экран монитора.
Рис. 3.1. Программа для измерения пробега указателя мыши
Использование этой программы крайне просто: сразу после запуска она начинает измерять пройденное указателем мыши расстояние в пикселах. Нижняя группа элементов управления формы нужна для правильного вывода пройденного расстояния в метрах. При нажатии кнопки Изменить масштаб становятся активными два текстовых поля (предназначенных для ввода ширины и высоты прямоугольника). Чтобы программа правильно преобразовывала пройденное расстояние, нужно линейкой измерить ширину белого прямоугольника и ввести полученное значение (в миллиметрах) в текстовое поле. При повторном нажатии кнопки Изменить масштаб введенные значения принимаются, и с этого момента показания пройденного расстояния начинают переводиться в метры с учетом текущего разрешения и размера монитора.
Теперь перейдем к рассмотрению способа реализации этого приложения. В табл. 3.1 приведены сведения о настройке элементов управления, не являющихся рамками или статическими надписями.
Таблица 3.1. Параметры элементов управления формы, показанной на рис. 3.1
В коде листинга 3.8 объявляются переменные (члены класса TForm1) и методы, добавленные вручную.Листинг 3.8. Форма для измерения пробега указателя
typeTForm1 = class(TForm)
...
private
isUp dating: Boolean; //Если равен False, то показания в txtDistance
//не обновляются
lastPos: TPoint; //Координаты указателя во время прошлого замера
distance: Real; //Пройденное расстояние в пикселах
procedure StartUpdating();
procedure StopUpdating();
procedure ShowDistance();
end;
Суммарное расстояние в пикселах, пройденное указателем, сохраняется в переменной distance. Ниже представлен способ перевода этого расстояния в метры (листинг 3.9).
Листинг 3.9. Перевод расстояния в метры с учетом масштаба
procedure TForm1.ShowDistance();var
scale: Real;
distanceMetters: Real;
begin
//Пересчитываем текущий пробег в метры и показываем его
//в текстовом поле
//..определяем масштаб для перевода измерений в метры
scale:= 0.001 * StrToInt(txtWidth.Text) / Shape1.Width;
//..подсчитываем расстояние с учетом масштаба
distanceMetters:= scale * distance;
//..округляем до трех знаков (мм) и показываем
distanceMetters:= Int(distanceMetters * 1000) * 0.001;
txtDistance.Text:= FloatToStr(distanceMetters);
end;
Главная процедура приложения – обработчик для таймера Timer1. Таймер срабатывает с максимальной для него частотой (около 18 раз в секунду). Текст обработчика Timer1Timer приведен в листинге 3.10.
Листинг 3.10. Подсчет разницы между положениями указателя мыши
procedure TForm1.Timer1Timer(Sender: TObject);var
curPos: TPoint;
delta: Real;
begin
if (curPos.X <> lastPos.X) or (curPos.Y <> lastPos.Y) then
begin
GetCursorPos(curPos);
//Вычисляем разницу между текущим и прошлым положением мыши
delta:= Sqrt(Sqr(curPos.X – lastPos.X) + Sqr(curPos.Y – lastPos.Y));
distance:= distance + delta;
//Не забываем сохранить новые координаты указателя
lastPos:= curPos;
if isUpdating then
begin
//Обновим показания в текстовом поле
ShowDistance();
end;
end;
end;
Из данного листинга видно, что обновление показаний происходит при истинном (True) значении переменной isUpdating. Значение этой переменной устанавливается в False во время изменения масштаба, чтобы во время ввода значений в текстовые поля не выводились неправильные цифры (листинг 3.11).
Листинг 3.11. Активизация и деактивизация режима изменения масштаба
procedure TForm1.cmbScaleClick(Sender: TObject);begin
if cmbScale.Caption = 'Изменить масштаб' then
begin
//Начинаем изменение масштаба
StopUpdating();
cmbScale.Caption:= 'Принять масштаб';
txtWidth.Enabled:= True;
end
else
begin
//Заканчиваем изменение масштаба
txtWidth.Enabled:= False;
cmbScale.Caption:= 'Изменить масштаб';
StartUpdating();
end;
end;
Процедуры StartUpdating и StopUpdating скрывают действия, которые необходимо произвести для остановки и возобновления отображения пройденного в текстовом поле указателем мыши расстояния. В данном примере они выглядят достаточно просто (листинг 3.12).
Листинг 3.12. Включение и выключение обновления результатов измерения
procedure TForm1.StartUpdating();begin
//Включаем обновление показаний в текстовом поле
isUpdating:= True;
end;
procedure TForm1.StopUpdating();
begin
//Отключаем обновление показаний в текстовом поле
isUpdating:= False;
end;
В завершение остается реализовать код инициализации координат указателя мыши при запуске программы и обработчик события Click для кнопки cmbClear (листинг 3.13).
Листинг 3.13. Инициализация при запуске и код сброса счетчика
procedure TForm1.FormCreate(Sender: TObject);begin
//Инициализируем координаты мыши
GetCursorPos(lastPos);
StartUpdating();
end;
procedure TForm1.cmbClearClick(Sender: TObject);
begin
//Сбрасываем счетчик пройденного расстояния
distance:= 0;
GetCursorPos(lastPos); //Начинаем отсчет с текущей позиции указателя
ShowDistance();
end;
Вот, собственно, и все, что нужно для работы рассматриваемой программы. Остается лишь уточнить, что способ установки масштаба, используемый в программе, предназначен для таких разрешений мониторов, при которых нет искажений по горизонтали или вертикали. Чаще всего это такие разрешения, при которых размеры изображения по горизонтали и вертикали подчиняются пропорции 4:3 (640 х 480, 800 х 600 и т. д.). При этом такими же пропорциями должен обладать и экран монитора.
Подсвечивание элементов управления
В реальных приложениях часто возникает необходимость изменять внешний вид элементов интерфейса программы в ответ на определенные действия пользователя. Поэтому стоит рассмотреть несложный, но достаточно полезный пример, позволяющий сделать более «живым» интерфейс приложения: изменение внешнего вида элементов управления при наведении на них указателя мыши.
В листинге 3.14 продемонстрирован способ создания статической надписи, похожей на гиперссылку (для большего эффекта для такой надписи можно установить свойство Cursor равным crHandPoint на этапе проектирования формы).
begin
lblUnderline.Font.Style:= [fsUnderline];
lblUnderline.Font.Color:= RGB(0, 0, 255);
end;
procedure TForm1.lblUnderlineMouseLeave(Sender: TObject);
begin
lblUnderline.Font.Style:= [];
lblUnderline.Font.Color:= RGB(0, 0, 0);
end;
Для надписи, чтобы получилась довольно правдоподобная гиперссылка, осталось добавить только обработчик события Click, правда, выполнять она сможет любое действие, а не только переход по ссылке (достаточно лишь определить обработчик).
Для стандартной кнопки начертание шрифта также можно изменить (листинг 3.15).
Shift: TShiftState; X, Y: Integer);
begin
cmbItalicBold.Font.Style:= [fsItalic, fsBold];
end;
procedure TForm1.lblItalicMouseEnter(Sender: TObject);
begin
lblItalic.Font.Style:= [fsItalic];
end
В листинге 3.15 используется обработчик MouseMove для кнопки потому, что обработчики событий MouseEnter и MouseLeave для нее (по крайней мере, с вкладки Standard) не предусмотрены.
В листинге 3.14 продемонстрирован способ создания статической надписи, похожей на гиперссылку (для большего эффекта для такой надписи можно установить свойство Cursor равным crHandPoint на этапе проектирования формы).
Листинг 3.14. Подчеркивание и изменение цвета надписи
procedure TForm1.lblUnderlineMouseEnter(Sender: TObject);begin
lblUnderline.Font.Style:= [fsUnderline];
lblUnderline.Font.Color:= RGB(0, 0, 255);
end;
procedure TForm1.lblUnderlineMouseLeave(Sender: TObject);
begin
lblUnderline.Font.Style:= [];
lblUnderline.Font.Color:= RGB(0, 0, 0);
end;
Для надписи, чтобы получилась довольно правдоподобная гиперссылка, осталось добавить только обработчик события Click, правда, выполнять она сможет любое действие, а не только переход по ссылке (достаточно лишь определить обработчик).
Для стандартной кнопки начертание шрифта также можно изменить (листинг 3.15).
Листинг 3.15. Изменение начертания шрифта
procedureTForm1. cmbItalicBoldMouseMove (Sender: TObject;Shift: TShiftState; X, Y: Integer);
begin
cmbItalicBold.Font.Style:= [fsItalic, fsBold];
end;
procedure TForm1.lblItalicMouseEnter(Sender: TObject);
begin
lblItalic.Font.Style:= [fsItalic];
end
В листинге 3.15 используется обработчик MouseMove для кнопки потому, что обработчики событий MouseEnter и MouseLeave для нее (по крайней мере, с вкладки Standard) не предусмотрены.
Клавиатура
Клавиатура является основным средством ввода информации в компьютер, поэтому не будем обходить стороной и рассмотрим некоторые не так часто используемые или не такие очевидные возможности работы с ней. В этом разделе вы научитесь определять тип клавиатуры, а также будет разработана программа опроса клавиатуры. Также будет рассказано, как программным способом имитировать нажатия клавиш, и разработана программа «Бегущие огни на клавиатуре».
Получение информации о клавиатуре
Начнем с небольшого примера, позволяющего определить некоторую информацию о клавиатуре (листинг 3.16). Данный пример основан на использовании API-функции GetKeyboardType.
begin
//Определяем тип клавиатуры
case GetKeyboardType(0) of
1: txt Type.Text:= 'PC/XT или совместимая (83 клавиши)';
2: txt Type.Text xtxt:= 'Olivetti" ICO " (102 клавиши)';
3: txt Type.Text xtxt:= 'PC/AT (84 клавиши) или похожая';
4: txt Type.Text:= 'Расширенная (101 или 102 клавиши)';
5: txt Type.Text:= 'Nokia 1050 или похожая';
6: txt Type.Text:= 'Nokia 9140 или похожая';
7: txt Type.Text:= 'японская';
end;
//Определяем код типа производителя
txtSubtype.Text:= IntToStr(GetKeyboardType(1));
//Определяем количество функциональных клавиш
txtKeys.Text:= IntToStr(GetKeyboardType(2));
end;
При создании формы происходит заполнение текстовых полей информацией о типе клавиатуры, коде типа, присвоенном производителем, и количестве функциональных клавиш.
На рис. 3.2 показан возможный результат определения информации о клавиатуре.
Рис. 3.2. Информация о клавиатуре
Листинг 3.16. Получение информации о клавиатуре
procedure TForm1.FormCreate(Sender: TObject);begin
//Определяем тип клавиатуры
case GetKeyboardType(0) of
1: txt Type.Text:= 'PC/XT или совместимая (83 клавиши)';
2: txt Type.Text xtxt:= 'Olivetti" ICO " (102 клавиши)';
3: txt Type.Text xtxt:= 'PC/AT (84 клавиши) или похожая';
4: txt Type.Text:= 'Расширенная (101 или 102 клавиши)';
5: txt Type.Text:= 'Nokia 1050 или похожая';
6: txt Type.Text:= 'Nokia 9140 или похожая';
7: txt Type.Text:= 'японская';
end;
//Определяем код типа производителя
txtSubtype.Text:= IntToStr(GetKeyboardType(1));
//Определяем количество функциональных клавиш
txtKeys.Text:= IntToStr(GetKeyboardType(2));
end;
При создании формы происходит заполнение текстовых полей информацией о типе клавиатуры, коде типа, присвоенном производителем, и количестве функциональных клавиш.
На рис. 3.2 показан возможный результат определения информации о клавиатуре.
Рис. 3.2. Информация о клавиатуре
Опрос клавиатуры
Существует достаточно удобная альтернатива обработке событий клавиатурного ввода, которая может оказаться особенно полезной, если необходима информация о состоянии сразу нескольких клавиш. Это может понадобиться, если пользователь должен одновременно удерживать нажатыми несколько клавиш. Например, в гоночных симуляторах, чтобы проезжать поворот, необходимо одновременно удерживать клавишу ↑ (газ) и одну из клавиш поворота (← или →).
В листинге 3.17 приведен пример обработчика события Timer1Timer, определяющего, нажаты ли клавиши ↑, ↓, ←, →, а также пробел, Enter, Ctrl (правый), Shift (правый) и Alt (правый).
var
buttons: TKeyBoardstate;
begin
//Получаем состояния клавиш
GetKeyboardState(buttons);
//Отобразим состояния клавиш
//..пробел
if buttons[VK_SPACE] and 128 <> 0 then
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..enter
if buttons[VK_RETURN] and 128 <> 0 then
SendMessage(cmbEnter.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbEnter.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..правый Ctrl
if buttons[VK_RCONTROL] and 128 <> 0 then
SendMessage(cmbRCtrl.Handle, BM_SETSTATE, BST_CHECKED, 0)
В листинге 3.17 приведен пример обработчика события Timer1Timer, определяющего, нажаты ли клавиши ↑, ↓, ←, →, а также пробел, Enter, Ctrl (правый), Shift (правый) и Alt (правый).
Листинг 3.17. Определение состояния некоторых клавиш
procedure TForm1.Timer1Timer(Sender: TObject);var
buttons: TKeyBoardstate;
begin
//Получаем состояния клавиш
GetKeyboardState(buttons);
//Отобразим состояния клавиш
//..пробел
if buttons[VK_SPACE] and 128 <> 0 then
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbSpace.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..enter
if buttons[VK_RETURN] and 128 <> 0 then
SendMessage(cmbEnter.Handle, BM_SETSTATE, BST_CHECKED, 0)
else
SendMessage(cmbEnter.Handle, BM_SETSTATE, BST_UNCHECKED, 0);
//..правый Ctrl
if buttons[VK_RCONTROL] and 128 <> 0 then
SendMessage(cmbRCtrl.Handle, BM_SETSTATE, BST_CHECKED, 0)
Конец бесплатного ознакомительного фрагмента