(Delphi THintWindow) How to draw a transparent PNG? - delphi

I have this delphi 2010 code:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Math, ExtCtrls, pngimage;
type
TMyHintWindow = class(THintWindow)
private
FBitmap : TBitmap;
ThePNG : TPngImage;
FRegion : THandle;
procedure FreeRegion;
protected
procedure CreateParams(var Params : TCreateParams); override;
procedure Paint; override;
procedure Erase(var Message : TMessage); message WM_ERASEBKGND;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure ActivateHint(Rect : TRect; const AHint : String); Override;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender : TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
// --------------------------------------------------------------------------- //
constructor TMyHintWindow.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
FBitmap := TBitmap.Create;
FBitmap.PixelFormat := pf32bit;
FBitmap.HandleType := bmDIB;
FBitmap.Transparent := True;
FBitmap.TransparentMode := tmAuto; // }tmFixed;
FBitmap.TransparentColor := clWhite;
FBitmap.AlphaFormat := {afPremultiplied; // }afDefined;
ThePNG := TPngImage.Create;
ThePNG.Transparent := True;
ThePNG.TransparentColor := clWhite;
ThePNG.LoadFromFile('D:\project-1\tooltip.png');
FBitmap.LoadFromFile('D:\project-1\tooltip.bmp');
end;
// --------------------------------------------------------------------------- //
destructor TMyHintWindow.Destroy;
begin
FBitmap.Free;
FreeRegion;
inherited;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.CreateParams(var Params : TCreateParams);
const
CS_DROPSHADOW = $20000;
begin
inherited;
Params.Style := Params.Style - WS_BORDER;
Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.FreeRegion;
begin
if FRegion <> 0 then
begin
SetWindowRgn(Handle, 0, True);
DeleteObject(FRegion);
FRegion := 0;
end;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.ActivateHint(Rect : TRect; const AHint : String);
var
i : Integer;
begin
Caption := AHint;
Canvas.Font := Screen.HintFont;
FBitmap.Canvas.Font := Screen.HintFont;
DrawText(Canvas.Handle, PChar(Caption), Length(Caption), Rect, DT_CALCRECT or DT_NOPREFIX);
Width := 230; // (Rect.Right - Rect.Left) + 16;
Height := 61; // (Rect.Bottom - Rect.Top) + 10;
FBitmap.Width := Width;
FBitmap.Height := Height;
Left := Rect.Left;
Top := Rect.Top;
FreeRegion;
with Rect do
FRegion := CreateRoundRectRgn(1, 1, Width, Height, 3, 3);
if FRegion <> 0 then
SetWindowRgn(Handle, FRegion, True);
AnimateWindowProc(Handle, 300, AW_BLEND);
SetWindowPos(Handle, HWND_TOPMOST, Left, Top, 0, 0, SWP_SHOWWINDOW or SWP_NOACTIVATE or SWP_NOSIZE);
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Paint;
var
CaptionRect : TRect;
begin
with FBitmap.Canvas do
begin
Font.Color := clWindowText;
Brush.Style := bsClear;
end; // with
CaptionRect := Rect(25, 26, Width - 10, Height - 10);
SetBkMode(Canvas.Handle, TRANSPARENT);
DrawText(FBitmap.Canvas.Handle, PChar(Caption), Length(Caption), CaptionRect, DT_WORDBREAK OR DT_NOPREFIX);
BitBlt(Canvas.Handle, 0, 0, Width, Height, FBitmap.Canvas.Handle, 0, 0, SRCERASE{SRCCOPY});
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Erase(var Message : TMessage);
begin
Message.Result := 0;
end;
// --------------------------------------------------------------------------- //
procedure TForm1.FormCreate(Sender : TObject);
begin
HintWindowClass := TMyHintWindow;
Button1.Hint := 'This is a nice fake tooltip!';
end;
// --------------------------------------------------------------------------- //
end.
This example has two issues:
I need to draw the PNG with the transparent borders. The image itself is here
If you run this project (form has just a button called Button1), and show the hint few times, you should realize that caption becomes bolder every time the hint is shown. I'm pretty sure I forgot a background I forgot to clear/erase, but I'm not sure how to fix that.
Can someone please tell me how to fix these two issues?

You will to have to perform adaption for position and png in cas of hint needed above, but the "engine" should work as expected. I didn't use GDI+ which would i have made much easier.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Math, ExtCtrls, pngimage;
type
TMyHintWindow = class(THintWindow)
private
FBitmap : TBitmap;
ThePNG : TPngImage;
FCurrAlpha:Integer;
FTimer:TTimer;
FActivated:Boolean;
FLastActive:Cardinal;
procedure PrepareBitmap;
procedure IncAlpha(Sender:TObject);
protected
procedure CreateParams(var Params : TCreateParams); override;
procedure Paint; override;
procedure Erase(var Message : TMessage); message WM_ERASEBKGND;
public
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure ActivateHint(Rect : TRect; const AHint : String); Override;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure FormCreate(Sender : TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1 : TForm1;
implementation
{$R *.dfm}
// --------------------------------------------------------------------------- //
constructor TMyHintWindow.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
FBitmap := TBitmap.Create;
FCurrAlpha := 1;
FTimer := TTimer.Create(self);
FTimer.Interval := 20;
Ftimer.OnTimer := IncAlpha;
Ftimer.Enabled := false;
ThePNG := TPngImage.Create;
ThePNG.Transparent := True;
ThePNG.TransparentColor := clWhite;
ThePNG.LoadFromFile('C:\temp\0o36B.png');
end;
// --------------------------------------------------------------------------- //
destructor TMyHintWindow.Destroy;
begin
FBitmap.Free;
ThePNG.Free;
inherited;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.IncAlpha(Sender:TObject);
begin
FCurrAlpha := FCurrAlpha + 10;
if FCurrAlpha >= 254 then
begin
FCurrAlpha := 254;
Ftimer.Enabled := false;
FActivated := false;
end;
invalidate;
end;
procedure TMyHintWindow.CreateParams(var Params : TCreateParams);
const
CS_DROPSHADOW = $20000;
begin
inherited;
Params.Style := Params.Style - WS_BORDER;
Params.WindowClass.Style := Params.WindowClass.Style or CS_DROPSHADOW;
end;
// --------------------------------------------------------------------------- //
type
pRGBQuadArray = ^TRGBQuadArray;
TRGBQuadArray = ARRAY[0..$effffff] OF TRGBQuad;
Procedure SetAlpha(bmp:TBitMap;Alpha:Byte);
var
pscanLine32 : pRGBQuadArray;
i,j:Integer;
begin
Bmp.PixelFormat := pf32Bit;
bmp.HandleType := bmDIB;
bmp.ignorepalette := true;
bmp.alphaformat := afDefined;
for i := 0 to bmp.Height -1 do
begin
pscanLine32 := bmp.Scanline[i];
for j := 0 to bmp.Width -1 do
begin
pscanLine32[j].rgbReserved := Alpha;
pscanLine32[j].rgbBlue := 0;
pscanLine32[j].rgbRed := 0;
pscanLine32[j].rgbGreen := 0;
end;
end;
end;
Procedure ResetSetAlpha(bmp:TBitMap;r:Trect;Alpha:Byte);
var
pscanLine32 : pRGBQuadArray;
i,j:Integer;
begin
for i := 0 to bmp.Height -1 do
begin
pscanLine32 := bmp.Scanline[i];
for j := 0 to bmp.Width -1 do
begin
if (i>=r.Top) and (i<=r.Bottom) and (j>=r.Left) and (j<=r.Right) then
pscanLine32[j].rgbReserved := Alpha;
end;
end;
end;
procedure TMyHintWindow.PrepareBitmap;
var
r:TRect;
begin
r := Clientrect;
r.Top := r.Top + 10;
InflateRect(r,-10,-10);
FreeAndNil(FBitmap);
FBitmap := TBitmap.Create;
FBitmap.Width := 230;
FBitmap.Height := 61;
SetAlpha(FBitmap, 0);
FBitmap.Canvas.Font := Screen.HintFont;
FBitmap.Canvas.Brush.Style := bsClear;
FBitmap.Canvas.Draw(0, 0, ThePNG);
DrawText(FBitmap.Canvas.Handle, PChar(Caption), Length(Caption), r,DT_Center or DT_Wordbreak or DT_NOPREFIX);
ResetSetAlpha(FBitmap,r,255);
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.ActivateHint(Rect : TRect; const AHint : String);
var
i : Integer;
begin
if (GetTickCount - FLastActive > 250) and (Length(AHint) < 100) then
if not FActivated then
begin
FCurrAlpha := 1;
FActivated := true;
Caption := AHint;
Canvas.Font := Screen.HintFont;
Width := 230; // (Rect.Right - Rect.Left) + 16;
Height := 61; // (Rect.Bottom - Rect.Top) + 10;
Left := rect.Left - Width div 2;
Top := Rect.Top;
Ftimer.Enabled := true;
ShowWindow(Handle, SW_SHOWNOACTIVATE);
SetWindowPos(Handle, HWND_TOPMOST, Left, Top, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
invalidate;
end;
FLastActive := GetTickCount;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Paint;
var
DestPoint, srcPoint:TPoint;
winSize:TSize;
DC : HDC;
blendfunc : BLENDFUNCTION;
Owner : HWnd;
curWinStyle:Integer;
exStyle:Dword;
begin
PrepareBitmap;
DC := GetDC(0);
try
winSize.cx := width;
winSize.cy := Height;
srcPoint.x := 0;
srcPoint.y := 0;
DestPoint := BoundsRect.TopLeft;
exStyle := GetWindowLongA(handle, GWL_EXSTYLE);
if (exStyle and WS_EX_LAYERED) = 0 then
SetWindowLong(handle, GWL_EXSTYLE, exStyle or WS_EX_LAYERED );
With blendFunc do
begin
AlphaFormat := 1; //=AC_SRC_ALPHA;
BlendFlags := 0;
BlendOp := AC_SRC_OVER;
SourceConstantAlpha := FCurrAlpha; // here you can set Alpha
end;
UpdateLayeredWindow(Handle, DC, #DestPoint, #winSize, FBitmap.Canvas.Handle, #srcPoint,clBlack, #blendFunc, 2);//=ULW_ALPHA
finally
ReleaseDC(0, DC);
end;
end;
// --------------------------------------------------------------------------- //
procedure TMyHintWindow.Erase(var Message : TMessage);
begin
Message.Result := 0;
end;
// --------------------------------------------------------------------------- //
procedure TForm1.FormCreate(Sender : TObject);
begin
HintWindowClass := TMyHintWindow;
Button1.Hint := 'This is a nice fake tooltip!';
ReportMemoryLeaksOnShutDown := true;
end;
// --------------------------------------------------------------------------- //
end.

Related

Caption Buttons not respond to mouse clicks after Non - Client painting with DWM in Delphi

I am painting my Application's non-client area with the help of Desktop Window Manager, adding a new button for testing purposes.
After compiling, my custom button is clickable, but the default caption buttons (Minimize, Maximize and Close) do nothing when I hover over them or click on them.
The repainted Title Bar responds to dragging and double-clicks. The Form
maximizes when I double-click the Title Bar as default. And the Close button responds to the very corner of it near Form's Right Border.
I have written my painting procedure as described in this post.
The new codes I added:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ImgList, Buttons;
type
TForm1 = class(TForm)
ImageList1: TImageList;
SpeedButton1: TSpeedButton;
function GetSysIconRect: TRect;
procedure PaintWindow(DC: HDC);
procedure InvalidateTitleBar;
procedure FormCreate(Sender: TObject);
procedure WndProc(var Message: TMessage);
procedure FormPaint(Sender: TObject);
procedure SpeedButton1Click(Sender: TObject);
protected
procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
procedure WMNCHitTest(var Message: TWMNCHitTest); message WM_NCHITTEST;
procedure WMActivate(var Message: TWMActivate); message WM_ACTIVATE;
procedure CMTextChanged(var Message: TMessage);
procedure WMWindowPosChanging(var Message: TWMWindowPosChanging); message WM_WINDOWPOSCHANGING;
procedure WMNCRButtonUp(var Message: TWMNCRButtonUp); message WM_NCRBUTTONUP;
private
{ Private declarations }
FWndFrameSize: Integer;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
uses
DWMAPI, CommCtrl, Themes, UXTheme, StdCtrls;
{$R *.dfm}
{$IF not Declared(UnicodeString)}
type
UnicodeString = WideString;
{$IFEND}
procedure DrawGlassCaption(Form: TForm; const Text: UnicodeString;
Color: TColor; var R: TRect; HorzAlignment: TAlignment = taLeftJustify;
VertAlignment: TTextLayout = tlCenter; ShowAccel: Boolean = False); overload;
const
BasicFormat = DT_SINGLELINE or DT_END_ELLIPSIS;
HorzFormat: array[TAlignment] of UINT = (DT_LEFT, DT_RIGHT, DT_CENTER);
VertFormat: array[TTextLayout] of UINT = (DT_TOP, DT_VCENTER, DT_BOTTOM);
AccelFormat: array[Boolean] of UINT = (DT_NOPREFIX, 0);
var
DTTOpts: TDTTOpts;
Element: TThemedWindow;
IsVistaAndMaximized: Boolean;
NCM: TNonClientMetrics;
ThemeData: HTHEME;
procedure DoTextOut;
begin
with ThemeServices.GetElementDetails(Element) do
DrawThemeTextEx(ThemeData, Form.Canvas.Handle, Part, State, PWideChar(Text),
Length(Text), BasicFormat or AccelFormat[ShowAccel] or
HorzFormat[HorzAlignment] or VertFormat[VertAlignment], #R, DTTOpts);
end;
begin
if Color = clNone then Exit;
IsVistaAndMaximized := (Form.WindowState = wsMaximized) and
(Win32MajorVersion = 6) and (Win32MinorVersion = 0);
ThemeData := OpenThemeData(0, 'CompositedWindow::Window');
Assert(ThemeData <> 0, SysErrorMessage(GetLastError));
Try
NCM.cbSize := SizeOf(NCM);
if SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, #NCM, 0) then
if Form.BorderStyle in [bsToolWindow, bsSizeToolWin] then
Form.Canvas.Font.Handle := CreateFontIndirect(NCM.lfSmCaptionFont)
else
Form.Canvas.Font.Handle := CreateFontIndirect(NCM.lfCaptionFont);
ZeroMemory(#DTTOpts, SizeOf(DTTOpts));
DTTOpts.dwSize := SizeOf(DTTOpts);
DTTOpts.dwFlags := DTT_COMPOSITED or DTT_TEXTCOLOR;
if Color <> clDefault then
DTTOpts.crText := ColorToRGB(Color)
else if IsVistaAndMaximized then
DTTOpts.dwFlags := DTTOpts.dwFlags and not DTT_TEXTCOLOR
else if Form.Active then
DTTOpts.crText := GetSysColor(COLOR_CAPTIONTEXT)
else
DTTOpts.crText := GetSysColor(COLOR_INACTIVECAPTIONTEXT);
if not IsVistaAndMaximized then
begin
DTTOpts.dwFlags := DTTOpts.dwFlags or DTT_GLOWSIZE;
DTTOpts.iGlowSize := 15;
end;
if Form.WindowState = wsMaximized then
if Form.Active then
Element := twMaxCaptionActive
else
Element := twMaxCaptionInactive
else if Form.BorderStyle in [bsToolWindow, bsSizeToolWin] then
if Form.Active then
Element := twSmallCaptionActive
else
Element := twSmallCaptionInactive
else
if Form.Active then
Element := twCaptionActive
else
Element := twCaptionInactive;
DoTextOut;
if IsVistaAndMaximized then DoTextOut;
Finally
CloseThemeData(ThemeData);
end;
end;
function GetDwmBorderIconsRect(Form: TForm): TRect;
begin
if DwmGetWindowAttribute(Form.Handle, DWMWA_CAPTION_BUTTON_BOUNDS, #Result, SizeOf(Result)) <> S_OK then SetRectEmpty(Result);
end;
procedure DrawGlassCaption(Form: TForm; var R: TRect;
HorzAlignment: TAlignment = taLeftJustify; VertAlignment: TTextLayout = tlCenter;
ShowAccel: Boolean = False); overload;
begin
DrawGlassCaption(Form, Form.Caption, clDefault, R,
HorzAlignment, VertAlignment, ShowAccel);
end;
procedure TForm1.FormCreate(Sender: TObject);
var
R: TRect;
begin
if DwmCompositionEnabled then
begin
SetRectEmpty(R);
AdjustWindowRectEx(R, GetWindowLong(Handle, GWL_STYLE), False,
GetWindowLong(Handle, GWL_EXSTYLE));
FWndFrameSize := R.Right;
GlassFrame.Top := -R.Top;
GlassFrame.Enabled := True;
SetWindowPos(Handle, 0, Left, Top, Width, Height, SWP_FRAMECHANGED);
DoubleBuffered := True;
end;
end;
procedure TForm1.InvalidateTitleBar;
var
R: TRect;
begin
if not HandleAllocated then Exit;
R.Left := 0;
R.Top := 0;
R.Right := Width;
R.Bottom := GlassFrame.Top;
InvalidateRect(Handle, #R, False);
end;
procedure TForm1.CMTextChanged(var Message: TMessage);
begin
inherited;
InvalidateTitleBar;
end;
procedure TForm1.WMActivate(var Message: TWMActivate);
begin
inherited;
InvalidateTitleBar;
end;
procedure TForm1.WMNCHitTest(var Message: TWMNCHitTest);
var
ClientPos: TPoint;
IconRect: TRect;
begin
inherited;
if not GlassFrame.Enabled then Exit;
case Message.Result of
HTCLIENT:
HTMINBUTTON, HTMAXBUTTON, HTCLOSE:
begin
Message.Result := HTCAPTION;
Exit;
end;
else
Exit;
end;
ClientPos := ScreenToClient(Point(Message.XPos, Message.YPos));
if ClientPos.Y > GlassFrame.Top then Exit;
if ControlAtPos(ClientPos, True) <> nil then Exit;
IconRect := GetSysIconRect;
if (ClientPos.X < IconRect.Right) and ((WindowState = wsMaximized) or
((ClientPos.Y >= IconRect.Top) and (ClientPos.Y < IconRect.Bottom))) then
Message.Result := HTSYSMENU
else if ClientPos.Y < FWndFrameSize then
Message.Result := HTTOP
else
Message.Result := HTCAPTION;
end;
procedure ShowSystemMenu(Form: TForm; const Message: TWMNCRButtonUp);
var
Cmd: WPARAM;
Menu: HMENU;
procedure UpdateItem(ID: UINT; Enable: Boolean; MakeDefaultIfEnabled: Boolean = False);
const
Flags: array[Boolean] of UINT = (MF_GRAYED, MF_ENABLED);
begin
EnableMenuItem(Menu, ID, MF_BYCOMMAND or Flags[Enable]);
if MakeDefaultIfEnabled and Enable then
SetMenuDefaultItem(Menu, ID, MF_BYCOMMAND);
end;
begin
Menu := GetSystemMenu(Form.Handle, False);
if Form.BorderStyle in [bsSingle, bsSizeable, bsToolWindow, bsSizeToolWin] then
begin
SetMenuDefaultItem(Menu, UINT(-1), 0);
UpdateItem(SC_RESTORE, Form.WindowState <> wsNormal, True);
UpdateItem(SC_MOVE, Form.WindowState <> wsMaximized);
UpdateItem(SC_SIZE, (Form.WindowState <> wsMaximized) and
(Form.BorderStyle in [bsSizeable, bsSizeToolWin]));
UpdateItem(SC_MINIMIZE, (biMinimize in Form.BorderIcons) and
(Form.BorderStyle in [bsSingle, bsSizeable]));
UpdateItem(SC_MAXIMIZE, (biMaximize in Form.BorderIcons) and
(Form.BorderStyle in [bsSingle, bsSizeable]) and
(Form.WindowState <> wsMaximized), True);
end;
if Message.HitTest = HTSYSMENU then
SetMenuDefaultItem(Menu, SC_CLOSE, MF_BYCOMMAND);
Cmd := WPARAM(TrackPopupMenu(Menu, TPM_RETURNCMD or
GetSystemMetrics(SM_MENUDROPALIGNMENT), Message.XCursor,
Message.YCursor, 0, Form.Handle, nil));
PostMessage(Form.Handle, WM_SYSCOMMAND, Cmd, 0)
end;
procedure TForm1.WMWindowPosChanging(var Message: TWMWindowPosChanging);
const
SWP_STATECHANGED = $8000;
begin
if GlassFrame.Enabled then
if (Message.WindowPos.flags and SWP_STATECHANGED) = SWP_STATECHANGED then
Invalidate
else
InvalidateTitleBar;
inherited;
end;
procedure TForm1.WMNCRButtonUp(var Message: TWMNCRButtonUp);
begin
if not GlassFrame.Enabled or not (biSystemMenu in BorderIcons) then
inherited
else
case Message.HitTest of
HTCAPTION, HTSYSMENU: ShowSystemMenu(Self, Message);
else
inherited;
end;
end;
procedure TForm1.WndProc(var Message: TMessage);
begin
if GlassFrame.Enabled and HandleAllocated and DwmDefWindowProc(Handle,
Message.Msg, Message.WParam, Message.LParam, Message.Result) then
Exit;
inherited;
end;
procedure TForm1.PaintWindow(DC: HDC);
begin
with GetClientRect do
ExcludeClipRect(DC, 0, GlassFrame.Top, Right, Bottom);
inherited;
end;
procedure TForm1.SpeedButton1Click(Sender: TObject);
begin
Close;
end;
procedure TForm1.FormPaint(Sender: TObject);
var
IconHandle: HICON;
R: TRect;
begin
if ImageList1.Count = 0 then
begin
ImageList1.Width := GetSystemMetrics(SM_CXSMICON);
ImageList1.Height := GetSystemMetrics(SM_CYSMICON);
{$IF NOT DECLARED(TColorDepth)}
ImageList1.Handle := ImageList_Create(ImageList1.Width,
ImageList1.Height, ILC_COLOR32 or ILC_MASK, 1, 1);
{$IFEND}
IconHandle := Icon.Handle;
if IconHandle = 0 then IconHandle := Application.Icon.Handle;
ImageList_AddIcon(ImageList1.Handle, IconHandle);
end;
R := GetSysIconRect;
ImageList1.Draw(Canvas, R.Left, R.Top, 0);
R.Left := R.Right + FWndFrameSize - 3;
if WindowState = wsMaximized then
R.Top := FWndFrameSize
else
R.Top := 0;
R.Right := GetDwmBorderIconsRect(Self).Left - FWndFrameSize - 1;
R.Bottom := GlassFrame.Top;
DrawGlassCaption(Self, R);
end;
function TForm1.GetSysIconRect: TRect;
begin
if not (biSystemMenu in BorderIcons) or not (BorderStyle in [bsSingle, bsSizeable]) then
SetRectEmpty(Result)
else
begin
Result.Left := 0;
Result.Right := GetSystemMetrics(SM_CXSMICON);
Result.Bottom := GetSystemMetrics(SM_CYSMICON);
if WindowState = wsMaximized then
Result.Top := GlassFrame.Top - Result.Bottom - 2
else
Result.Top := 6;
Inc(Result.Bottom, Result.Top);
end;
end;
procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
begin
if not GlassFrame.Enabled then
inherited
else
with Message.CalcSize_Params.rgrc[0] do
begin
Inc(Left, FWndFrameSize);
Dec(Right, FWndFrameSize);
Dec(Bottom, FWndFrameSize);
end;
end;
end.
Please help me find what is causing the Caption Buttons to become unresponsive to mouse clicks.
The standard buttons do not work because your WM_NCHITTEST handler is returning HTCAPTION for them. You are lying to Windows, telling it that the mouse is not over the buttons even if it really is. If the inherited handler returns HTMINBUTTON, HTMAXBUTTON, or HTCLOSE, just Exit without modifying the Message.Result:
procedure TForm1.WMNCHitTest(var Message: TWMNCHitTest);
var
ClientPos: TPoint;
IconRect: TRect;
begin
inherited;
if not GlassFrame.Enabled then Exit;
case Message.Result of
HTCLIENT:
HTMINBUTTON, HTMAXBUTTON, HTCLOSE:
begin
//Message.Result := HTCAPTION; // <-- here
Exit;
end;
else
Exit;
end;
...
end;

Custom Button Component : caption is displaying different color in Delphi7

i am working with Delphi 7
Below is my code for sample button component. When i place button component on form at design time it is displaying different color in Delphi7 as attached in screen shot.
in Delphi 5 same is Working fine. i mean it is displaying caption as black color.
unit TestButton1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, ExtCtrls, Math;
type
TButtonShape = (bsRectangle, bsOval, bsTriangle);
type
TTestButton = class(TCustomControl)
private
{ Private declarations }
FCaption: TCaption;
FButtonDown: boolean;
FBorderStyle: TBorderStyle;
FBtnHighLight: TColor;
FBtnShadow: TColor;
FBtnFace: TColor;
procedure DrawButton;
procedure DrawButtonDown;
procedure DrawCaption(rc: TRect);
procedure DrawButtonUp;
procedure SetCaption(Value: TCaption);
procedure SetButtonColor(Value: TColor);
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure WndProc(var Msg: TMessage); override;
published
{ Published declarations }
property Caption: TCaption read FCaption write SetCaption;
property Color: TColor read FBtnFace write SetButtonColor;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TTestButton]);
end;
{ TTestButton }
constructor TTestButton.Create(AOwner: TComponent);
begin
inherited;
if AOwner is TWinControl then Parent := TWinControl(AOwner);
FCaption := Name;
FButtonDown := FALSE;
FBorderStyle := bsNone;
ControlStyle := ControlStyle - [csDoubleClicks];
FBtnHighLight := clBtnHighLight;
FBtnShadow := clBtnShadow;
FBtnFace := clBtnFace;
Width := 75;
Height := 25;
end;
destructor TTestButton.Destroy;
begin
inherited;
end;
procedure TTestButton.DrawButton;
var rc: TRect;
begin
with rc do
Begin
Top := 0;
Left := 0;
Bottom := Height;
Right := Width;
end;
with Canvas do
Begin
if FBorderStyle = bsSingle then
Begin
Brush.Color := clBlack;
Framerect(rc);
end;
end;
if FButtonDown then DrawButtonDown
else DrawButtonUp;
end;
procedure TTestButton.DrawButtonDown;
var rc: TRect;
Cnv: TCanvas;
begin
with rc do
Begin
Top := 0;
Left := 0;
Bottom := Height - 1;
Right := Width - 1;
end;
Cnv := TCanvas.Create;
Cnv.Handle := CreateCompatibleDC(Canvas.Handle);
SelectObject(Cnv.Handle, CreateCompatibleBitmap(Canvas.Handle, Width, Height));
with Canvas do
Begin
Brush.Color := FBtnFace;
FillRect(rc);
Pen.Color := clBlack;
MoveTo(rc.Left, rc.Bottom - 1);
LineTo(rc.Left, rc.Top);
LineTo(rc.Right, rc.Top);
Pen.Color := FBtnShadow;
MoveTo(rc.Left + 1, rc.Bottom - 2);
LineTo(rc.Left + 1, rc.Top + 1);
LineTo(rc.Right - 1, rc.Top + 1);
Pen.Color := FBtnHighlight;
MoveTo(rc.Left, rc.Bottom);
LineTo(rc.Right, rc.Bottom);
Lineto(rc.Right, rc.Top - 1);
rc.Top := rc.Top + 1;
rc.Left := rc.Left + 1;
end;
rc.Top := rc.Top + 1;
rc.Left := rc.Left + 1;
if FCaption > '' then DrawCaption(rc);
end;
procedure TTestButton.DrawButtonUp;
var rc: TRect;
begin
with rc do
Begin
Top := 0;
Left := 0;
Bottom := Height - 1;
Right := Width - 1;
end;
with Canvas do
Begin
Brush.Color := FBtnFace;
FillRect(rc);
Pen.Color := FBtnHighlight;
MoveTo(rc.Left, rc.Bottom - 1);
LineTo(rc.Left, rc.Top);
LineTo(rc.Right, rc.Top);
Pen.Color := FBtnShadow;
MoveTo(rc.Left + 1, rc.Bottom - 1);
LineTo(rc.Right - 1, rc.Bottom - 1);
LineTo(rc.Right - 1, rc.Top);
Pen.Color := clBlack;
MoveTo(rc.Left, rc.Bottom);
LineTo(rc.Right, rc.Bottom);
Lineto(rc.Right, rc.Top - 1);
end;
if FCaption > '' then DrawCaption(rc);
end;
procedure TTestButton.DrawCaption(rc: TRect);
begin
Canvas.Brush.Style := bsClear;
SetTextColor(Canvas.Handle, Canvas.Font.Color);
DrawText(Canvas.Handle, PChar(FCaption), Length(FCaption), rc, DT_CENTER + DT_VCENTER + DT_SINGLELINE);
end;
procedure TTestButton.SetButtonColor(Value: TColor);
var OldValue: TColor;
begin
OldValue := FBtnFace;
FBtnFace := Value;
if (OldValue <> Value) then Repaint;
end;
procedure TTestButton.SetCaption(Value: TCaption);
begin
FCaption := Value;
Repaint;
end;
procedure TTestButton.WndProc(var Msg: TMessage);
begin
inherited;
with Msg do if Msg = WM_PAINT then
Begin
DrawButton;
Result := 0;
Exit;
end
else if Msg = WM_LBUTTONDOWN then
Begin
if not (csDesigning in ComponentState) then FButtonDown := TRUE;
Repaint;
Result := 0;
Exit;
end
else if Msg = WM_LBUTTONUP then
Begin
FButtonDown := FALSE;
Repaint;
Result := 0;
Exit;
end
end;
end.
at design time, Button caption is displaying in black color with delphi5 and with delphi7 button caption is displaying in different color.
Why it is working with delphi5 and why it is not working with Delphi7.
SetTextColor(Canvas.Handle, Canvas.Font.Color); in DrawCaption sets font color to same value it already has.
Different Delphi versions can set different initial values therefore you see the difference in behavior.
Use SetTextColor(Canvas.Handle, clBlack); instead (if you want black text color)

How to make custom BitBtn?

How to make custom BitBtn with color property?
I have found one solution here, but it is a TButton not TBitBtn so I have edited the code as follows :
unit ColorBitBtn;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, StdCtrls, Buttons, ExtCtrls;
type
TColorBitBtn = class(TBitBtn)
private
ShowBackColor : Boolean;
FCanvas : TCanvas;
IsFocused : Boolean;
FBackColor : TColor;
FForeColor : TColor;
FHoverColor : TColor;
procedure SetBackColor(const Value: TColor);
procedure SetForeColor(const Value: TColor);
procedure SetHoverColor(const Value: TColor);
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WndProc(var Message : TMessage); override;
procedure SetBitBtnStyle(Value: Boolean);
procedure DrawBitBtn(Rect: TRect; State: UINT);
procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
procedure CMFontChanged(var Message: TMessage); message CM_FONTCHANGED;
procedure CNMeasureItem(var Message: TWMMeasureItem); message CN_MEASUREITEM;
procedure CNDrawItem(var Message: TWMDrawItem); message CN_DRAWITEM;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property BackColor : TColor read FBackColor write SetBackColor default clBtnFace;
property ForeColor : TColor read FForeColor write SetForeColor default clBtnText;
property HoverColor: TColor read FHoverColor write SetHoverColor default clBtnFace;
end;
procedure Register;
implementation
constructor TColorBitBtn.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ShowBackColor := True;
FCanvas := TCanvas.Create;
BackColor := clBtnFace;
ForeColor := clBtnText;
HoverColor := clBtnFace;
end;
destructor TColorBitBtn.Destroy;
begin
FreeAndNil(FCanvas);
inherited Destroy;
end;
procedure TColorBitBtn.WndProc(var Message : TMessage);
begin
if (Message.Msg = CM_MOUSELEAVE) then
begin
ShowBackColor := True;
Invalidate;
end;
if (Message.Msg = CM_MOUSEENTER) then
begin
ShowBackColor := False;
Invalidate;
end;
inherited;
end;
procedure TColorBitBtn.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
Style := Style or BS_OWNERDRAW;
end;
procedure TColorBitBtn.SetBitBtnStyle(Value: Boolean);
begin
if Value <> IsFocused then
begin
IsFocused := Value;
Invalidate;
end;
end;
procedure TColorBitBtn.CNMeasureItem(var Message: TWMMeasureItem);
begin
with Message.MeasureItemStruct^ do
begin
itemWidth := Width;
itemHeight := Height;
end;
end;
procedure TColorBitBtn.CNDrawItem(var Message: TWMDrawItem);
var
SaveIndex: Integer;
begin
with Message.DrawItemStruct^ do
begin
SaveIndex := SaveDC(hDC);
FCanvas.Lock;
try
FCanvas.Handle := hDC;
FCanvas.Font := Font;
FCanvas.Brush := Brush;
DrawBitBtn(rcItem, itemState);
finally
FCanvas.Handle := 0;
FCanvas.Unlock;
RestoreDC(hDC, SaveIndex);
end;
end;
Message.Result := 1;
end;
procedure TColorBitBtn.CMEnabledChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TColorBitBtn.CMFontChanged(var Message: TMessage);
begin
inherited;
Invalidate;
end;
procedure TColorBitBtn.SetBackColor(const Value: TColor);
begin
if FBackColor <> Value then
begin
FBackColor:= Value;
Invalidate;
end;
end;
procedure TColorBitBtn.SetForeColor(const Value: TColor);
begin
if FForeColor <> Value then
begin
FForeColor:= Value;
Invalidate;
end;
end;
procedure TColorBitBtn.SetHoverColor(const Value: TColor);
begin
if FHoverColor <> Value then
begin
FHoverColor:= Value;
Invalidate;
end;
end;
procedure TColorBitBtn.DrawBitBtn(Rect: TRect; State: UINT);
var Flags, OldMode: Longint;
IsDown, IsDefault, IsDisabled: Boolean;
OldColor: TColor;
OrgRect: TRect;
NewCaption : string;
begin
NewCaption := Caption;
OrgRect := Rect;
Flags := DFCS_BUTTONPUSH or DFCS_ADJUSTRECT;
IsDown := State and ODS_SELECTED <> 0;
IsDisabled := State and ODS_DISABLED <> 0;
IsDefault := State and ODS_FOCUS <> 0;
if IsDown then Flags := Flags or DFCS_PUSHED;
if IsDisabled then Flags := Flags or DFCS_INACTIVE;
if (IsFocused or IsDefault) then
begin
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Pen.Width := 1;
FCanvas.Brush.Style := bsClear;
FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
InflateRect(Rect, - 1, - 1);
end;
if IsDown then
begin
FCanvas.Pen.Color := clBtnShadow;
FCanvas.Pen.Width := 1;
FCanvas.Brush.Color := clBtnFace;
FCanvas.Rectangle(Rect.Left, Rect.Top, Rect.Right, Rect.Bottom);
InflateRect(Rect, - 1, - 1);
end
else
begin
DrawFrameControl(FCanvas.Handle, Rect, DFC_BUTTON, Flags);
end;
if IsDown then OffsetRect(Rect, 1, 1);
OldColor := FCanvas.Brush.Color;
if ShowBackColor then
FCanvas.Brush.Color := BackColor
else
FCanvas.Brush.Color := HoverColor;
FCanvas.FillRect(Rect);
FCanvas.Brush.Color := OldColor;
OldMode := SetBkMode(FCanvas.Handle, TRANSPARENT);
FCanvas.Font.Color := ForeColor;
if IsDisabled then
DrawState(FCanvas.Handle, FCanvas.Brush.Handle, nil, Integer(NewCaption), 0,
((Rect.Right - Rect.Left) - FCanvas.TextWidth(NewCaption)) div 2,
((Rect.Bottom - Rect.Top) - FCanvas.TextHeight(NewCaption)) div 2,
0, 0, DST_TEXT or DSS_DISABLED)
else
begin
InflateRect(Rect, -4, -4);
DrawText(FCanvas.Handle, PChar(NewCaption), - 1, Rect, DT_WORDBREAK or DT_CENTER);
end;
SetBkMode(FCanvas.Handle, OldMode);
if (IsFocused and IsDefault) then
begin
Rect := OrgRect;
InflateRect(Rect, - 4, - 4);
FCanvas.Pen.Color := clWindowFrame;
FCanvas.Brush.Color := clBtnFace;
DrawFocusRect(FCanvas.Handle, Rect);
end;
end;
procedure Register;
begin
RegisterComponents('Standard', [TColorBitBtn]);
end;
initialization
RegisterClass(TColorBitBtn); // needed for persistence at runtime
end.
After doing the same. it compiles perfectly without any error. But the Font.Color does not get changed on any event like OnClick, OnMouseDown etc and another problem is not look like Button or BitBtn after enabling Theme Manifest like the following picture
Here the first is Standard Button, Standard BitBtn followed by Custom BitBtn created by the above code after adding Theme Manifest.

Why doesn't my size-changing control work when it shares a form with a TSplitter?

I'm writing a panel control that allows the user to mimimize the panel and to hide the components on this panel.
A single THidePanel seems to work as expected, but not when I put two of them on a form separated by a splitter. The first panel is aligned alLeft; the second panel alClient:
When the second panel's button is clicked, it does not react to minimize or maximize. Here is all of my code. Why doesn't it work?
const
BoarderSize = 20;
type
TButtonPosition = (topleft, topright, buttomleft, buttomright);
///
/// a panel with a smaller panel inside and a button on the side
///
THidePanel = class(TPanel)
private
{ Private-Deklarationen }
///
/// a smaller working panel
WorkingPanel: TPanel;
FLargeHight: Integer;
FLargeWidth: Integer;
FActivateButton: TButton;
FExpandState: Boolean;
FButtonPosition: TButtonPosition;
FOnActivateBtnClick: TNotifyEvent;
procedure SetButtonPosition(const Value: TButtonPosition);
protected
{ Protected-Deklarationen }
public
{ Public-Deklarationen }
constructor create(aOwner: TComponent); override;
procedure WMSize(var Msg: TWMSize); message WM_SIZE;
procedure HideComponents;
procedure H_ActivateButtonClick(Sender: TObject);
procedure SetState(astate: Boolean);
procedure free;
destructor destroy; override;
published
{ Published-Deklarationen }
property OnActivateButtonClick: TNotifyEvent read FOnActivateBtnClick
write FOnActivateBtnClick;
property ButtonPosition: TButtonPosition read FButtonPosition
write SetButtonPosition;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [THidePanel]);
end;
{ THidePanel }
constructor THidePanel.create(aOwner: TComponent);
begin
inherited;
WorkingPanel := TPanel.create(self);
WorkingPanel.Caption := 'V01';
FActivateButton := TButton.create(self);
FActivateButton.Parent := self;
FActivateButton.Caption := '<';
FActivateButton.OnClick := H_ActivateButtonClick;
FActivateButton.Width := BoarderSize;
FActivateButton.Height := BoarderSize;
WorkingPanel.Caption := '';
FLargeWidth := self.Width;
SetButtonPosition(topright);
end;
destructor THidePanel.destroy;
begin
inherited;
end;
procedure THidePanel.free;
begin
inherited;
WorkingPanel.free;
FActivateButton.free;
end;
procedure THidePanel.HideComponents;
var
i: Integer;
begin
for i := 0 to WorkingPanel.ControlCount - 1 do
WorkingPanel.Controls[i].Visible := False;
end;
procedure THidePanel.WMSize(var Msg: TWMSize);
begin
/// set inner panel size
WorkingPanel.Top := self.Top + BoarderSize;
WorkingPanel.Left := self.Left + BoarderSize;
WorkingPanel.Width := self.Width - 2 * BoarderSize;
WorkingPanel.Height := self.Height - 2 * BoarderSize;
/// move button
SetButtonPosition(FButtonPosition);
end;
procedure THidePanel.H_ActivateButtonClick(Sender: TObject);
begin
/// button is clicked!
///
FExpandState := not FExpandState;
SetState( FExpandState );
///
if (Assigned(FOnActivateBtnClick)) then
FOnActivateBtnClick(self);
end;
procedure THidePanel.SetButtonPosition(const Value: TButtonPosition);
begin
FButtonPosition := Value;
case FButtonPosition of
topleft:
begin
FActivateButton.Left := 0;
FActivateButton.Top := 0;
end;
topright:
begin
FActivateButton.Left := self.Width - BoarderSize;
FActivateButton.Top := 0;
end;
buttomleft:
begin
FActivateButton.Left := 0;
FActivateButton.Top := self.ClientWidth - BoarderSize;
end;
buttomright:
begin
FActivateButton.Top := self.ClientWidth - BoarderSize;
FActivateButton.Left := self.Width - BoarderSize;
end;
else
/// never go here
end;
end;
procedure THidePanel.SetState(astate: Boolean);
begin
if astate then
begin
/// ...
FActivateButton.Caption := '>';
self.Width := BoarderSize;
end
else
begin
/// ...
FActivateButton.Caption := '<';
self.Width := FLargeWidth;
end;
end;
When Control's Anchors set to alClient, you can not change the size . Set second panel align to alLeft or alRight . if you want fill form with this control, set AutoSize of form True or manually set max size of your control on resize it .
Like MohsenB already explained (+1ed), you cannot change the size of a control with Align = alClient. But since you are making this a component, I would choose to change the Align setting of the component temporarily, instead of dealing with this in the designer code: i.e. make it a feature of the component to be able to set its Align property to alClient and let it behave accordingly when situation requires.
I think you are looking for the following enhancements:
unit Unit2;
interface
uses
Messages, Classes, Controls, StdCtrls, ExtCtrls;
const
BorderSize = 20;
type
TButtonPosition = (bpTopLeft, bpTopRight, bpBottomLeft, bpBottomRight);
THidePanel = class(TPanel)
private
FActivateButton: TButton;
FButtonPosition: TButtonPosition;
FExpandState: Boolean;
FOldAlign: TAlign;
FOldWidth: Integer;
FOnActivateBtnClick: TNotifyEvent;
FWorkingPanel: TPanel;
procedure ActivateButtonClick(Sender: TObject);
procedure SetButtonPosition(Value: TButtonPosition);
protected
function CanResize(var NewWidth, NewHeight: Integer): Boolean; override;
procedure Resize; override;
public
constructor Create(AOwner: TComponent); override;
procedure SetState(AState: Boolean);
published
property ButtonPosition: TButtonPosition read FButtonPosition
write SetButtonPosition default bpTopRight;
property OnActivateButtonClick: TNotifyEvent read FOnActivateBtnClick
write FOnActivateBtnClick;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [THidePanel]);
end;
{ THidePanel }
constructor THidePanel.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FWorkingPanel := TPanel.Create(Self);
FWorkingPanel.Caption := '';
FWorkingPanel.SetBounds(BorderSize, BorderSize, Width - 2 * BorderSize,
Height - 2 * BorderSize);
FWorkingPanel.Anchors := [akLeft, akTop, akRight, akBottom];
FWorkingPanel.Parent := Self;
FActivateButton := TButton.Create(Self);
FActivateButton.Caption := '<';
FActivateButton.OnClick := ActivateButtonClick;
FActivateButton.Width := BorderSize;
FActivateButton.Height := BorderSize;
FActivateButton.Parent := Self;
SetButtonPosition(bpTopRight);
end;
procedure THidePanel.ActivateButtonClick(Sender: TObject);
begin
FExpandState := not FExpandState;
SetState(FExpandState);
if Assigned(FOnActivateBtnClick) then
FOnActivateBtnClick(Self);
end;
procedure THidePanel.SetButtonPosition(Value: TButtonPosition);
begin
if FButtonPosition <> Value then
begin
FButtonPosition := Value;
case FButtonPosition of
bpTopLeft:
begin
FActivateButton.Left := 0;
FActivateButton.Top := 0;
FActivateButton.Anchors := [akLeft, akTop];
end;
bpTopRight:
begin
FActivateButton.Left := Width - BorderSize;
FActivateButton.Top := 0;
FActivateButton.Anchors := [akRight, akTop];
end;
bpBottomLeft:
begin
FActivateButton.Left := 0;
FActivateButton.Top := ClientWidth - BorderSize;
FActivateButton.Anchors := [akLeft, akBottom];
end;
bpBottomRight:
begin
FActivateButton.Top := ClientWidth - BorderSize;
FActivateButton.Left := Width - BorderSize;
FActivateButton.Anchors := [akRight, akBottom];
end;
end;
end;
end;
procedure THidePanel.SetState(AState: Boolean);
begin
if AState then
begin
FActivateButton.Caption := '>';
FOldAlign := Align;
if FOldAlign = alClient then
Align := alLeft;
Width := BorderSize;
end
else
begin
FActivateButton.Caption := '<';
if FOldAlign = alClient then
Align := FOldAlign
else
Width := FOldWidth;
end;
end;
procedure THidePanel.Resize;
begin
if not FExpandState then
FOldWidth := Width;
inherited Resize;
end;
function THidePanel.CanResize(var NewWidth, NewHeight: Integer): Boolean;
begin
Result := inherited CanResize(NewWidth, NewHeight);
if FExpandState then
NewWidth := BorderSize;
end;
end.
Testing code:
unit Unit1;
interface
uses
Controls, Forms, Unit2, ExtCtrls;
type
TForm1 = class(TForm)
procedure FormClick(Sender: TObject);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormClick(Sender: TObject);
begin
with THidePanel.Create(Self) do
begin
Align := alLeft;
Parent := Self;
end;
with TSplitter.Create(Self) do
begin
Left := 200;
Parent := Self;
end;
with THidePanel.Create(Self) do
begin
Align := alClient;
Parent := Self;
end;
end;
end.

Rounded and titled "TPanel" in Delphi 7

I would have a TPanel in my application but with another look.
For it I want a colored title bar and the up corner rounded just like in some user interfaces like it
Do you know any component or library for it ? (Prefered Open source but not only).
I tried TJVCaptionPanel it's OK but needs rounded up corner.
Like this?
unit CustomCaptionPanel;
interface
uses
Windows, SysUtils, Classes, Controls, Graphics;
type
TCustomCaptionPanel = class(TCustomControl)
private const
DEFAULT_BORDER_COLOR = $0033CCFF;
DEFAULT_CLIENT_COLOR = clWindow;
DEFAULT_BORDER_RADIUS = 16;
private
{ Private declarations }
FBorderColor: TColor;
FClientColor: TColor;
FBorderRadius: integer;
FCaption: TCaption;
FAlignment: TAlignment;
procedure SetBorderColor(BorderColor: TColor);
procedure SetClientColor(ClientColor: TColor);
procedure SetBorderRadius(BorderRadius: integer);
procedure SetCaption(const Caption: TCaption);
procedure SetAlignment(Alignment: TAlignment);
protected
{ Protected declarations }
procedure Paint; override;
public
{ Public declarations }
constructor Create(AOwner: TComponent); override;
published
{ Published declarations }
property Color;
property Caption read FCaption write SetCaption;
property Alignment: TAlignment read FAlignment write SetAlignment default taCenter;
property Font;
property BorderColor: TColor read FBorderColor write SetBorderColor default DEFAULT_BORDER_COLOR;
property ClientColor: TColor read FClientColor write SetClientColor default DEFAULT_CLIENT_COLOR;
property BorderRadius: integer read FBorderRadius write SetBorderRadius default DEFAULT_BORDER_RADIUS;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Rejbrand 2009', [TCustomCaptionPanel]);
end;
{ TCustomCaptionPanel }
constructor TCustomCaptionPanel.Create(AOwner: TComponent);
begin
inherited;
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csSetCaption, csOpaque, csDoubleClicks, csReplicatable, csPannable];
FBorderColor := DEFAULT_BORDER_COLOR;
FClientColor := DEFAULT_CLIENT_COLOR;
FBorderRadius := DEFAULT_BORDER_RADIUS;
FAlignment := taCenter;
end;
procedure TCustomCaptionPanel.Paint;
var
r: TRect;
const
Alignments: array[TAlignment] of integer = (DT_LEFT, DT_RIGHT, DT_CENTER);
begin
inherited;
Canvas.Pen.Color := FBorderColor;
Canvas.Brush.Color := FBorderColor;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(Rect(FBorderRadius,
0,
ClientWidth - FBorderRadius,
FBorderRadius));
Canvas.Ellipse(Rect(0,
0,
2*FBorderRadius,
2*FBorderRadius));
Canvas.Ellipse(Rect(ClientWidth - 2*FBorderRadius,
0,
ClientWidth,
2*FBorderRadius));
Canvas.Brush.Color := FClientColor;
Canvas.Rectangle(Rect(0,
FBorderRadius,
ClientWidth,
ClientHeight));
Canvas.Font.Assign(Self.Font);
r := Rect(FBorderRadius, 0, ClientWidth - FBorderRadius, FBorderRadius);
Canvas.Brush.Style := bsClear;
DrawText(Canvas.Handle,
PChar(Caption),
length(Caption),
r,
DT_SINGLELINE or DT_LEFT or DT_VCENTER or DT_END_ELLIPSIS or Alignments[FAlignment]);
end;
procedure TCustomCaptionPanel.SetAlignment(Alignment: TAlignment);
begin
if FAlignment <> Alignment then
begin
FAlignment := Alignment;
Invalidate;
end;
end;
procedure TCustomCaptionPanel.SetBorderColor(BorderColor: TColor);
begin
if FBorderColor <> BorderColor then
begin
FBorderColor := BorderColor;
Invalidate;
end;
end;
procedure TCustomCaptionPanel.SetBorderRadius(BorderRadius: integer);
begin
if FBorderRadius <> BorderRadius then
begin
FBorderRadius := BorderRadius;
Invalidate;
end;
end;
procedure TCustomCaptionPanel.SetCaption(const Caption: TCaption);
begin
if not SameStr(FCaption, Caption) then
begin
FCaption := Caption;
Invalidate;
end;
end;
procedure TCustomCaptionPanel.SetClientColor(ClientColor: TColor);
begin
if FClientColor <> ClientColor then
begin
FClientColor := ClientColor;
Invalidate;
end;
end;
end.
If you wanna round the corner of anything you want, try this:
procedure RoundCornerOf(Control: TWinControl) ;
var
R: TRect;
Rgn: HRGN;
begin
with Control do
begin
R := ClientRect;
rgn := CreateRoundRectRgn(R.Left, R.Top, R.Right, R.Bottom, 20, 20) ;
Perform(EM_GETRECT, 0, lParam(#r)) ;
InflateRect(r, - 4, - 4) ;
Perform(EM_SETRECTNP, 0, lParam(#r)) ;
SetWindowRgn(Handle, rgn, True) ;
Invalidate;
end;
end;

Resources