Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Функции работы с окнами и регионами

Регионы в Delphi

Оглавление

  • Общее описание
  • Функции создания/удаления регионов
  • Функции отображения регионов
  • Прочие функции
  • Функции работы с окнами и регионами
  • Создание региона
  • Комбинирование регионов
  • Рисование регионов
  • Прочие функции
  • Сохранение и загрузка региона

 

Общее описание

Windows API предоставляет набор функций, позволяющих описать произвольную (при желании - достаточно сложную) геометрическую фигуру, которая потом может использоваться при работе с окнами, или, в терминологии Delphi, элементами управления. Использование может заключаться, например, в отрисовке на холсте, в задании специфического региона обновления окна, и т.д. Помимо таких вот относительно невинных возможностей, технология регионов позволяяет также глумиться над благородными очертаниями любого потомка TWinControl (иными словами, любым контролом, имеющим Handle, aka TForm, TButton, и т.д.). Особенно широко регионы используются в формах, при их отрисовке и обновлении.

Итак, для начала, давайте разберёмся, что такое этот самый регион.

Регион определяет следующим образом:

Регионом называется прямоугольник, полигон или эллипс (либо комбинация двух или более этих фигур), которые могут быть заполненны, нарисованы, инвертированы, обрамлены и могут использоваться для определения местонахождения курсора (имеется в виду, что есть стандартная функция, определяющая входит ли точка (X,Y) в наш регион).

Существуют три основных типа регионов: прямоугольные, эллиптические, и полигональные. Регионы могут комбинироваться между собой с применением логических операций OR, XOR и т.д.

С точки зрения Windows, регион является обыкновенным объектом GDI, задаваемым дескриптором HRGN. В Delphi также доступен этот тип, и объявление его выглядит следующим образом:

HRGN = type LongWord;

Как видим, ничего сверхъестественного. По сути это указатель на некую структуру в памяти. Структура эта описана следующим образом:

TRgnData = _RGNDATA;

_RGNDATA = record

rdh: TRgnDataHeader;
Buffer: array[0..0] of CHAR;
Reserved: array[0..2] of CHAR;
end;

_RGNDATA объявлена немного иначе:

TRgnDataHeader = _RGNDATAHEADER;

_RGNDATAHEADER = packed record
dwSize: DWORD;
iType: DWORD;
nCount: DWORD;
nRgnSize: DWORD;
rcBound: TRect;
end;

Вот его стоит рассмотреть подробнее.

dwSize:

Указывает размер заголовка (т.е. _RGNDATAHEADER) в байтах;

iType:

Тип региона, всегда равен RDH_RECTANGLES = 1;

nCount:

Кол-во прямоугольников, из которых составлен регион;

nRgnSize:

Размер буфера, необходимого для получения структуры TRect, указывающей координаты прямоугольников, составляющих регион. Если размер неизвестен, поле может быть равно нулю;

rcBound:

Координаты прямоугольника, в который вписан регион.

 

Вот именно эта структура и задаёт регион, как таковой, и именно с ней работают практически все функции API.

Функции создания/удаления регионов

