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

на главную - закладки

Жанры

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

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

Шрифт:

1.3.3. Обобщающий пример 3 — "Дырявое" окно

В этом примере мы создадим "дырявое" окно. Те, кто уже знаком с функцией

SetWindowRgn
, знает, что сделать "дырку" в окне или придать ему какую-либо другую необычную форму не так уж и сложно. Но мы здесь пойдем дальше: у дырки в нашем окне будет рамка, и пользователь сможет изменять размеры и положение дырки так же, как он может изменять положение и размеры окна. Как это выглядит, показано на рис. 1.14.

Рассмотрим те средства, которые нам понадобятся для реализации этого.

1.3.3.1. Сообщение WM_NCHCHITTEST

Каждое окно в Windows делится на две области: клиентскую и не клиентскую. Клиентской называется та

область, в которой отображается содержимое окна. Неклиентская область — это различные служебные области окна: рамка, заголовок, полосы прокрутки, главное меню и т.п. Положение клиентской части окна относительно неклиентской определяет само окно при обработке сообщения
WM_NCCALCRECT
. Многие окна (особенно различные элементы управления) вообще не имеют неклиентской части.

Некоторые сообщения для клиентской части окна имеют аналоги для неклиентской. Например, перерисовка клиентской области осуществляется с помощью сообщения

WM_PAINT
, а неклиентской —
WM_NCPAINT
. Нажатие левой кнопки мыши над клиентской частью окна генерирует сообщение
WM_LBUTTONDOWN
, а над неклиентской —
WM_NCLBUTTONDOWN
и т.п. Неклиентская область неоднородна: в нее входит заголовок, кнопки сокрытия, разворачивания и закрытия окна, иконка системного меню, главное меню, вертикальная и горизонтальная полосы прокрутки и рамка. Рамка тоже неоднородна — она имеет левую, правую, верхнюю и нижнюю границы и четыре угла. Сообщение
WM_NCCALCSIZE
позволяет выяснить, какая область окна является неклиентской, но не позволяет узнать, где какая часть неклиентской области находится. Эта задача решается с помощью другого сообщения —
WM_NCHITTEST
. В качестве входных параметров
WM_NCHITTEST
получает координаты точки, а результат кодирует, к какой части окна относится эта точка (например,
HTCLIENT
означает, что точка принадлежит к клиентской части окна,
HTCAPTION
 — к заголовку,
HTLEFT
— к левой границе рамки, меняющей размер, и т.п.).

Рис. 1.14. "Дырявое" окно

При любых событиях от мыши система начинает с того, что посылает окну сообщение

WM_NCHITTEST
с координатами положения мыши. Получив результат, система решает, что делать дальше. В частности, при нажатии левой кнопки мыши окну посылается
WM_NCHITTEST
. Затем, если результатом был
HTCLIENT
, посылается сообщение
WM_LBUTTONDOWN
, в противном случае —
WM_NCLBUTTONDOWN
. При каждом перемещении мыши окно также получает
WM_NCHITTEST
— это позволяет системе постоянно отслеживать, над какой частью окна находится курсор, при необходимости меняя его вид (как, например, при прохождении курсора над рамкой).

Что будет, если подменить обработчик

WM_NCHITTEST
? Например, так, чтобы при попадании точки в клиентскую часть окна он возвращал не
HTCLIENT
, а
HTCAPTION
? Это приведет к тому, что любые события от мыши над клиентской областью будут восприниматься так же, как над заголовком. Например, можно будет взять окно за клиентскую часть и переместить его, а двойной щелчок на ней приведет к разворачиванию окна. Однако это полностью блокирует нормальную реакцию на мышь, потому что вместо клиентских "мышиных" сообщений окно будет получать неклиентские.

С практической точки зрения окно, которое можно таскать за любую точку, обычно не очень интересно (особенно это касается приложений, разработанных с помощью VCL: на мышь перестанет правильно реагировать не только само окно, но и расположенные на нем неоконные элементы управления). Однако обработчик

