Перемещение за клиентскую область
Здесь на конкретном примере (перемещение формы за любую точку клиентской области) продемонстрировано, как можно самостоятельно определять положение некоторых важных элементов окна. Под элементами окна здесь подразумеваются:
• строка заголовка (не только предназначена для отображения текста заголовка, но и служит областью захвата при перемещении окна мышью);
• границы окна (при щелчке кнопкой мыши на верхней, нижней, правой и левой границе можно изменять размер окна, если, правда, стиль окна это допускает);
• четыре угла окна (предназначены для изменения размера окна при помощи мыши);
• системные кнопки – закрытия, разворачивания, сворачивания, контекстной справки (обычно расположены в строке заголовка окна);
• полосы прокрутки – горизонтальная и вертикальная;
• системное меню (раскрывается при щелчке кнопкой мыши на значке окна);
• меню – полоса меню, обычно вверху окна;
• клиентская область – по умолчанию все пространство окна, кроме строки заголовка, меню и полос прокрутки.
Каждый раз, когда над окном перемещается указатель мыши либо происходит нажатие кнопки мыши, система посылает соответствующему окну сообщение WM_NCHITTEST для определения того, над какой из перечисленных выше областей окна находится указатель. Обработчик этого сообщения, вызываемый по умолчанию, информирует систему о расположении элементов окна в привычных для нас местах: заголовка – сверху, правой границы – справа и т. д.
Как вы, скорее всего, уже догадались, реализовав свой обработчик сообщения WM_NCHITTEST, можно изменить назначение элементов окна. Этот прием как раз и используется в листинге 1.21.
...Листинг 1.21. Перемещение окна за клиентскую область
procedure TfrmMoveClient.WMNCHitTest(var Message: TWMNCHitTest);
var
rc: TRect;
p: TPoint;
begin
//Если точка приходится на клиентскую область, то заставим
//систему считать эту область частью строки заголовка
rc := GetClientRect();
p.X := Message.XPos;
p.Y := Message.YPos;
p := ScreenToClient(p);
if PtInRect(rc, p) then
Message.Result := HTCAPTION
else
//Обработка по умолчанию
Message.Result := DefWindowProc(Handle, Message.Msg, 0,
65536 * Message.YPos + Message.XPos);
end;
Приведенный в листинге 1.21 обработчик переопределяет положение только строки заголовка, возвращая значение HTCAPTION. Этот обработчик может возвращать следующие значения (целочисленные константы, возвращаемые функцией DefWindowProc):
• HTBORDER – указатель мыши находится над границей окна (размер окна не изменяется);
• НТВОТТОМ, НТТОР, HTLEFT, HTRIGHT – над нижней, верхней, левой или правой границей окна соответственно (размер окна можно изменить, «потянув» за границу);
• HTBOTTOMLEFT, HTBOTTOMRIGHT, HTTOPLEFT, HTTOPRIGHT – В левом нижнем, правом нижнем, левом верхнем или правом верхнем углу окна (размер окна можно изменять по диагонали);
• HTSIZE, HTGROWBOX – над областью, предназначенной для изменения размера окна по диагонали (обычно в правом нижнем углу окна);
• HTCAPTION – над строкой заголовка окна (за это место окно перемещается);
• HTCLIENT – над клиентской областью окна;
• HTCLOSE – над кнопкой закрытия окна;
• HTHELP – над кнопкой вызова контекстной справки;
• HTREDUCE, HTMINBUTTON – над кнопкой минимизации окна;
• HTZOOM, HTMAXBUTTON – над кнопкой максимизации окна;
• HTMENU – над полоской меню окна;
• HTSYSMENU – над значком окна (используется для вызова системного меню);
• HTHSCROLL, HTVSCROLL – указатель находится над вертикальной или горизонтальной полосой прокрутки соответственно;
• HTTRANS PARENT – если возвращается это значение, то сообщение пересылается окну, находящемуся под данным окном (окна должны принадлежать одному потоку);
• HTNOWHERE – указатель не находится над какой-либо из областей окна (например, на границе между окнами);
• HTERROR – то же, что и NTNOWHERE, только при возврате этого значения обработчик по умолчанию (DefWindowProc) воспроизводит системный сигнал, говорящий об ошибке.
Перемещаемые элементы управления
В завершение материала о перемещении окон приведем один совсем несложный, но довольно интересный пример, позволяющий прямо «на лету» модифицировать внешний вид приложения, перемещая и изменяя размер элементов управления так, как будто это обычные перекрывающиеся окна.
Чтобы вас заинтересовать, сразу приведем результат работы примера. Итак, на рис. 1.13 показан внешний вид формы в начале работы примера.