Функция Синтаксис Описание
CombineRgn function CombineRgn (p1, p2, p3: HRGN; p4: Integer): Integer; stdcall; Создаёт новый регион p1, комбинируя регионы p2 и p3 на основе режима p4
CreateEllipticRgn function CreateEllipticRgn (p1, p2, p3, p4: Integer): HRGN; stdcall; Создаёт эллиптический регион по четырём координатам
CreateEllipticRgnIndirect function CreateEllipticRgnIndirect (const p1: TRect): HRGN; stdcall; Создаёт эллиптический регион, впсанный в заданный прямоугольник p1
CreatePolygonRgn function CreatePolygonRgn (const Points; Count, FillMode: Integer): HRGN; stdcall; Создаёт полигональный регион по массиву точек Points с числом вершин Count с режимом заливки FillMode
CreatePolyPolygonRgn function CreatePolyPolygonRgn (const pPtStructs; const pIntArray; p3, p4: Integer): HRGN; stdcall; Создаёт регион, состоящий из серии полигонов, задаваемых массивом точек pPtStructs, число вершин каждого из которых указывается в массиве pIntArray с общим числом вершин p3 и режимом заливки p4
CreateRectRgn function CreateRectRgn (p1, p2, p3, p4: Integer): HRGN; stdcall; Создаёт прямоугольный регион по четырём координатам
CreateRectRgnIndirect function CreateRectRgnIndirect (const p1: TRect): HRGN; stdcall; Создаёт прямоугольный регион по заданному прямоугольнику p1
CreateRoundRectRgn function CreateRoundRectRgn (p1, p2, p3, p4, p5, p6: Integer): HRGN; stdcall; Создаёт прямоугольный регион по координатам p1-p4 с эллиптически закруглёнными краями с высотой эллипса скругления p5 и шириной p6
ExtCreateRegion function ExtCreateRegion (XForm: PXForm; Count: DWORD; const RgnData: TRgnData): HRGN; stdcall; Создаёт регион по его данным RgnData размером Count с указанной трансформацией XForm
DeleteObject function DeleteObject (p1: HGDIOBJ): BOOL; stdcall; Удаляет регион и освобождает все, занятые под него ресурсы

Функции отображения регионов

Функция Синтаксис Описание
SetPolyFillMode function SetPolyFillMode (DC: HDC; PolyFillMode: Integer): Integer; stdcall; Устанавливает режим заливки регионов PolyFillMode для контекста dc
GetPolyFillMode function GetPolyFillMode (DC: HDC): Integer; stdcall; Возвращает текущий режим заливки регионов на конктексте dc
PaintRgn function PaintRgn (DC: HDC; RGN: HRGN): BOOL; stdcall; Рисует регион rgn на контексте dc текущей кистью контекста
FillRgn function FillRgn (DC: HDC; hrgn: HRGN; hbr: HBRUSH): BOOL; stdcall; Заливает регион hrgn на контексте dc с заданной кистью hbr
FrameRgn function FrameRgn (DC: HDC; hrgn: HRGN; hbr: HBRUSH; Width, Height: Integer): BOOL; stdcall; Рисует на контексте dc рамку вокруг региона hrgn кистью hbr соответствующей ширины и высоты Width и Height

Прочие функции

Функция Синтаксис Описание
EqualRgn function EqualRgn (p1, p2: HRGN): BOOL; stdcall; Проверяет, являются ли размеры и форма регионов p1 и p2 идентичными
InvertRgn function InvertRgn (DC: HDC; p2: HRGN): BOOL; stdcall; Инвертирует цвета региона p2 на контексте dc
OffsetRgn function OffsetRgn (RGN: HRGN; XOffset, YOffset: Integer): Integer; stdcall; Сдвигает регион rgn по осям X и Y на XOffset и YOffset соответственно
SetRectRgn function SetRectRgn (Rgn: HRgn; X1, Y1, X2, Y2: Integer): BOOL; stdcall; Конвертирует регион rgn в прямоугольный, заданный четырьмя координатами
GetRgnBox function GetRgnBox (RGN: HRGN; var p2: TRect): Integer; stdcall; Возвращает прямоугольник p2, в который вписан указанный регион rgn
RectInRegion function RectInRegion (RGN: HRGN; const Rect: TRect): BOOL; stdcall; Определяет, "влазит" ли пряпоугольник Rect в границы региона rgn
PtInRegion function PtInRegion (RGN: HRGN; X, Y: Integer): BOOL; stdcall; Определяет, входит ли точка (X,Y) в регион rgn
GetRegionData function GetRegionData (RGN: HRGN; p2: DWORD; p3: PRgnData): DWORD; stdcall; Заполняет данными региона rgn буфер PRgnData размером p2, при pRgnData = nil - возвращает размер региона в памяти.

Функции работы с окнами и регионами

Функция Синтаксис Описание
SetWindowRgn function SetWindowRgn(hWnd: HWND; hRgn: HRGN; bRedraw: BOOL): Integer; stdcall; Устанавливает для окна с дескриптором hWnd регион отображения hRgn, при взведённом флаге bRedraw - перерисовывая окно
GetWindowRgn function GetWindowRgn(hWnd: HWND; hRgn: HRGN): Integer; stdcall; Возвращает регион отображения hRgn для окна с дескриптором hWnd

Создание региона

Запускаем среду, заводим новый проект, тщательно сохраняем куда попало. В обработчике OnCreate пишем первое, что, по идее, приходит в голову:

procedure TForm1.FormCreate(Sender: TObject);
var
r: hRGN;
begin
r:= CreateEllipticRgn (0, 0, Width, Height);
try
SetWindowRgn (Handle, r, TRUE);
finally
DeleteObject (r);
end;
end;

Результатом запуска окажется симпатичный эллипс на экране. А теперь попробуйте дописать такой простенький обработчик на OnKeyPress:

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);
var
bmp: TBitmap;
begin
if Key = 'p' then begin
bmp:= GetFormImage;
try
bmp.SaveToFile ('c:\temp.bmp');
finally
bmp.Free;
end;
end;
end;

Аналогичным образом создаются и другие типы регионов, единственное внимание стоит, пожалуй, уделить полигональным. Начнём с самого простого. В делкарации функции есть интересный параметр: FillMode. Согласно SDK он может принимать два значения - ALTERNATE и WINDING. Со вторым всё просто - вычисляются границы, и всё, что между - заливается текущей кистью. А вот первый при заливке анализирует, по какую сторону ребра (чётную или нечётную) он находится. Что это означает, мы увидим на следующем примере.

 

procedure TForm1.FormCreate(Sender: TObject);
var
ap: array [1..5] of TPoint;
r: hRGN;
begin
ap [1]:= Point (Width div 2, 0);
ap [2]:= Point (Width div 3 * 2, Height);
ap [3]:= Point (0, Height div 3);
ap [4]:= Point (Width, Height div 3);
ap [5]:= Point (Width div 3, Height);
r:= CreatePolygonRgn (ap, 5, WINDING);
try
SetWindowRgn (Handle, r, TRUE);
finally
DeleteObject (r);
end;
end;

Теперь разберёмся, как работает функция CreatePolyPolygonRgn. Её предназначение в том, чтобы рисовать сразу несколько регионов, которые могу даже не пересекаться. А могу - и пересекаться, именно для этого и нужен параметр FillMode. Поглядим, как это выглядит, и что оно делает:

procedure TForm1.FormCreate(Sender: TObject);
var
ap: array [1..7] of TPoint;
av: array [1..2] of integer;
r: hRGN;
begin
ap [1]:= Point (0, 0);
ap [2]:= Point (Width, 0);
ap [3]:= Point (Width, Height div 2);
ap [4]:= Point (0, Height div 2);
ap [5]:= Point (Width div 2, Height div 2);
ap [6]:= Point (0, Height);
ap [7]:= Point (Width, Height);
av [1]:= 4;
av [2]:= 3;
r:= CreatePolyPolygonRgn (ap, av, 2, WINDING);
try
SetWindowRgn (Handle, r, TRUE);
finally
DeleteObject (r);
end;
end;

Комбинирование регионов

Результат комбинирования определяется четвёртым параметром функции CombineRgn. Давайте посмтрим, какие значения он может принимать.

RGN_ADD

Пересечение двух регионов p2 и p3

RGN_COPY

Создаёт копию региона p2

RGN_DIFF

Вернёт часть p2, не являющуюся частью p3

RGN_OR

Объединение двух регионов p2 и p3

RGN_XOR

Объединяет регионы p2 и p3, исключая пересекающиеся области

Возвращаемые значения могут быть NULLREGION (пустой регион), SIMPLEREGION (один прямоугольник), COMPLEXREGION (всё остальное) и ERROR (не создано). Посмотрим, как это выглядит на практике (дописываем предыдущий пример).

