经过上面的几篇文章的介绍,相信大家已经会自己做一个透明皮肤窗口了,但是要记住,上文因为是基础教程,所以很多细节仍然是需要处理的,例如:
1、为了加快速度,实际上可以先用MakeBmp函数制作好皮肤图片,平均颜色就是取图片右下角的一个点即可。例如QQ的皮肤包就是这么干的。
2、如果实在想运行时计算,那么可以先将图片缩小,再计算平均颜色。这样一来,循环的次数就减少了,而效果是一样的。
3、为了美观,边框可以画个线条上去,这样一来立体感就强很多:
另外,还要其它一些细节,例如 Caption,如果平均颜色是黑色,那么字体应该自动换成白色。我在这里偷了个懒,直接使用了Blur算法,根据字体直接在下面画一个对应的浅色区域,这样一来,即使背景色偏暗,也不影响标题的字体颜色。
另外,还可以做成控件形式,例如我们的AQ控件:
做成控件形式的话,代码可以更加模块化。我们的控件是分为两个系列:NC系列和 CLIENT 系列。顾名思义,NC系列的控件就是工作于NC区的,CLIENT则是工作于CLIENT区。
看一下我们的鼠标信息函数:
我们的演示代码使用了一个Timer检测状态,改成控件后,是可以去掉的:
1、为了加快速度,实际上可以先用MakeBmp函数制作好皮肤图片,平均颜色就是取图片右下角的一个点即可。例如QQ的皮肤包就是这么干的。
2、如果实在想运行时计算,那么可以先将图片缩小,再计算平均颜色。这样一来,循环的次数就减少了,而效果是一样的。
3、为了美观,边框可以画个线条上去,这样一来立体感就强很多:
另外,还要其它一些细节,例如 Caption,如果平均颜色是黑色,那么字体应该自动换成白色。我在这里偷了个懒,直接使用了Blur算法,根据字体直接在下面画一个对应的浅色区域,这样一来,即使背景色偏暗,也不影响标题的字体颜色。
另外,还可以做成控件形式,例如我们的AQ控件:
做成控件形式的话,代码可以更加模块化。我们的控件是分为两个系列:NC系列和 CLIENT 系列。顾名思义,NC系列的控件就是工作于NC区的,CLIENT则是工作于CLIENT区。
看一下我们的鼠标信息函数:
function OnClientMouseMove(): Boolean;
var
P: TPoint;
begin
P.X := Short(LoWord(Message.lParam));
P.Y := Short(HiWord(Message.lParam));
ClientToScreen(FForm.Handle, P);
Result := NCMouseMove(P.X, P.Y – 1)
end;
function OnClientMouseUp(): Boolean;
var
P: TPoint;
begin
P.X := Short(LoWord(Message.lParam));
P.Y := Short(HiWord(Message.lParam));
ClientToScreen(FForm.Handle, P);
Result := NCMouseUp(P.X, P.Y)
end;
WM_NCMOUSEMOVE:
begin
if (not NCMouseMove(LoWord(Message.lParam), HiWord(Message.lParam))) then
FOldWinProc(Message)
else
Message.Result := 0;
end;
......
也就是说,窗口、NC的鼠标消息,都是直接传递给NC函数去处理,以鼠标移动为例,处理如下:
function TAQCustomForm.NCMouseDown(X, Y: Integer): Boolean;
var
i: Integer;
begin
with FForm do
for i := ComponentCount – 1 downto 0 do
if Components[i] is TAQCustomNCCtrlBase then
with TAQCustomNCCtrlBase(Components[i]) do
if PtInRect(GetWindowRect(), Point(X, Y)) then
begin
FIsMouseDown := True;
MouseDown(X, Y);
Result := True;
exit;
end;
Result := False;
end;
也就是说,如果鼠标位于NC控件上,则通知控件处理:
procedure TAQCustomNCCtrl.MouseDown(X, Y: Integer);
begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self, X, Y);
end;
procedure TAQCustomNCButton.Paint(Canvas: TCanvas);
begin
if SkinData = nil then
Exit;
if not Visible then
Exit;
if IsMouseDown and IsMouseEnter and (GetPicDown().Graphic <> nil) then
Canvas.StretchDraw(Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight),
GetPicDown.Graphic)
else if IsMouseEnter and (GetPicHighLight().Graphic <> nil) then
Canvas.StretchDraw(Rect(FLeft, FTop – 1, FLeft + FWidth,
FTop + FHeight – 1), GetPicHighLight.Graphic)
else
Canvas.StretchDraw(Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight),
GetPicNormal.Graphic);
end;
TAQSysMinButton = class(TAQCustomSysButton)
private
protected
procedure Click(); override;
function GetPicNormal(): TPicture; override;
function GetPicDown(): TPicture; override;
function GetPicHighLight(): TPicture; override;
public
constructor Create(AOwner: TComponent); override;
published
end;
这样处理后,需要多增加一个NC控件的话,只要从基类继承,然后重载GetPicXXX函数,返回状态图片即可,而窗口代码不需要作任何修改。我们的演示代码使用了一个Timer检测状态,改成控件后,是可以去掉的:
function TAQCustomForm.NCMouseDown(X, Y: Integer): Boolean;
var
i: Integer;
begin
with FForm do
for i := ComponentCount – 1 downto 0 do
if Components[i] is TAQCustomNCCtrlBase then
with TAQCustomNCCtrlBase(Components[i]) do
if PtInRect(GetWindowRect(), Point(X, Y)) then
begin
FIsMouseDown := True;
MouseDown(X, Y);
Result := True;
exit;
end;
Result := False;
end;
也就是说,如果鼠标位于NC控件上,则通知控件处理:
procedure TAQCustomNCCtrl.MouseDown(X, Y: Integer);
begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self, X, Y);
end;
procedure TAQCustomNCButton.Paint(Canvas: TCanvas);
begin
if SkinData = nil then
Exit;
if not Visible then
Exit;
if IsMouseDown and IsMouseEnter and (GetPicDown().Graphic <> nil) then
Canvas.StretchDraw(Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight),
GetPicDown.Graphic)
else if IsMouseEnter and (GetPicHighLight().Graphic <> nil) then
Canvas.StretchDraw(Rect(FLeft, FTop – 1, FLeft + FWidth,
FTop + FHeight – 1), GetPicHighLight.Graphic)
else
Canvas.StretchDraw(Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight),
GetPicNormal.Graphic);
end;
TAQSysMinButton = class(TAQCustomSysButton)
private
protected
procedure Click(); override;
function GetPicNormal(): TPicture; override;
function GetPicDown(): TPicture; override;
function GetPicHighLight(): TPicture; override;
public
constructor Create(AOwner: TComponent); override;
published
end;
procedure TAQCustomNCCtrl.MouseDown(X, Y: Integer);begin
if Assigned(FOnMouseDown) then
FOnMouseDown(Self, X, Y);
end;
procedure TAQCustomNCButton.Paint(Canvas: TCanvas);
begin
if SkinData = nil then
Exit;
if not Visible then
Exit;
if IsMouseDown and IsMouseEnter and (GetPicDown().Graphic <> nil) then
Canvas.StretchDraw(Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight),
GetPicDown.Graphic)
else if IsMouseEnter and (GetPicHighLight().Graphic <> nil) then
Canvas.StretchDraw(Rect(FLeft, FTop – 1, FLeft + FWidth,
FTop + FHeight – 1), GetPicHighLight.Graphic)
else
Canvas.StretchDraw(Rect(FLeft, FTop, FLeft + FWidth, FTop + FHeight),
GetPicNormal.Graphic);
end;
TAQSysMinButton = class(TAQCustomSysButton)
private
protected
procedure Click(); override;
function GetPicNormal(): TPicture; override;
function GetPicDown(): TPicture; override;
function GetPicHighLight(): TPicture; override;
public
constructor Create(AOwner: TComponent); override;
published
end;
这样处理后,需要多增加一个NC控件的话,只要从基类继承,然后重载GetPicXXX函数,返回状态图片即可,而窗口代码不需要作任何修改。
我们的演示代码使用了一个Timer检测状态,改成控件后,是可以去掉的:
function TAQCustomForm.NCMouseMove(X, Y: Integer): Boolean;
procedure CheckPrev(New: TAQCustomNCCtrlBase);
var
i: Integer;
begin
with FForm do
for i := 0 to ComponentCount – 1 do
if (Components[i] is TAQCustomNCCtrlBase) and (Components[i] <> New)
then
with TAQCustomNCCtrlBase(Components[i]) do
if FIsMouseEnter then
begin
FIsMouseEnter := False;
if not FIsMouseDown then
ReleaseCapture();
MouseLeave();
end;
end;
var
i: Integer;
begin
Result := False;
with FForm do
for i := ComponentCount – 1 downto 0 do
if Components[i] is TAQCustomNCCtrlBase then
with TAQCustomNCCtrlBase(Components[i]) do
if PtInRect(GetWindowRect(), Point(X, Y)) then
begin
MouseMove(X, Y);
if not FIsMouseEnter then
begin
CheckPrev(TAQCustomNCCtrlBase(FForm.Components[i]));
FIsMouseEnter := True;
SetCapture(FForm.Handle);
MouseEnter();
end;
Result := True;
exit;
end
else if FIsMouseEnter then
begin
FIsMouseEnter := False;
if not FIsMouseDown then
ReleaseCapture();
MouseLeave();
end;
end;
另外,为了多个窗口共享一张图片,可以制作一个DATA控件,用于存储图片: