Files
HeidiSQL/source/vcl-styles-utils/Vcl.Styles.Utils.SysStyleHook.pas

1415 lines
40 KiB
ObjectPascal

// **************************************************************************************************
//
// Unit Vcl.Styles.SysStyleHook
// unit for the VCL Styles Utils
// https://github.com/RRUZ/vcl-styles-utils/
//
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License");
// you may not use this file except in compliance with the License. You may obtain a copy of the
// License at http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF
// ANY KIND, either express or implied. See the License for the specific language governing rights
// and limitations under the License.
//
// The Original Code is uSysStyleHook.pas.
//
// Portions created by Mahdi Safsafi [SMP3] e-mail SMP@LIVE.FR
// Portions created by Rodrigo Ruz V. are Copyright (C) 2013-2023 Rodrigo Ruz V.
// All Rights Reserved.
//
// **************************************************************************************************
unit Vcl.Styles.Utils.SysStyleHook;
interface
uses
System.Classes,
System.Types,
System.SysUtils,
Winapi.Windows,
Winapi.Messages,
Winapi.UxTheme,
Winapi.CommCtrl,
Vcl.Themes,
Vcl.ExtCtrls,
Vcl.Controls,
Vcl.Graphics;
const
CM_BASE = WM_USER + $113;
CM_CTLCOLORBTN = CM_BASE + WM_CTLCOLORBTN;
CM_CTLCOLORDLG = CM_BASE + WM_CTLCOLORDLG;
CM_CTLCOLOREDIT = CM_BASE + WM_CTLCOLOREDIT;
CM_CTLCOLORLISTBOX = CM_BASE + WM_CTLCOLORLISTBOX;
CM_CTLCOLORMSGBOX = CM_BASE + WM_CTLCOLORMSGBOX;
CM_CTLCOLORSCROLLBAR = CM_BASE + WM_CTLCOLORSCROLLBAR;
CM_CTLCOLORSTATIC = CM_BASE + WM_CTLCOLORSTATIC;
CM_SCROLLTRACKING = CM_BASE + 350;
CM_PARENTHOOKED = CM_BASE + 360;
CM_CONTROLHOOKED = CM_BASE + 361;
CM_INITCHILDS = CM_BASE + 362;
CM_CONTROLHOOKEDDIRECTLY = CM_BASE + 363;
type
TBidiModeDirection = (bmLeftToRight, bmRightToLeft);
type
TSysStyleHook = class;
TMouseTrackSysControlStyleHook = class;
TSysControl = class;
TSysStyleHookClass = class of TSysStyleHook;
{$REGION 'TSysControl'}
TSysControl = class
private
FFont: TFont;
FParent: TSysControl;
FHandle: THandle;
FWindowClassName: string;
FDestroyed: Boolean;
function GetParent: TSysControl;
function GetParentHandle: THandle;
function GetText: String;
function GetStyle: NativeInt;
function GetExStyle: NativeInt;
function GetWidth: Integer;
function GetHeight: Integer;
function GetLeft: Integer;
function GetTop: Integer;
function GetBorder: Boolean;
function GetEnabled: Boolean;
function GetVisible: Boolean;
function GetClientRect: TRect;
function GetWinRect: TRect;
function GetClientEdge: Boolean;
function GetControlClassName: String;
function GetWndProc: NativeInt;
procedure SetWndProc(Value: NativeInt);
function GetBidiMode: TBidiModeDirection;
procedure SetExStyle(const Value: NativeInt);
procedure SetStyle(const Value: NativeInt);
function GetControlID: Integer;
function GetBoundsRect: TRect;
function GetFont: TFont;
function IsControlChild: Boolean;
function GetClientHeight: Integer;
function GetClientWidth: Integer;
public
constructor Create(AHandle: THandle); virtual;
Destructor Destroy; override;
property ClientHeight: Integer read GetClientHeight;
property ClientWidth: Integer read GetClientWidth;
property Font: TFont read GetFont;
property Parent: TSysControl read GetParent;
property ParentHandle: THandle read GetParentHandle;
property Handle: THandle read FHandle write FHandle;
property Text: String read GetText;
property Style: NativeInt read GetStyle write SetStyle;
property ExStyle: NativeInt read GetExStyle write SetExStyle;
property Width: Integer read GetWidth;
property Height: Integer read GetHeight;
property Left: Integer read GetLeft;
property Top: Integer read GetTop;
property HasBorder: Boolean read GetBorder;
property Enabled: Boolean read GetEnabled;
property Visible: Boolean read GetVisible;
property ClientRect: TRect read GetClientRect;
property WindowRect: TRect read GetWinRect;
property HasClientEdge: Boolean read GetClientEdge;
property ControlClassName: string read GetControlClassName;
property WndProc: NativeInt read GetWndProc write SetWndProc;
property BidiMode: TBidiModeDirection read GetBidiMode;
property ControlID: Integer read GetControlID;
property BoundsRect: TRect read GetBoundsRect;
property IsChild: Boolean read IsControlChild;
property Destroyed: Boolean read FDestroyed write FDestroyed; //WM_DESTROY
function DrawTextBiDiModeFlags(const Flags: Longint): Longint;
function UseRightToLeftAlignment: Boolean; dynamic;
function DrawTextBiDiModeFlagsReadingOnly: Longint;
function UseRightToLeftReading: Boolean;
function Focused: Boolean; dynamic;
end;
{$ENDREGION}
{$REGION 'TSysStyleHook'}
TSysStyleHook = class
private
FHandle: HWND;
FProcInstance: Pointer;
FOrgWndProc: NativeInt;
FSysControl: TSysControl;
FOverrideEraseBkgnd: Boolean;
FOverridePaint: Boolean;
FOverridePaintNC: Boolean;
FOverrideFont: Boolean;
FDoubleBuffered: Boolean;
FPaintOnEraseBkgnd: Boolean;
FFontColor: TColor;
FBrush: TBrush;
FHandled: Boolean;
FParentColor: Boolean;
{$IF CompilerVersion > 23}
FStyleElements: TStyleElements;
{$IFEND}
FColor: TColor;
FFont: TFont;
FText: string;
FHookedDirectly, FMustRemove: Boolean;
procedure WMPaint(var Message: TMessage); message WM_PAINT;
procedure WMNCPaint(var Message: TMessage); message WM_NCPAINT;
procedure WMEraseBkgnd(var Message: TMessage); message WM_ERASEBKGND;
{$IF CompilerVersion > 23}
procedure SetStyleElements(Value: TStyleElements);
{$IFEND}
function GetFontColor: TColor;
function GetColor: TColor;
procedure SetColor(const Value: TColor);
procedure SetOverridePaint(const Value: Boolean);
function GetFocused: Boolean;
function GetParentHandle: HWND;
procedure SetFont(Value: TFont);
function UseLeftScrollBar: Boolean;
function GetText: string;
protected
function GetBorderSize: TRect; virtual;
function CheckIfParentBkGndPainted: Boolean; virtual;
function CheckIfParentHooked: Boolean;
procedure Paint(Canvas: TCanvas); virtual;
procedure DrawParentBackground(DC: HDC); overload;
procedure DrawParentBackground(DC: HDC; const ARect: PRect); overload;
procedure PaintBorder(Control: TSysControl; const EraseLRCorner: Boolean);
procedure DrawBorder(Canvas: TCanvas); virtual;
procedure PaintBackground(Canvas: TCanvas); virtual;
procedure PaintNC(Canvas: TCanvas); virtual;
function CallDefaultProc(var Msg: TMessage): LRESULT;
procedure SetRedraw(const Value: Boolean); overload;
procedure SetRedraw(AHandle: HWND; const Value: Boolean); overload; virtual;
function StyleServicesEnabled: Boolean;
procedure WndProc(var Message: TMessage); virtual;
function InternalPaint(DC: HDC): Boolean; virtual;
procedure UpdateColors; virtual;
function PaintControls(AControl: HWND; DC: HDC): Boolean;
property HookedDirectly: Boolean read FHookedDirectly write FHookedDirectly;
property MustRemove: Boolean read FMustRemove;
public
constructor Create(AHandle: THandle); virtual;
Destructor Destroy; override;
procedure Invalidate; virtual;
procedure InvalidateNC; virtual;
procedure Refresh; virtual;
procedure DrawControlText(Canvas: TCanvas; Details: TThemedElementDetails; const S: string; var R: TRect; const Flags: Cardinal);
function DrawTextCentered(DC: HDC; Details: TThemedElementDetails; const R: TRect; S: String; Const Flags: DWORD = 0): Integer;
function DrawText(DC: HDC; Details: TThemedElementDetails; S: String; var R: TRect; Const Flags: TTextFormat = []): Integer;
property Handle: HWND read FHandle;
property ParentHandle: HWND read GetParentHandle;
property Handled: Boolean read FHandled write FHandled;
property SysControl: TSysControl read FSysControl write FSysControl;
{$IF CompilerVersion > 23}
property StyleElements: TStyleElements read FStyleElements write SetStyleElements;
{$IFEND}
property DoubleBuffered: Boolean read FDoubleBuffered write FDoubleBuffered;
property OverridePaint: Boolean read FOverridePaint write SetOverridePaint;
property OverridePaintNC: Boolean read FOverridePaintNC write FOverridePaintNC;
property OverrideFont: Boolean read FOverrideFont write FOverrideFont;
property OverrideEraseBkgnd: Boolean read FOverrideEraseBkgnd write FOverrideEraseBkgnd;
property FontColor: TColor read GetFontColor write FFontColor;
property Color: TColor read GetColor write SetColor;
property Brush: TBrush read FBrush;
property Font: TFont read FFont write SetFont;
property Focused: Boolean read GetFocused;
property ParentBkGndPainted: Boolean read CheckIfParentBkGndPainted;
property ParentColor: Boolean read FParentColor write FParentColor;
property Text: string read GetText;
end;
{$ENDREGION}
{$REGION 'TMouseTrackSysControlStyleHook'}
TMouseTrackSysControlStyleHook = class(TSysStyleHook)
private
FMouseInControl: Boolean;
FMouseInNCArea: Boolean;
FHotTrackTimer: TComponent;
FMouseDown: Boolean;
procedure WMMouseMove(var Message: TWMMouse); message WM_MOUSEMOVE;
procedure WMNCMouseMove(var Message: TWMMouse); message WM_NCMOUSEMOVE;
procedure WMLButtonDown(var Message: TWMLButtonDown); message WM_LBUTTONDOWN;
procedure WMLButtonUp(var Message: TWMLButtonUp); message WM_LBUTTONUP;
protected
procedure MouseEnter; virtual;
procedure MouseLeave; virtual;
function IsChildHandle(AHandle: HWND): Boolean; virtual;
procedure StartHotTrackTimer;
procedure StopHotTrackTimer;
procedure DoHotTrackTimer(Sender: TObject); virtual;
public
constructor Create(AHandle: THandle); override;
Destructor Destroy; override;
property MouseInControl: Boolean read FMouseInControl write FMouseInControl;
property MouseInNCArea: Boolean read FMouseInNCArea write FMouseInNCArea;
property MouseDown: Boolean read FMouseDown;
end;
{$ENDREGION}
function IsControlHooked(Handle: HWND): Boolean;
implementation
uses
System.UITypes,
Vcl.Styles.Utils.Misc,
Vcl.Styles.Utils.SysControls;
// ------------------------------------------------------------------------------
function IsControlHooked(Handle: HWND): Boolean;
begin
{ Return True if Control is already hooked ! }
Result := False;
if Handle > 0 then
Result := (SendMessage(Handle, CM_CONTROLHOOKED, 0, 0) = $77);
end;
// ------------------------------------------------------------------------------
{ TSysControl }
{$REGION 'TSysControl'}
constructor TSysControl.Create(AHandle: THandle);
begin
inherited Create;
FFont := nil;
FParent := nil;
Handle := AHandle;
FWindowClassName := '';
FDestroyed := False;
end;
destructor TSysControl.Destroy;
begin
if Assigned(FParent) then
FreeAndNil(FParent);
if FFont <> nil then
FFont.Free;
inherited;
end;
function TSysControl.DrawTextBiDiModeFlags(const Flags: Integer): Longint;
begin
Result := Flags;
{ do not change center alignment }
if UseRightToLeftAlignment then
if Result and DT_RIGHT = DT_RIGHT then
Result := Result and not DT_RIGHT { removing DT_RIGHT, makes it DT_LEFT }
else if not(Result and DT_CENTER = DT_CENTER) then
Result := Result or DT_RIGHT;
Result := Result or DrawTextBiDiModeFlagsReadingOnly;
end;
function TSysControl.DrawTextBiDiModeFlagsReadingOnly: Longint;
begin
if UseRightToLeftReading then
Result := DT_RTLREADING
else
Result := 0;
end;
function TSysControl.Focused: Boolean;
begin
Result := (Handle <> 0) and (GetFocus = Handle);
end;
function TSysControl.GetBidiMode: TBidiModeDirection;
begin
Result := bmLeftToRight;
if Style <> 0 then
if (ExStyle and WS_EX_RIGHT = WS_EX_RIGHT) or (ExStyle and WS_EX_RTLREADING = WS_EX_RTLREADING) or (ExStyle and WS_EX_LAYOUTRTL = WS_EX_LAYOUTRTL) then
Result := bmRightToLeft;
end;
function TSysControl.GetBorder: Boolean;
begin
Result := (Style and WS_BORDER = WS_BORDER) or (ExStyle and WS_EX_CLIENTEDGE = WS_EX_CLIENTEDGE);
end;
function TSysControl.GetBoundsRect: TRect;
begin
Result.Left := Left;
Result.Top := Top;
Result.Right := Left + Width;
Result.Bottom := Top + Height;
end;
function TSysControl.GetClientEdge: Boolean;
begin
Result := ExStyle and WS_EX_CLIENTEDGE = WS_EX_CLIENTEDGE;
end;
function TSysControl.GetClientHeight: Integer;
begin
Result := ClientRect.Bottom;
end;
function TSysControl.GetClientRect: TRect;
begin
Result := Rect(0, 0, 0, 0);
Winapi.Windows.GetClientRect(Handle, Result);
end;
function TSysControl.GetClientWidth: Integer;
begin
Result := ClientRect.Right;
end;
function TSysControl.GetControlClassName: String;
begin
if FWindowClassName='' then
FWindowClassName := GetWindowClassName(Handle);
Result:=FWindowClassName;
end;
function TSysControl.GetControlID: Integer;
begin
Result := GetWindowLongPtr(Handle, GWL_ID);
end;
function TSysControl.GetEnabled: Boolean;
begin
Result := False;
if Handle > 0 then
Result := IsWindowEnabled(Handle);
end;
function TSysControl.GetHeight: Integer;
begin
Result := WindowRect.Height;
end;
function TSysControl.GetLeft: Integer;
begin
Result := WindowRect.Left;
end;
function TSysControl.GetParent: TSysControl;
begin
Result := nil;
if Assigned(FParent) then
FreeAndNil(FParent);
if ParentHandle <> 0 then
begin
FParent := TSysControl.Create(ParentHandle);
Result := FParent;
end;
end;
function TSysControl.GetParentHandle: THandle;
begin
Result := Winapi.Windows.GetParent(Handle);
end;
function TSysControl.GetStyle: NativeInt;
begin
Result := GetWindowLongPtr(Handle, GWL_STYLE);
end;
function TSysControl.GetExStyle: NativeInt;
begin
Result := GetWindowLongPtr(Handle, GWL_EXSTYLE);
end;
function TSysControl.GetFont: TFont;
var
LogFont: TLogFont;
hFont: HGDIOBJ;
begin
if FFont <> nil then
Exit(FFont);
hFont := HGDIOBJ(SendMessage(Handle, WM_GETFONT, 0, 0));
Result := TFont.Create;
FillChar(LogFont, SizeOf(LogFont), 0);
GetObject(hFont, SizeOf(LogFont), @LogFont);
Result.Name := StrPas(LogFont.lffaceName);
Result.Height := LogFont.lfHeight;
if LogFont.lfWeight >= FW_MEDIUM then
Result.Style := Result.Style + [fsBold];
if LogFont.lfItalic <> 0 then
Result.Style := Result.Style + [fsItalic];
if LogFont.lfUnderline <> 0 then
Result.Style := Result.Style + [fsUnderline];
if LogFont.lfStrikeout <> 0 then
Result.Style := Result.Style + [fsStrikeout];
case (LogFont.lfPitchAndFamily and 3) of
VARIABLE_PITCH: Result.Pitch := fpVariable;
FIXED_PITCH: Result.Pitch := fpFixed;
end;
FFont := Result;
end;
function TSysControl.GetText: String;
var
Buffer: array [0 .. 1023] of Char;
begin
SetString(Result, Buffer, Winapi.Windows.GetWindowText(Handle, Buffer, Length(Buffer)));
end;
function TSysControl.GetTop: Integer;
begin
Result := WindowRect.Top;
end;
function TSysControl.GetVisible: Boolean;
begin
Result := IsWindowVisible(Handle);
end;
function TSysControl.GetWidth: Integer;
begin
Result := WindowRect.Width;
end;
function TSysControl.GetWinRect: TRect;
begin
Result := Rect(0, 0, 0, 0);
GetWindowRect(Handle, Result);
end;
function TSysControl.GetWndProc: NativeInt;
begin
Result := GetWindowLongPtr(Handle, GWL_WNDPROC);
end;
function TSysControl.IsControlChild: Boolean;
begin
Result := (Style and WS_CHILD = WS_CHILD);
end;
procedure TSysControl.SetExStyle(const Value: NativeInt);
begin
SetWindowLongPtr(Handle, GWL_EXSTYLE, Value);
end;
procedure TSysControl.SetStyle(const Value: NativeInt);
begin
SetWindowLongPtr(Handle, GWL_STYLE, Value);
end;
procedure TSysControl.SetWndProc(Value: NativeInt);
begin
if Value <> WndProc then
SetWindowLongPtr(Handle, GWL_WNDPROC, Value);
end;
function TSysControl.UseRightToLeftAlignment: Boolean;
begin
Result := SysLocale.MiddleEast and (BidiMode = TBidiModeDirection.bmRightToLeft);
end;
function TSysControl.UseRightToLeftReading: Boolean;
begin
Result := SysLocale.MiddleEast and (BidiMode <> TBidiModeDirection.bmLeftToRight);
end;
{$ENDREGION}
{ TSysStyleHook }
{$REGION 'TSysStyleHook'}
constructor TSysStyleHook.Create(AHandle: THandle);
begin
FHandled := False;
FSysControl := nil;
FHandle := AHandle;
FOrgWndProc := 0;
FProcInstance := nil;
FBrush := nil;
FFont := TFont.Create;
{$IF CompilerVersion > 23}
StyleElements := [];
{$IFEND}
FMustRemove := False;
FParentColor := False;
FDoubleBuffered := False;
FPaintOnEraseBkgnd := False;
FHookedDirectly := False;
OverridePaint := False;
OverridePaintNC := False;
OverrideEraseBkgnd := False;
OverrideFont := False;
if AHandle > 0 then
begin
FProcInstance := MakeObjectInstance(WndProc);
FSysControl := TSysControl.Create(AHandle);
FOrgWndProc := FSysControl.WndProc;
// if FOrgWndProc > 0 then
begin
FSysControl.WndProc := LONG_PTR(FProcInstance);
FBrush := TBrush.Create;
UpdateColors;
end;
end;
end;
destructor TSysStyleHook.Destroy;
begin
if FOrgWndProc <> 0 then
FSysControl.WndProc := FOrgWndProc;
if Assigned(FProcInstance) then
FreeObjectInstance(FProcInstance);
if Assigned(FSysControl) then
FreeAndNil(FSysControl);
if Assigned(FBrush) then
FreeAndNil(FBrush);
if Assigned(FFont) then
FreeAndNil(FFont);
inherited;
end;
function TSysStyleHook.CallDefaultProc(var Msg: TMessage): LRESULT;
begin
Result := 0;
try
if (FOrgWndProc <> 0) then
Result := CallWindowProc(Pointer(FOrgWndProc), Handle, Msg.Msg, Msg.wParam, Msg.lParam);
except
on e: exception do
OutputDebugString(PWideChar('CallDefaultProc error: ' + e.message + chr(0)));
end;
end;
procedure TSysStyleHook.DrawBorder(Canvas: TCanvas);
var
BorderSize: TRect;
begin
BorderSize := GetBorderSize;
with BorderSize do
if (Left > 0) and (Right > 0) and (Top > 0) and (Bottom > 0) then
PaintBorder(SysControl, True);
end;
procedure TSysStyleHook.DrawControlText(Canvas: TCanvas; Details: TThemedElementDetails; const S: string; var R: TRect; const Flags: Cardinal);
var
ThemeTextColor: TColor;
TextFormat: TTextFormatFlags;
begin
Canvas.Font := SysControl.Font;
TextFormat := TTextFormatFlags(Flags);
if StyleServices.GetElementColor(Details, ecTextColor, ThemeTextColor) then
begin
Canvas.Font.Color := ThemeTextColor;
StyleServices.DrawText(Canvas.Handle, Details, S, R, TextFormat, Canvas.Font.Color);
end
else
begin
Canvas.Refresh;
StyleServices.DrawText(Canvas.Handle, Details, S, R, TextFormat);
end;
end;
procedure TSysStyleHook.DrawParentBackground(DC: HDC; const ARect: PRect);
var
Bmp: TBitmap;
P: TPoint;
begin
P := Point(0, 0);
if ARect <> nil then
P := Point(ARect.Left, ARect.Top);
Bmp := TBitmap.Create;
try
Bmp.SetSize(SysControl.Parent.Width, SysControl.Parent.Height);
SendMessage(ParentHandle, WM_ERASEBKGND, Bmp.Canvas.Handle, $93);
ClientToScreen(Handle, P);
ScreenToClient(ParentHandle, P);
if ARect <> nil then
BitBlt(DC, ARect.Left, ARect.Top, ARect.Width, ARect.Height, Bmp.Canvas.Handle, P.X, P.Y, SRCCOPY)
else
BitBlt(DC, 0, 0, SysControl.Width, SysControl.Height, Bmp.Canvas.Handle, P.X, P.Y, SRCCOPY);
finally
Bmp.Free;
end;
end;
function TSysStyleHook.DrawText(DC: HDC; Details: TThemedElementDetails; S: String; var R: TRect; const Flags: TTextFormat): Integer;
var
DrawFlags: Cardinal;
SaveIndex: Integer;
LColor: TColor;
begin
SaveIndex := SaveDC(DC);
try
SetBkMode(DC, TRANSPARENT);
if not StyleServices.GetElementColor(Details, ecTextColor, LColor) then
LColor := FontColor;
if not OverrideFont then
LColor := FontColor;
SetTextColor(DC, ColorToRGB(LColor));
DrawFlags := TTextFormatFlags(Flags);
Result := Winapi.Windows.DrawText(DC, S, -1, R, DrawFlags);
finally
RestoreDC(DC, SaveIndex);
end;
end;
function TSysStyleHook.DrawTextCentered(DC: HDC; Details: TThemedElementDetails; const R: TRect; S: String; Const Flags: DWORD = 0): Integer;
var
DrawRect: TRect;
DrawFlags: Cardinal;
DrawParams: TDrawTextParams;
SaveIndex: Integer;
LColor: TColor;
begin
SaveIndex := SaveDC(DC);
try
SetBkMode(DC, TRANSPARENT);
if not StyleServices.GetElementColor(Details, ecTextColor, LColor) then
LColor := FontColor;
if not OverrideFont then
LColor := FontColor;
SetTextColor(DC, ColorToRGB(LColor));
DrawRect := R;
DrawFlags := DT_END_ELLIPSIS or DT_WORDBREAK or DT_EDITCONTROL or DT_CENTER;
if DrawFlags <> 0 then
DrawFlags := DrawFlags or Flags;
Winapi.Windows.DrawText(DC, PChar(S), -1, DrawRect, DrawFlags or DT_CALCRECT);
DrawRect.Right := R.Right;
if DrawRect.Bottom < R.Bottom then
OffsetRect(DrawRect, 0, (R.Bottom - DrawRect.Bottom) div 2)
else
DrawRect.Bottom := R.Bottom;
ZeroMemory(@DrawParams, SizeOf(DrawParams));
DrawParams.cbSize := SizeOf(DrawParams);
DrawTextEx(DC, PChar(S), -1, DrawRect, DrawFlags, @DrawParams);
Result := DrawParams.uiLengthDrawn;
finally
RestoreDC(DC, SaveIndex);
end;
end;
function TSysStyleHook.GetFocused: Boolean;
begin
Result := (GetFocus = Handle);
end;
function TSysStyleHook.GetBorderSize: TRect;
begin
Result := Rect(0, 0, 0, 0);
end;
function TSysStyleHook.GetColor: TColor;
begin
// if OverrideEraseBkgnd then
// Result := StyleServices.GetStyleColor(scWindow)
// else
Result := FColor;
end;
function TSysStyleHook.GetFontColor: TColor;
begin
// if OverrideFont then
// Result := StyleServices.GetSystemColor(clWindowText)
// else
Result := FFontColor;
end;
function TSysStyleHook.GetParentHandle: HWND;
begin
Result := GetParent(Handle);
end;
function TSysStyleHook.GetText: string;
var
Buffer: array [0 .. 255] of Char;
begin
if (Handle <> 0) then
SetString(Result, Buffer, Winapi.Windows.GetWindowText(Handle, Buffer, Length(Buffer)));
FText := Result;
end;
function TSysStyleHook.InternalPaint(DC: HDC): Boolean;
begin
Result := False;
end;
procedure TSysStyleHook.SetColor(const Value: TColor);
begin
if (FBrush <> nil) and ((Value <> FColor) or (Value <> FBrush.Color)) then
begin
FColor := Value;
FBrush.Color := Value;
end;
end;
procedure TSysStyleHook.SetFont(Value: TFont);
begin
if Value <> FFont then
FFont.Assign(Value);
end;
procedure TSysStyleHook.SetOverridePaint(const Value: Boolean);
begin
if Value then
OverrideEraseBkgnd := Value;
FOverridePaint := Value;
end;
procedure TSysStyleHook.SetRedraw(AHandle: HWND; const Value: Boolean);
begin
SendMessage(AHandle, WM_SETREDRAW, wParam(Value), 0);
end;
procedure TSysStyleHook.SetRedraw(const Value: Boolean);
begin
SetRedraw(Handle, Value);
end;
{$IF CompilerVersion > 23}
procedure TSysStyleHook.SetStyleElements(Value: TStyleElements);
begin
if Value <> FStyleElements then
begin
FStyleElements := Value;
OverridePaint := (seClient in FStyleElements);
// OverrideEraseBkgnd := OverridePaint;
OverridePaintNC := (seBorder in FStyleElements);
OverrideFont := (seFont in FStyleElements);
end;
end;
{$IFEND}
function TSysStyleHook.StyleServicesEnabled: Boolean;
begin
Result := (StyleServices.Available) and not(StyleServices.IsSystemStyle);
if Result then
if not TSysStyleManager.HookVclControls then
Result := not(IsVCLControl(Handle));
end;
procedure TSysStyleHook.UpdateColors;
begin
if (OverrideEraseBkgnd) or (OverridePaint) then
Color := StyleServices.GetStyleColor(scWindow)
else
Color := clBtnFace;
if OverrideFont then
FontColor := StyleServices.GetSystemColor(clWindowText)
else
FontColor := clBlack;
end;
function TSysStyleHook.UseLeftScrollBar: Boolean;
begin
Result := (SysControl.ExStyle and WS_EX_LEFTSCROLLBAR = WS_EX_LEFTSCROLLBAR)
end;
procedure TSysStyleHook.Invalidate;
begin
if FOverridePaintNC then
InvalidateNC;
InvalidateRect(Handle, nil, False);
end;
procedure TSysStyleHook.InvalidateNC;
begin
SendMessage(Handle, WM_NCPAINT, 0, 0);
end;
procedure TSysStyleHook.Paint(Canvas: TCanvas);
begin
//
end;
procedure TSysStyleHook.PaintBackground(Canvas: TCanvas);
begin
Canvas.Brush.Color := Color;
Canvas.FillRect(SysControl.ClientRect);
end;
procedure TSysStyleHook.PaintBorder(Control: TSysControl; const EraseLRCorner: Boolean);
var
EmptyRect, DrawRect: TRect;
DC: HDC;
H, W: Integer;
AStyle: Integer;
Details: TThemedElementDetails;
BorderSize: TRect;
begin
BorderSize := GetBorderSize;
Control.ExStyle := GetWindowLong(Handle, GWL_EXSTYLE);
if (Control.ExStyle and WS_EX_CLIENTEDGE) <> 0 then
begin
GetWindowRect(Control.Handle, DrawRect);
OffsetRect(DrawRect, -DrawRect.Left, -DrawRect.Top);
DC := GetWindowDC(Control.Handle);
try
EmptyRect := DrawRect;
if EraseLRCorner then
begin
AStyle := GetWindowLong(Control.Handle, GWL_STYLE);
if ((AStyle and WS_HSCROLL) <> 0) and ((AStyle and WS_VSCROLL) <> 0) then
begin
W := GetSystemMetrics(SM_CXVSCROLL);
H := GetSystemMetrics(SM_CYHSCROLL);
InflateRect(EmptyRect, -2, -2);
with EmptyRect do
if not UseLeftScrollBar then
EmptyRect := Rect(Left, Bottom - H, Left + W, Bottom)
else
EmptyRect := Rect(Right - W, Bottom - H, Right, Bottom);
FillRect(DC, EmptyRect, GetSysColorBrush(COLOR_BTNFACE));
end;
end;
with DrawRect do
ExcludeClipRect(DC, Left + BorderSize.Left, Top + BorderSize.Top, Right - BorderSize.Right, Bottom - BorderSize.Bottom);
Details := StyleServices.GetElementDetails(teEditTextNormal);
StyleServices.DrawElement(DC, Details, DrawRect);
finally
ReleaseDC(Control.Handle, DC);
end;
end;
end;
function TSysStyleHook.PaintControls(AControl: HWND; DC: HDC): Boolean;
var
Child: HWND;
SavedDC: HDC;
SysChild: TSysControl;
P: TPoint;
FrameBrush: HBRUSH;
begin
Result := False;
Child := GetTopWindow(AControl);
while Child <> 0 do
begin
Result := True;
SysChild := TSysControl.Create(Child);
with SysChild do
begin
SavedDC := SaveDC(DC);
P := Point(Left, Top);
ScreenToClient(ParentHandle, P);
if Visible and IsChild and RectVisible(DC, Rect(P.X, P.Y, P.X + Width, P.Y + Height)) then
begin
MoveWindowOrg(DC, P.X, P.Y);
IntersectClipRect(DC, 0, 0, Width, Height);
SendMessage(Child, WM_PAINT, lParam(DC), 0);
if SysChild.HasBorder then
begin
// SendMessage(Child, WM_NCPAINT, 0, 0);
FrameBrush := CreateSolidBrush(ColorToRGB(clBtnShadow));
FrameRect(DC, System.Types.Rect(0, 0, Width, Height), FrameBrush);
DeleteObject(FrameBrush);
FrameBrush := CreateSolidBrush(ColorToRGB(clBtnHighlight));
FrameRect(DC, System.Types.Rect(0, 0, Width + 1, Height + 1), FrameBrush);
DeleteObject(FrameBrush);
end;
end;
FreeAndNil(SysChild);
PaintControls(Child, DC);
RestoreDC(DC, SavedDC);
Child := GetNextWindow(Child, GW_HWNDNEXT);
end;
end;
end;
procedure TSysStyleHook.PaintNC(Canvas: TCanvas);
begin
end;
procedure TSysStyleHook.DrawParentBackground(DC: HDC);
begin
DrawParentBackground(DC, nil);
end;
procedure TSysStyleHook.Refresh;
begin
SendMessage(Handle, WM_PAINT, 0, 0);
end;
procedure TSysStyleHook.WMEraseBkgnd(var Message: TMessage);
var
DC: HDC;
Canvas: TCanvas;
SaveIndex: Integer;
begin
Handled := False;
if not StyleServicesEnabled then
Exit;
UpdateColors;
if FOverrideEraseBkgnd then
begin
if not FDoubleBuffered then
begin
DC := HDC(Message.wParam);
SaveIndex := 0;
if DC = 0 then
DC := GetDC(Handle)
else
SaveIndex := SaveDC(DC);
Canvas := TCanvas.Create;
try
Canvas.Handle := DC;
if Assigned(FFont) then
Canvas.Font.Assign(FFont);
if (FParentColor) and (ParentHandle > 0) then
DrawParentBackground(Canvas.Handle)
else
PaintBackground(Canvas);
if (FPaintOnEraseBkgnd) and (Message.lParam <> $93) then
Paint(Canvas);
finally
Canvas.Handle := 0;
Canvas.Free;
if Message.wParam = 0 then
ReleaseDC(Handle, DC)
else if SaveIndex <> 0 then
RestoreDC(DC, SaveIndex);
end;
end;
Handled := True;
Message.Result := 1;
end;
end;
function TSysStyleHook.CheckIfParentBkGndPainted: Boolean;
var
Test: Integer;
PTest: PInteger;
LParentHandle: HWND;
begin
//Exit(True);
Test := $93;
PTest := @Test;
Result := False;
LParentHandle := GetParent(Handle);
if LParentHandle > 0 then
begin
if not IsControlHooked(LParentHandle) then
Exit(False);
SendMessage(LParentHandle, WM_ERASEBKGND, 0, lParam(PTest));
Result := (PTest^ = $11);
end;
end;
function TSysStyleHook.CheckIfParentHooked: Boolean;
begin
Result := (SendMessage(ParentHandle, CM_PARENTHOOKED, 0, 0) = $77);
end;
procedure TSysStyleHook.WMNCPaint(var Message: TMessage);
var
Canvas: TCanvas;
begin
Handled := False;
if not StyleServicesEnabled then
Exit;
if FOverridePaintNC then
begin
Canvas := TCanvas.Create;
try
Canvas.Handle := GetWindowDC(SysControl.Handle);
if Assigned(FFont) then
Canvas.Font.Assign(FFont);
DrawBorder(Canvas);
PaintNC(Canvas);
finally
ReleaseDC(Handle, Canvas.Handle);
Canvas.Handle := 0;
Canvas.Free;
end;
Handled := True;
end;
end;
procedure TSysStyleHook.WMPaint(var Message: TMessage);
var
OrgDC, DC: HDC;
Buffer: TBitmap;
Canvas: TCanvas;
PS: TPaintStruct;
function ClipControls(AControl: HWND; Siblings: Boolean): Boolean;
var
Child: HWND;
SysChild: TSysControl;
P: TPoint;
begin
Result := False;
SysChild := nil;
Child := GetTopWindow(AControl);
if GetParent(Child) = Handle then
while Child <> 0 do
begin
Result := True;
SysChild := TSysControl.Create(Child);
with SysChild, P do
begin
P := Point(Left, Top);
ScreenToClient(Self.Handle, P);
if Visible and IsChild and RectVisible(DC, Rect(X, Y, X + Width, Y + Height)) then
begin
ExcludeClipRect(DC, X, Y, X + Width, Y + Height);
end;
FreeAndNil(SysChild);
if Siblings then
ClipControls(Child, Siblings);
Child := GetNextWindow(Child, GW_HWNDNEXT);
end;
end;
if Assigned(SysChild) then
FreeAndNil(SysChild);
end;
function DoClipControls: Boolean;
begin
Result := False;
if SysControl.Style and WS_CLIPSIBLINGS = WS_CLIPSIBLINGS then
Result := ClipControls(Handle, True)
else if SysControl.Style and WS_CLIPCHILDREN = WS_CLIPCHILDREN then
Result := ClipControls(Handle, False);
end;
begin
Handled := False;
if not StyleServicesEnabled then
Exit;
if OverridePaint then
begin
OrgDC := HDC(Message.wParam);
Canvas := TCanvas.Create;
try
if OrgDC <> 0 then
begin
Canvas.Handle := OrgDC;
DC:= OrgDC;
end
else
begin
DC := GetDC(Handle);
BeginPaint(SysControl.Handle, PS);
Canvas.Handle := DC;
end;
if Assigned(FFont) then
Canvas.Font.Assign(FFont);
if not InternalPaint(Canvas.Handle) then
if FDoubleBuffered and (DC = 0) then
begin
Buffer := TBitmap.Create;
try
Buffer.SetSize(SysControl.Width, SysControl.Height);
DoClipControls;
PaintBackground(Buffer.Canvas);
Paint(Buffer.Canvas);
// PaintControls(Handle,Canvas.Handle);
Canvas.Draw(0, 0, Buffer);
finally
Buffer.Free;
end;
end
else
begin
DoClipControls;
Paint(Canvas);
// PaintControls(Handle,Canvas.Handle);
end;
if OrgDC = 0 then
begin
ReleaseDC(SysControl.Handle, DC);
EndPaint(SysControl.Handle, PS);
end;
finally
Canvas.Handle := 0;
Canvas.Free;
end;
Handled := True;
end;
end;
procedure TSysStyleHook.WndProc(var Message: TMessage);
var
TempResult: LRESULT;
ChildHandle: HWND;
ItemRemoved: Boolean;
begin
case Message.Msg of
CM_CONTROLHOOKEDDIRECTLY:
begin
{ Child controls are not hooked inside the parent . }
FHookedDirectly := True;
Exit;
end;
CM_INITCHILDS:
begin
Message.Result := 0;
with TSysStyleManager do
begin
for ChildHandle in ChildRegSysStylesList.Keys do
if (not IsControlHooked(ChildHandle)) and (ChildRegSysStylesList[ChildHandle].Parent = Handle) then
begin
if not SysStyleHookList.ContainsKey(ChildHandle) then
begin
SysStyleHookList.Add(ChildHandle, ChildRegSysStylesList[ChildHandle].StyleHookClass.Create(ChildHandle));
{ Child control need to be repainted . }
RedrawWindow(ChildHandle, nil, 0, RDW_ERASE or RDW_FRAME or RDW_INTERNALPAINT or RDW_INVALIDATE);
{ Send WM_NCCALCSIZE message to the child control . }
SetWindowPos(ChildHandle, 0, 0, 0, 0, 0, SWP_NOSIZE or SWP_NOMOVE or SWP_NOZORDER or SWP_FRAMECHANGED);
Message.Result := 1;
end;
end;
end;
Exit;
end;
CM_PARENTHOOKED, CM_CONTROLHOOKED:
begin
Message.Result := $77;
Exit;
end;
WM_CHANGEUISTATE, WM_PARENTNOTIFY, WM_QUERYUISTATE:
begin
CallDefaultProc(Message);
{
Do not Send ===> Use Post
Return first then hook child !
}
PostMessage(Handle, CM_INITCHILDS, 0, 0);
Exit;
end;
WM_ERASEBKGND:
begin
if (Message.lParam > 0) and (Message.wParam = 0) and (FOverrideEraseBkgnd or FOverridePaint or FPaintOnEraseBkgnd) then
if PInteger(Message.lParam)^ = $93 then
begin
{ lParam = Result
if (lParam=$11) then Parent background was painted .
}
PInteger(Message.lParam)^ := $11;
{ Do not process the default message ..
this is only for test !! .
}
Exit; { Do not Dispatch . }
end;
end;
WM_SETREDRAW:
begin
Message.Result := CallDefaultProc(Message);
Dispatch(Message);
Exit;
end;
WM_CTLCOLORMSGBOX .. WM_CTLCOLORSTATIC:
begin
// avoid use cuurent style colors on ignored controls
if (not StyleServicesEnabled) or (not TSysStyleManager.UseStyleColorsChildControls and (not TSysStyleManager.SysStyleHookList.ContainsKey(Message.lParam))) then
// if (not StyleServicesEnabled) then
begin
Message.Result := CallDefaultProc(Message);
Exit;
end;
TempResult := SendMessage(Handle, CM_BASE + Message.Msg, Message.wParam, Message.lParam);
Message.Result := SendMessage(Message.lParam, CM_BASE + Message.Msg, Message.wParam, Message.lParam);
if Message.Result = 0 then
Message.Result := TempResult;
Exit;
end;
CM_CTLCOLORMSGBOX .. CM_CTLCOLORSTATIC:
begin
SetTextColor(Message.wParam, ColorToRGB(FontColor));
SetBkColor(Message.wParam, ColorToRGB(FBrush.Color));
Message.Result := LRESULT(FBrush.Handle);
Exit;
end;
WM_DESTROY:
begin
Message.Result := CallDefaultProc(Message);
Dispatch(Message);
Exit;
end;
//The WM_NCDESTROY message is sent after the child windows have been destroyed.
//In contrast, WM_DESTROY is sent before the child windows are destroyed.
WM_NCDESTROY:
begin
Message.Result := CallDefaultProc(Message);
ItemRemoved:=False;
if TSysStyleManager.SysStyleHookList.ContainsKey(FHandle) then
begin
// OutputDebugString(PChar('SysStyleHookList WM_NCDESTROY Removed '+IntToHex(Handle, 8)));
// TSysStyleManager.SysStyleHookList.Remove(FHandle);
FMustRemove:=True;
ItemRemoved:=True;
end;
if not ItemRemoved and TSysStyleManager.ChildRegSysStylesList.ContainsKey(FHandle) then
begin
TSysStyleManager.ChildRegSysStylesList.Remove(Handle);
//OutputDebugString(PChar('ChildRegSysStylesList WM_NCDESTROY Removed '+IntToHex(Handle, 8)));
end;
for ChildHandle in TSysStyleManager.ChildRegSysStylesList.Keys do
if (TSysStyleManager.ChildRegSysStylesList[ChildHandle].Parent = FHandle) then
begin
TSysStyleManager.ChildRegSysStylesList.Remove(ChildHandle);
//OutputDebugString(PChar('Sub ChildRegSysStylesList WM_NCDESTROY Removed '+IntToHex(ChildHandle, 8)));
end;
Exit;
end;
end;
Dispatch(Message);
if not Handled then
Message.Result := CallDefaultProc(Message);
Handled := False;
end;
{$ENDREGION}
{ TMouseTrackSysControlStyleHook }
{$REGION 'TMouseTrackSysControlStyleHook'}
constructor TMouseTrackSysControlStyleHook.Create(AHandle: THandle);
begin
inherited;
FMouseInControl := False;
FMouseInNCArea := False;
FHotTrackTimer := nil;
end;
destructor TMouseTrackSysControlStyleHook.Destroy;
begin
if Assigned(FHotTrackTimer) then
FreeAndNil(FHotTrackTimer);
inherited;
end;
procedure TMouseTrackSysControlStyleHook.WMLButtonDown(var Message: TWMLButtonDown);
begin
FMouseDown := True;
inherited;
end;
procedure TMouseTrackSysControlStyleHook.WMLButtonUp(var Message: TWMLButtonUp);
begin
FMouseDown := False;
inherited;
end;
procedure TMouseTrackSysControlStyleHook.WMMouseMove(var Message: TWMMouse);
begin
inherited;
if not FMouseInControl and not FMouseInNCArea then
begin
FMouseInControl := True;
StartHotTrackTimer;
MouseEnter;
end
else if FMouseInNCArea and FMouseInControl then
begin
StopHotTrackTimer;
FMouseInControl := False;
MouseLeave;
end;
end;
procedure TMouseTrackSysControlStyleHook.WMNCMouseMove(var Message: TWMMouse);
begin
inherited;
if not FMouseInControl then
begin
FMouseInControl := True;
StartHotTrackTimer;
MouseEnter;
end;
end;
procedure TMouseTrackSysControlStyleHook.StartHotTrackTimer;
begin
if FHotTrackTimer <> nil then
StopHotTrackTimer;
FHotTrackTimer := TTimer.Create(nil);
TTimer(FHotTrackTimer).Interval := 100;
TTimer(FHotTrackTimer).OnTimer := DoHotTrackTimer;
TTimer(FHotTrackTimer).Enabled := True;
end;
procedure TMouseTrackSysControlStyleHook.StopHotTrackTimer;
begin
if FHotTrackTimer <> nil then
begin
TTimer(FHotTrackTimer).Enabled := False;
FreeAndNil(FHotTrackTimer);
end;
end;
function TMouseTrackSysControlStyleHook.IsChildHandle(AHandle: HWND): Boolean;
begin
Result := False;
end;
procedure TMouseTrackSysControlStyleHook.DoHotTrackTimer(Sender: TObject);
var
P: TPoint;
FWindowHandle: HWND;
begin
GetCursorPos(P);
FWindowHandle := WindowFromPoint(P);
if (FWindowHandle <> Handle) and not IsChildHandle(FWindowHandle) then
begin
StopHotTrackTimer;
FMouseInControl := False;
MouseLeave;
end;
end;
procedure TMouseTrackSysControlStyleHook.MouseEnter;
begin
end;
procedure TMouseTrackSysControlStyleHook.MouseLeave;
begin
end;
{$ENDREGION}
end.