-
1
- #1
GreenKnight
Programmer
To create a graphic component with transparent sections, add a message handler for CM_HITTEST.
This passes a point to the control, and expects an 'HT' constant return value (see the Windows unit). Return HTNOWHERE if the point is in an invisible section, or HTCLIENT if it's in the other bit.
Example:
Say I have a subclass of a TGraphicControl called TMyCircle, which displays a circle of Diameter 'fDiameter', but I want the bits outside the circle to be transparent.
When you, say, click in the circle, the TMyCircle gets a 'MouseDown' event, but if you click on the control (outside the circle) the MouseDown will be sent and handled by the control/form behind the TMyCircle.
You could also use this to create more complex shapes.
[tt]
interface
uses
Windows, Messages, Controls, Classes, Graphics, Math, SysUtils;
type
TMyCircle = class( TGraphicControl )
public
fDiameter: integer;
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
procedure Paint; override;
function HitTest( const pX, pY : integer ) : boolean;
{ ... }
end;
implementation
procedure TMyCircle.CMHitTest(var Message: TCMHitTest);
begin
if Self.HitTest( Message.XPos, Message.YPos ) then
begin
Message.Result := HTCLIENT;
end
else
begin
Message.Result := HTNOWHERE;
end;
end;
function TMyCircle.HitTest(const pX, pY: integer): boolean;
var
lCentre : Double;
begin
{ pX, pY are relative to the TopLeft of the control. }
{ Only need one "lCentre" since the graphic is symmetrical. }
lCentre := Self.Width / 2;
{ Return True if twice the distance from the point to the centre is less than the diameter. }
{ Uses the Euler's distance formula. }
Result := ( 2 * Abs( Sqrt( Sqr( pX - lCentre ) + Sqr( pY - lCentre ) ) ) ) <= Self.fDiameter;
end;
procedure TMyCircle.Paint;
var
lDrawRect : TRect;
begin
{ Draw a circle within the border }
lDrawRect.Top := Self.BorderWidth;
lDrawRect.Left := Self.BorderWidth;
lDrawRect.Bottom := lDrawRect.Top + Self.fDiameter;
lDrawRect.Right := lDrawRect.Left + Self.fDiameter;
Canvas.Ellipse( lDrawRect );
end;
[/tt]
This passes a point to the control, and expects an 'HT' constant return value (see the Windows unit). Return HTNOWHERE if the point is in an invisible section, or HTCLIENT if it's in the other bit.
Example:
Say I have a subclass of a TGraphicControl called TMyCircle, which displays a circle of Diameter 'fDiameter', but I want the bits outside the circle to be transparent.
When you, say, click in the circle, the TMyCircle gets a 'MouseDown' event, but if you click on the control (outside the circle) the MouseDown will be sent and handled by the control/form behind the TMyCircle.
You could also use this to create more complex shapes.
[tt]
interface
uses
Windows, Messages, Controls, Classes, Graphics, Math, SysUtils;
type
TMyCircle = class( TGraphicControl )
public
fDiameter: integer;
procedure CMHitTest(var Message: TCMHitTest); message CM_HITTEST;
procedure Paint; override;
function HitTest( const pX, pY : integer ) : boolean;
{ ... }
end;
implementation
procedure TMyCircle.CMHitTest(var Message: TCMHitTest);
begin
if Self.HitTest( Message.XPos, Message.YPos ) then
begin
Message.Result := HTCLIENT;
end
else
begin
Message.Result := HTNOWHERE;
end;
end;
function TMyCircle.HitTest(const pX, pY: integer): boolean;
var
lCentre : Double;
begin
{ pX, pY are relative to the TopLeft of the control. }
{ Only need one "lCentre" since the graphic is symmetrical. }
lCentre := Self.Width / 2;
{ Return True if twice the distance from the point to the centre is less than the diameter. }
{ Uses the Euler's distance formula. }
Result := ( 2 * Abs( Sqrt( Sqr( pX - lCentre ) + Sqr( pY - lCentre ) ) ) ) <= Self.fDiameter;
end;
procedure TMyCircle.Paint;
var
lDrawRect : TRect;
begin
{ Draw a circle within the border }
lDrawRect.Top := Self.BorderWidth;
lDrawRect.Left := Self.BorderWidth;
lDrawRect.Bottom := lDrawRect.Top + Self.fDiameter;
lDrawRect.Right := lDrawRect.Left + Self.fDiameter;
Canvas.Ellipse( lDrawRect );
end;
[/tt]