О чём не пишут в книгах по Delphi
Шрифт:
// может отстоять курсор вглубь от края панели, чтобы его
// положение расценивалось как попадание в рамку.
BorderMouseSensivity = 3;
// Зона чувствительности угла рамки панели - на сколько пикселов
// может отстоять курсор от угла панели, чтобы его
// положение расценивалось как попадание в угол рамки.
CornerMouseSensivity = 15;
// Толщина рамки дырки, использующаяся при вычислении региона
HoleBorder = 3;
//
Минимальная ширина и высота дырки
MinHoleSize = 10;
// Смещение стрелки относительно соответствующего угла
ArrowOffset = 8;
Теперь приступаем к созданию программы. На форму "кладем" панель. С помощью функции
SetWindowRgn
устанавливаем такую форму окна, чтобы от панели была видна только рамка, а на всю внутреннюю часть панели пришлась дырка. Рамку выбираем такую, чтобы панель выглядела утопленной, так края дырки будут выглядеть естественней. Для расчета региона используется метод SetRegion
(листинг 1.52), он вызывается всегда, когда нужно изменить регион окна. Листинг 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
Поделиться:
Популярные книги
Газлайтер. Том 8
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
На Ларэде
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
Охота на попаданку. Бракованная жена
Любовные романы:
любовно-фантастические романы
5.60
рейтинг книги
Кай из рода красных драконов
1. Красная кость
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Хозяйка Проклятой Пустоши. Книга 2
2. Хозяйка Проклятой Пустоши
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Безумный Макс. Поручик Империи
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Потусторонний. Книга 2
2. Господин Артемьев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Чапаев и пустота
Проза:
современная проза
8.39
рейтинг книги
Солнечный корт
4. Все ради игры
Фантастика:
зарубежная фантастика
5.00
рейтинг книги
Лютая
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Ведьмак (большой сборник)
Ведьмак
Фантастика:
фэнтези
9.29
рейтинг книги
Наследие Маозари 4
4. Наследие Маозари
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Ученик
1. Тай Фун
Фантастика:
фэнтези
5.00
рейтинг книги
Начальник милиции. Книга 5
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00