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.

Установка шрифта элементов управления

   Существует множество способов установки шрифта текста, отображаемого в элементах управления. Можно, например, при создании элемента управления посылать ему сообщение WM_SETFONT, передавая дескриптор (HFONT) созданного ранее объекта шрифта. В таком случае код создания и установки шрифта элементов управления (с использованием рассмотренных в этой главе функций) может выглядеть, как показано в листинге 2.35.
Листинг 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).
Листинг 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 приведены обработчики нажатия и отпускания кнопки мыши, которые реализуют захват указателя на время от нажатия до отпускания кнопки.
Листинг 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 используется для освобождения указателя.
   Примечание
   При использовании SetCapture окно получает сообщения, когда указатель находится не над окном, только в том случае, если кнопка мыши нажата либо указатель находится над одним из окон, созданных тем же потоком (независимо от того, нажата ли кнопка мыши).
   Можно также упомянуть об API-функции GetCapture. Эта функция не принимает аргументов и возвращает дескриптор окна, захватившего указатель ранее. С помощью этой функции можно, например, удостовериться, что захватом указателя мыши не нарушается работа другого приложения (что маловероятно).

Ограничение области перемещения указателя

   С помощью несложных манипуляций можно ограничить перемещение указателя мыши определенной областью экрана (прямоугольником). Для этого используется API-функция ClipCursor. Она принимает в качестве параметра структуру TRect с координатами прямоугольника, в пределах которого может перемещаться указатель. Если ограничивающая область движения курсора заданна успешно, то функция возвращает ненулевое значение.
   С функцией ClipCursor тесно связана функция GetClipCursor, позволяющая получить координаты прямоугольника, которым ограничено перемещение указателя в данный момент.
   Способ использования функций ClipCursor и GetClipCursor показан в листинге 3.6.
Листинг 3.6. Ограничение области перемещения указателя
   var
    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.
   Примечание
   Вообще, задание ограничения на перемещение указателя мыши не считается хорошим тоном. Потому для использования такой возможности в реальном приложении должны быть действительно веские причины.

Изменение назначений кнопок мыши

   Как известно, операционная система Windows предоставляет возможность работать с компьютером широкому кругу людей. Большинство производителей манипуляторов типа «мышь» предусматривают возможность простой адаптации манипулятора под правшу или левшу. К тому же, мышь адаптировать к таким различиям намного проще, нежели выпускать манипуляторы различных типов: конструкцию изменять не надо, достаточно программно поменять функции кнопок мыши.
   Способ программного изменения функций левой и правой кнопок мыши продемонстрирован в листинге 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 приведены сведения о настройке элементов управления, не являющихся рамками или статическими надписями.
Таблица 3.1. Параметры элементов управления формы, показанной на рис. 3.1
   В коде листинга 3.8 объявляются переменные (члены класса TForm1) и методы, добавленные вручную.
Листинг 3.8. Форма для измерения пробега указателя
   type
   TForm1 = 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 на этапе проектирования формы).
Листинг 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.
Листинг 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 (правый).
Листинг 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)
   
Конец бесплатного ознакомительного фрагмента