Чтение онлайн

ЖАНРЫ

О чём не пишут в книгах по Delphi

Григорьев Антон Борисович

Шрифт:
Листинг 1.52. Метод
SetRegion
, устанавливающий регион окна

procedure TFormHole.SetRegion;

var

 Rgn1, Rgn2: HRGN;

 R, R2: TRect;

begin

 // Создаем регион, соответствующий прямоугольнику окна

 Rgn1 := CreateRectRgn(0, 0, Width, Height);

 // Нам потребуются координаты панели относительно левого

 // верхнего угла окна (а не относительно левого верхнего

 // угла клиентской области, как это задается свойствами

 // Left
и Тор). Функций для получения смещения клиентской

 // области относительно левого верхнего угла окна нет.

 // Придется воспользоваться сообщением WM_NCCalcRect

 R2 := Rect(Left, Top, Left + Width, Top + Height);

 Perform(WM_NCCALCSIZE, 0, LParam(@R2));

 // Переводим координаты полученного прямоугольника из

 // экранных в координаты относительно левого верхнего

 // угла окна

 OffsetRect(R2, -Left, -Top);

 // получаем координаты панели относительно левого

 // верхнего угла клиентской области и пересчитываем их

 // в координаты относительно верхнего левого угла окна

 R := Rect(0, 0, PanelHole.Width, PanelHole.Height);

 OffsetRect(R, PanelHole.Left + R2.Left, PanelHole.Top + R2.Top);

 // уменьшаем прямоугольник на величину рамки и создаем

 // соответствующий регион

 InflateRect(R, -HoleBorder, -HoleBorder);

 Rgn2 := CreateRectRgnIndirect(R);

 // вычитаем один прямоугольный регион из другого, получая

 // прямоугольник с дыркой

 CombineRgn(Rgn1, Rgn1, Rgn2, RGN_DIFF);

 // уничтожаем вспомогательный регион

 DeleteObject(Rgn2);

 // Назначаем регион с дыркой окну

 SetWindowRgn(Handle, Rgn1, True);

 // обратите внимание, что регион, назначенный окну, нигде

 // не уничтожается. После выполнения функции SetWindowRgn

 // регион переходит в собственность системы, и она сама

 // уничтожит его при необходимости

end;

Сообщения, поступающие с панели, перехватываются через ее свойство

WindowProc
(подробно эта технология описана в первой части данной главы, здесь мы ее касаться не будем). Сообщение
WM_NCHITTEST
будем обрабатывать так, чтобы при попадании мыши на рамку панели возвращались такие значения, чтобы за эту рамку можно было тянуть. В обработчике сообщения
WM_SIZE
панели изменяем регион так, чтобы он соответствовал новому размеру панели. Все, дырка с изменяемыми размерами готова. Теперь нужно научить "дырку" менять размеры при изменении размеров окна, если окно стало слишком маленьким, чтобы вместить в себя дырку. Осталось только немного "навести красоту". "Красота" заключается в том, чтобы пользователь не мог уменьшить размеры дырки до нуля и увеличить так, чтобы она вышла за пределы окна, а также уменьшить окно так. чтобы дырка оказалась за пределами окна. Первая из этих задач решается просто: добавляется обработчик сообщения
WM_SIZING
для дырки таким образом, чтобы ее размеры не могли стать меньше, чем
MinHoleSize
на
MinHoleSize
пикселов, а границы нельзя было придвинуть к границам окна ближе, чем на
HoleDistance
пикселов. Вторая задача решается еще проще: в обработчике
WM_SIZE
дырки меняем свойство
Constraints
формы таким образом, чтобы пользователь не мог слишком сильно уменьшить окно. Теперь окно с дыркой ведет себя корректно при любых действиях пользователя с дыркой. Получившийся в результате код обработчика сообщений панели приведен в листинге 1.53.

Листинг 1.53. Обработчик сообщений панели, образующей "дырку"

procedure TFormHole.PanelWndProc(var Msg: TMessage);

var

 Pt: TPoint;

 R: TRect;

begin

 POldPanelWndProc(Msg);

 if Msg.Msg = WM_NCHITTEST then

 begin

// Вся хитрость обработки сообщения WM_NCHITTEST

// заключается в правильном переводе экранных координат

// в клиентские и в несколько муторной проверке попадания

// мыши на сторону рамки или в ее угол. Дело упрощается

// тем, что у панели нет неклиентской части, поэтому

// верхний левый угол окна и верхний левый угол клиентской

// части совпадают.

Pt := PanelHole.ScreenToClient(Point(Msg.LParamLo, Msg.LParamHi));

if Pt.X < BorderMouseSensivity then

if Pt.Y < CornerMouseSensivity then Msg.Result := HTTOPLEFT

else

if Pt.Y >= PanelHole.Height - CornerMouseSensivity then

Msg.Result := HTBOTTOMLEFT

else Msg.Result := HTLEFT

else

if Pt.X >= PanelHole.Width - BorderMouseSensivity then

if Pt.Y < CornerMouseSensivity then Msg.Result := HTTOPRIGHT

else

if Pt.Y >= PanelHole.Height - CornerMouseSensivity then

Msg.Result := HTBOTTOMRIGHT

else Msg.Result := HTRIGHT

else

if Pt.Y < BorderMouseSensivity then

if Pt.X < CornerMouseSensivity then Msg.Result := HTTOPLEFT

else

if Pt.X >= PanelHole.Width - CornerMouseSensivity then

Msg.Result := HTTOPRIGHT

else Msg.Result := HTTOP

else

if Pt.Y >= PanelHole.Height - BorderMouseSensivity then

if Pt.X < CornerMouseSensivity then

Msg.Result := HTBOTTOMLEFT

else

if Pt.X >= PanelHole.Width - CornerMouseSensivity then

Msg.Result := HTBOTTOMRIGHT

else Msg. Result := HTBOTTOM;

 end

 else if Msg.Msg = WM_SIZE then

 begin

// Пересчитываем регион SetRegion;

// Устанавливаем новые ограничения для размеров окна.

// учитывающие новое положение дырки

Constraints.MinWidth :=

Width - ClientWidth + PanelHole.Left + MinHoleSize + HoleDistance;

Constraints.MinHeight :=

Height - ClientHeight + PanelHole.Top + MinHoleSize + HoleDistance;

Поделиться с друзьями: