Статьи
Во многих приложениях можно заметить, что кнопки не прямоугольной формы
(например, круглой) становятся активными тогда, когда указатель мыши ещё не
находится на них:
Получается, кнопка выглядит как круглая, но на самом деле она прямоугольная.
Чтобы избежать этого предлагаю воспользоваться регионами (тип Windows -
HRGN).
Общая схема такая: создаём для кнопки регион и затем проверяем, находится ли
указатель мыши (или другого манипулятора
) в этом регионе.
Для написания примера воспользуемся средой Borland Delphi.
Сначала разместим на форме экземпляр компонента изображения TImage.
Загрузим какое-нибудь изображение окружности (свойство Picture).
Далее изменим следующие свойства:
Свойство
|
Значение
|
AutoSize
|
True
|
Transparent
|
True
|
Таким образом, изображение будет прозрачным и размер подгонится по размеру
изображения.
Я также добавил TStatusBar для отображения текста (понадобится далее).
Далее объявляем переменную региона кнопки и переменную булевого типа, которая
показывает, находится ли указатель мыши в регионе:
//Круглый регион
RgnCircle: HRGN;
//Находится ли указатель (мыши) в круглом регионе
inRgnCircle: Bool;
|
Затем создаём регион (круглый) и инициируем переменную inRgnCircle
(указывающую находится ли указатель мыши в регионе) значением
False:
//Круглый регион
RgnCircle := CreateEllipticRgn(0, 0,
ImgCircle.Width, ImgCircle.Height);
//Указатель (мыши) находится не в круглом регионе
inRgnCircle := False;
|
Теперь напишем метод для обработки события изображения TImage
OnMouseMove (когда курсор мыши перемещается по изображению):
procedure TFrmMain.ImgCircleMouseMove(Sender: TObject; Shift: TShiftState;
X, Y: Integer);
begin
//Если указатель (мыши) в круглом регионе
if PtInRegion(RgnCircle, X, Y) then
begin
//Указатель (мыши) находится в круглом регионе
inRgnCircle := True;
//Курсор в виде руки
Cursor := crHandPoint;
end else
begin
//Указатель (мыши) находится не в круглом регионе
inRgnCircle := False;
//Стандартный курсор
Cursor := crDefault;
//Очищаем текст в строке состояния
StsBrMain.SimpleText := '';
end;
end;
|
В этом коде используется функция PtInRegion(), которая проверяет
находятся ли указанные координаты точки внутри заданного региона. Если указатель
мыши внутри региона, то вид указателя применят форму руки и переменная
inRgnCircle принимает значение True (это пригодится нам далее).
Указатель мыши может покинуть сразу и заданный регион и само изображение,
поэтому необходимо обработать событие OnMouseMove самой формы:
procedure TFrmMain.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
//Указатель (мыши) находится не в круглом регионе
inRgnCircle := False;
//Стандартный курсор
Cursor := crDefault;
//Очищаем текст в строке состояния
StsBrMain.SimpleText := '';
end;
|
Теперь обработаем клик кнопкой мыши по изображению (событие OnClick):
procedure TFrmMain.ImgCircleClick(Sender: TObject);
begin
//Если указатель (мыши) в круглом регионе
if inRgnCircle then
begin
//Текст в строке состояния
StsBrMain.SimpleText := 'Клик по круглому изображению.';
end;
end;
|
Здесь проверяется состояние булевой переменной inRgnCircle Если
указатель мыши находится в регионе, то в StatusBar'е появляется текст
говорящий, что нажата кнопка.
Также ещё нужно учесть, что функция PtInRegion() проверяет, находится ли
точка внутри региона. То есть "обводку" региона не учитывает. Поэтому
координаты региона лучше использовать, как в следующем коде:
//Круглый регион
RgnCircle := CreateEllipticRgn(-1, -1,
ImgCircle.Width + 2, ImgCircle.Height + 2);
|
Разумеется, что регионы можно создавать произвольной формы, простые (да и
сложные) регионы можно комбинировать в сложные. Для этого есть стандартные
функции WinApi: CreateRectRgn(), CreateRoundRectRgn(),
CreateEllipticRgn(), CreatePolygonRgn().
Если изображение кнопки слишком сложное, то можно воспользоваться функцией
BitmapToRgn():
function BitmapToRgn(Image: TBitmap): HRGN;
var
TmpRgn: HRGN;
x, y: integer;
ConsecutivePixels: integer;
TranparentColor: TColor; //Прозрачный цвет
CurrentPixel: TColor;
CurrentColor: TColor;
begin
Result := CreateRectRgn(0, 0, Image.Width, Image.Height);
if (Image.Width = 0) or (Image.Height = 0) then
exit;
TranparentColor := Image.Canvas.Pixels[0, 0]; //Прозрачный цвет
for y := 0 to Image.Height - 1 do
begin
CurrentColor := Image.Canvas.Pixels[0, y];
ConsecutivePixels := 1;
for x := 0 to Image.Width - 1 do
begin
CurrentPixel := Image.Canvas.Pixels[x, y];
if CurrentColor = CurrentPixel then
inc(ConsecutivePixels)
else
begin
// Входим в новую зону
if CurrentColor = TranparentColor then
begin
TmpRgn := CreateRectRgn(x - ConsecutivePixels, y, x, y + 1);
CombineRgn(Result, Result, TmpRgn, RGN_DIFF);
DeleteObject(TmpRgn);
end;
CurrentColor := CurrentPixel;
ConsecutivePixels := 1;
end;
end;
if (CurrentColor = TranparentColor) and (ConsecutivePixels > 0) then
begin
TmpRgn := CreateRectRgn(x - ConsecutivePixels, y, x, y + 1);
CombineRgn(Result, Result, TmpRgn, RGN_DIFF);
DeleteObject(TmpRgn);
end;
end;
end;
|
Эта функция популярна и публикуется во многих источниках. Я чучуть изменил её,
добавив определение прозрачного цвета по точке (0, 0) изображения. В функции
нет ничего сложного. Она просто комбинирует непрозрачные линии, которые находит
в изображении.
Конечно, в приведённом примере есть некоторые недостатки. Но сама идея, думаю,
ясна .
EXE'шник: rgn_btn.zip (26.3KB).
Исходники: rgn_btn_c.zip (38.4KB).
В них примеры с круглым изображением, изображением прямоугольника
со скруглёнными краями и сложного изображения.
Исходные коды написаны в Delphi 7, должны легко переконвертироваться
для BDS.
▲Вверх
Статьи
|