procedure TForm1.FormCreate(Sender: TObject);
var
ap: array [1..7] of TPoint;
av: array [1..2] of integer;
r, r2, r3, r4: hRGN;
begin
ap [1]:= Point (0, 0);
ap [2]:= Point (Width, 0);
ap [3]:= Point (Width, Height div 2);
ap [4]:= Point (0, Height div 2);
ap [5]:= Point (Width div 2, Height div 2);
ap [6]:= Point (0, Height);
ap [7]:= Point (Width, Height);
av [1]:= 4;
av [2]:= 3;
r:= CreatePolyPolygonRgn (ap, av, 2, WINDING);
try
r2:= CreateRoundRectRgn
(Width div 4 - 20, Height div 6 - 20,
Width div 4 + 20, Height div 6 + 20, 16, 16);
r3:= CreateRoundRectRgn
(Width div 4 * 3 - 20, Height div 6 - 20,
Width div 4 * 3 + 20, Height div 6 + 20, 16, 16);
r4:= CreateEllipticRgn
(Width div 10, Height div 9 * 3,
Width div 10 * 9, Height div 9 * 4);
try
CombineRgn (r, r, r2, RGN_XOR);
CombineRgn (r, r, r3, RGN_XOR);
CombineRgn (r, r, r4, RGN_XOR);
finally
DeleteObject (r2);
DeleteObject (r3);
DeleteObject (r4);
end;
SetWindowRgn (Handle, r, TRUE);
finally
DeleteObject (r);
end;
end;

Нерасмотренной из первой группы функций осталась только ExtCreateRgn. Я сейчас не буду на ней заостряться, скажу только, что на пару с функцией GetRegionData, она может пригодиться, например, для сохранения и загрузки регионов в файл / из файла.

Рисование регионов

Регионы нужны не только для того, чтобы резать дырки в формах. Иногда они могут оказаться довольно полезным инструментом именно в своём "родном" качестве, т.е. для отрисовки на экране достаточно сложных геометрических фигур. Например, для вывода карт, представляющих собой совокупность ломанных линий, построенных по массивам точек.

Прочие функции

Сделаем это на примере. Давайте озадачимся возможностью таскать мышкой по всей форме круг, созданный в предыдущем примере. Что нам нужно. Во-первых, запоминать, где началось перетаскивание (fStartX, fStartY). Во-вторых, флаг (fDragging), указывающий, что юзер действительно перетскивает наш регион, а не просто гоняет с экрана мух. В третьих, надо узнать, ткнул ли он на регион, а не мимо (PtInRegion). В четвёртых, надо двигать регион по мере того, как он двигает мышь (OffsetRgn). Вот, пожалуй, и всё. На этот раз текст модуля приведу полностью. Единственное что там стоит упомянуть - это свойство DoubleBuffered. Оно выставлено в TRUE, т.к. иначе появляется мерцание. Итак.


unit Unit1;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;

type
TForm1 = class(TForm)
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
private
{ Private declarations }
fDragging: boolean;
fRgn: hRGN;
fStartX,
fStartY: integer;
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
fRgn:= CreateEllipticRgn (10, 10, 200, 200);
fDragging:= FALSE;
DoubleBuffered:= TRUE;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteObject (fRgn);
end;

procedure TForm1.FormPaint(Sender: TObject);
var
bmp: TBitmap;
begin
bmp:= TBitmap.Create;
try
bmp.LoadFromFile ('C:\WINDOWS\Aieoaua e?o?aaa 16.bmp');
Canvas.Brush.Bitmap:= bmp;
PaintRgn (Canvas.Handle, fRgn);
Canvas.Brush.Color:= clBlack;
FrameRgn (Canvas.Handle, fRgn, Canvas.Brush.Handle, 2, 2);
finally
Canvas.Brush.Bitmap:= nil;
bmp.Free;
end;
end;

procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if (Button = mbLeft) and (PtInRegion (fRgn, X, Y)) then begin
fDragging:= TRUE;
fStartX:= X;
fStartY:= Y;
end;
end;

procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if (ssLeft in Shift) and fDragging then begin
OffsetRgn (fRgn, X - fStartX, Y - fStartY);
fStartX:= X;
fStartY:= Y;
Refresh;
end;
end;

procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
fDragging:= FALSE;
end;

end.

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...