CreateMemo(20, 270, 430, 100, 2001);
   ShowWindow(hMainWnd, SW_NORMAL);
   //Запуск цикла обработки сообщений
   while (Longint(GetMessage(mess, 0, 0, 0)) <> 0) do
   begin
   if (IsDialogMessage(hMainWnd, mess) = False) then
   begin
   TranslateMessage(mess);
   DispatchMessage(mess);
   end;
   end;
   end.
   Код функции RegisterWindow опущен, так как он аналогичен приведенному в листинге 2.4. Функции работы с рассмотренными ранее диалоговыми окнами вынесены в модуль StdWindows (файл StdWindows. pas).
   Особенностью цикла обработки сообщений в этом примере является использование API-функции IsDialogMessage, которая позволяет реагировать на некоторые действия пользователя так, как это делается в диалоговых окнах. Примером может быть перемещение фокуса между окнами при нажатии клавиши Tab.
   Перед функцией RegisterWindow (на месте многоточия перед ее объявлением в листинге 2.33) находится функция обработки сообщений, имеющая следующий вид (листинг 2.34).
Листинг 2.34. Функция обработки сообщений
   function WindowFunc(hWnd:HWND; msg:UINT; wParam:WPARAM;
   lParam:LPARAM):LRESULT; stdcall;
   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(2001, 'Открыт файл:' + #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(2001, 'Выбранная папка:' + #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));
   SetEditText(2001, 'Текст, записанный выбранным шрифтом');
   if (hOldFont <> 0) then DeleteObject(hOldFont);
   Этот довольно объемный фрагмент кода всего лишь заменяет шрифт в текстовом поле. Подобную операцию можно использовать для задания шрифта любого элемента управления. В частности, в приведенных в этой главе примерах текст на кнопках, надписях и т. д. выглядит довольно невзрачно потому, что используется системный шрифт, установленный по умолчанию.
   Способ, которым можно установить шрифт всех элементов управления окна, показан далее. Теперь еще один существенный момент: не забывайте удалять объекты GDI (в данном случае – шрифт) после того, как они стали не нужны. Дело в том, что приложение может владеть не более чем 65 000 объектов GDI. И при наличии так называемой «утечки» ресурсов GDI может наступить момент (при продолжительной работе программы), когда вдруг окна приложения начинают отрисовываться по меньшей мере странно (если вообще отрисовываются).

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

   Есть множество способов установки шрифта текста, отображаемого в элементах управления. Можно, например, при создании каждого элемента управления посылать ему сообщение 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 := CreateButton(20, 30, 70, 30, 1001, 'Кнопка 1');
   SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
   ctrl := CreateButton(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 := CreateOption(210, 100, 180, 20, 3001, 'Переключатель 1',
   True);
   SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
   ctrl := CreateOption(210,130,180,20,3002, 'Переключатель 2',
   False, True);
   SendMessage(ctrl, WM_SETFONT, HFONT(font), 1);
   ctrl := CreateOption(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), которая будет вызываться по одному разу для каждого дочернего окна. В нашем примере функцияЕпитРипс имеет следующий вид (листинг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).
Листинг 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 типа ТPoint, мы получим текущие экранные координаты указателя.
   Рассмотрим пример, в котором указатель мыши при нажатии кнопки 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, сделайте размер его окна меньше размера холста, после чего, нажав кнопку мыши, нарисуйте линию так, чтобы в ходе рисования указатель вышел за пределы окна редактора. Есть ли на рисунке часть линии, которую вы рисовали, двигая указатель за пределами окна (должна быть)?
   Захват указателя полезен и в других случаях, потому мы рассмотрим, как его реализовать (а сделать это действительно просто). В листинге 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 приводятся объявления переменных (членов класса TForml) и методов, добавленных вручную.
Листинг 3.8. Форма для измерения пробега указателя
   type
   TForm1 = class(TForm)
   …
   private
   isUpdating: 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;
   В приведенном расчете нет ничего сложного, как, собственно, нет ничего сложного и во всем примере. Главная процедура приложения – обработчик для таймера Timerl. Таймер срабатывает с максимальной для него частотой (не 1 мс, конечно, но где-то 18 раз в секунду). Текст обработчикаТ1тег1Т1тег приводится в листинге 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;
   Как можно увидеть при внимательном рассмотрении листинга 3.10, обновление показаний происходит при истинном значении переменной 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. Изменение начертания шрифта
   procedure TForm1.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 для кнопки потому, что, к великому сожалению, обработчики co6biTHftMouseEnter nMouseLeave для нее (по крайней мере, с вкладки Standard) не предусмотрены.

3.2. Клавиатура

   Клавиатура является основным средством для ввода информации в компьютер, поэтому не будем обходить стороной и рассмотрим некоторые не так часто используемые или не такие очевидные возможности работы с ней.

Определение информации о клавиатуре

   Начнем с небольшого примера, позволяющего определить некоторую информацию о клавиатуре (листинг 3.16). Пример основан на использовании API-функции GetKeyboardType.
Листинг 3.16. Определение информации о клавиатуре
   procedure TForm1.FormCreate(Sender: TObject);
   begin
   //Определяем тип клавиатуры
   case GetKeyboardType(0) of
   1: txtType.Text := 'PC/XT или совместимая (83 клавиши)';
   2: txtType.Text := 'Olivetti «ICO» (102 клавиши)';
   3: txtType.Text := 'PC/AT (84 клавиши) или похожая';
   4: txtType.Text := 'Расширенная (101 или 102 клавиши)';
   5: txtType.Text := 'Nokia 1050 или похожая';
   6: txtType.Text := 'Nokia 9140 или похожая';
   7: txtType.Text := 'японская';
   end;
   //Определяем код типа производителя
   txtSubtype.Text := IntToStr(GetKeyboardType(1));
   //Определяем количество функциональных клавиш
   txtKeys.Text := IntToStr(GetKeyboardType(2));
   end;
   При создании формы происходит заполнение текстовых полей информацией о типе клавиатуры, коде типа, присвоенном производителем, и количестве функциональных клавиш.
   
Конец бесплатного ознакомительного фрагмента