WM_NCHITTEST
можно сделать более интеллектуальным и получить довольно интересные эффекты. Например, положив на форму панель и переопределив у панели обработчик
WM_NCHITTEST
таким образом, чтобы при нахождении мыши около границ панели возвращался результат, соответствующий различным частям рамки с изменяемым размером, можно получить панель,
размеры которой пользователь программы сможет изменять: система будет реагировать на эту область панели как на обычную рамку, которую можно взять и потянуть. (Пример такой панели можно увидеть в статье "Компонент, который меняет свои размеры в режиме run-time аналогично тому, как это происходит в design-time" Фантазия может подсказать и многие другие способы получения интересных эффектов с помощью
WM_NCHITTEST
.

1.3.3.2. Регионы

Регионы — это особые графические объекты, представляющие собой области произвольной формы. Ограничений на форму региона нет, они даже не обязаны быть связными. Существует ряд функций для создания регионов простых форм (

CreateRectRgn
,
CreateEllipticRgn
,
CreatePolygonRgn
и т.п.), а также функция
СombineRgn
для объединения регионов различными способами. Все это вместе позволяет получать регионы любых форм. Область применения регионов достаточно широка. Ранее мы уже видели, как с помощью регионов можно ограничить область вывода графики. Здесь же мы будем с помощью функции
SetWindowRgn
изменять форму окна, придавая ему форму заданного региона.

1.3.3.3. Сообщения WM_SIZE и WM_SIZING

События

WM_SIZE
и
WM_SIZING
позволяют окну реагировать на перемещение его пользователем. В "классическом" варианте, когда пользователь начинает тянуть рамку окна, на экране рисуется "резиновый" прямоугольник, соответствующая сторона или угол которого движется за курсором мыши. Окно получает сообщение
WM_SIZING
при каждом изменении размера этого прямоугольника. Параметр
lParam
при этом содержит указатель на структуру
TRect
с новыми координатами прямоугольника. Окно может не только прочитать эти координаты, но и изменить их, блокировав тем самым нежелательные изменения размера. На этом, в частности, основано использование свойства
Constraints
: если размер окна при перемещении становится меньше или больше заданного, при обработке сообщения
WM_SIZING
размер увеличивается или уменьшается до необходимого. Параметр
wParam
содержит информацию о том, за какую сторону или угол тянет пользователь, чтобы программа знала, координаты какого из углов прямоугольника нужно смещать, если возникнет такая необходимость.

После того как пользователь закончит изменять размеры окна и отпустит кнопку мыши, окно получает сообщение

WM_SIZE
. При получении этого сообщения окно должно перерисовать себя с учетом новых размеров. (Окно получает сообщение
WM_SIZE
после изменения его размеров по любой причине, а не только из-за действий пользователя.)

Описанный "классический" вариант в чистом виде существует только в Windows 95. Во всех более поздних версиях по умолчанию включена опция отображения содержимого окна при перетаскивании и изменении размеров (начиная с Windows ХР эта опция не только включается по умолчанию, но и не отключается средствами пользовательского интерфейса). В таком режиме при изменении размеров окна вместо прямоугольника "резиновым" становится само окно, и любое перемещение мыши при изменении размеров приводит к перерисовке окна. В этом режиме окно получает сообщение

WM_SIZE
каждый раз после сообщения
WM_SIZING
, а не только при завершении изменения размеров. Но в целом логика этих сообщений остается прежней, просто с точки зрения программы это выглядит так, как будто пользователь изменяет размеры окна "по чуть-чуть".

1.3.3.4. А теперь — все вместе

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

Для начала объявим несколько констант, которые нам потребуются при вычислении размеров дырки и т.п. (листинг 1.51).

Листинг 1.51. Константы примера WndHole

const

 // минимальное расстояние от дырки до края окна

 HoleDistance = 40;

 // Зона чувствительности рамки панели - на сколько пикселов

Поделиться:
Популярные книги

Газлайтер. Том 8

Володин Григорий
8. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 8

На Ларэде

Кронос Александр
3. Лэрн
Фантастика:
фэнтези
героическая фантастика
стимпанк
5.00
рейтинг книги
На Ларэде

Охота на попаданку. Бракованная жена

Герр Ольга
Любовные романы:
любовно-фантастические романы
5.60
рейтинг книги
Охота на попаданку. Бракованная жена

Кай из рода красных драконов

Бэд Кристиан
1. Красная кость
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Кай из рода красных драконов

Хозяйка Проклятой Пустоши. Книга 2

Белецкая Наталья
2. Хозяйка Проклятой Пустоши
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Хозяйка Проклятой Пустоши. Книга 2

Безумный Макс. Поручик Империи

Ланцов Михаил Алексеевич
1. Безумный Макс
Фантастика:
героическая фантастика
альтернативная история
7.64
рейтинг книги
Безумный Макс. Поручик Империи

Потусторонний. Книга 2

Погуляй Юрий Александрович
2. Господин Артемьев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Потусторонний. Книга 2

Чапаев и пустота

Пелевин Виктор Олегович
Проза:
современная проза
8.39
рейтинг книги
Чапаев и пустота

Солнечный корт

Сакавич Нора
4. Все ради игры
Фантастика:
зарубежная фантастика
5.00
рейтинг книги
Солнечный корт

Лютая

Шёпот Светлана Богдановна
Любовные романы:
любовно-фантастические романы
6.40
рейтинг книги
Лютая

Ведьмак (большой сборник)

Сапковский Анджей
Ведьмак
Фантастика:
фэнтези
9.29
рейтинг книги
Ведьмак (большой сборник)

Наследие Маозари 4

Панежин Евгений
4. Наследие Маозари
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Наследие Маозари 4

Ученик

Губарев Алексей
1. Тай Фун
Фантастика:
фэнтези
5.00
рейтинг книги
Ученик

Начальник милиции. Книга 5

Дамиров Рафаэль
5. Начальник милиции
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Начальник милиции. Книга 5