diff --git a/Common/Vcl.Styles.DPIAware.pas b/Common/Vcl.Styles.DPIAware.pas index 1a865f8..daa022d 100644 --- a/Common/Vcl.Styles.DPIAware.pas +++ b/Common/Vcl.Styles.DPIAware.pas @@ -1,3 +1,32 @@ +{ + Modified 20-Mar-2019 by Rickard Johansson (www.rj-texted.se). + Purpose: Add per-monitor DPI awareness + Usage: Add the unit to the interface uses statement of the main form and add code below: + + TMyForm = class(TForm) + private + FStyleDPIAwareness : TStyleDPIAwareness; + + procedure TFrmMain.FormCreate(Sender: TObject); + begin + FStyleDPIAwareness := TStyleDPIAwareness.Create(Self); + FStyleDPIAwareness.Parent := Self; + + procedure TFrmMain.FormDestroy(Sender: TObject); + begin + FStyleDPIAwareness.Free; + + procedure TFrmMain.FormAfterMonitorDpiChanged(Sender: TObject; OldDPI, NewDPI: Integer); + begin + FStyleDPIAwareness.AfterDPIChange(OldDPI, NewDPI); + end; + + procedure TFrmMain.FormBeforeMonitorDpiChanged(Sender: TObject; OldDPI, NewDPI: Integer); + begin + FStyleDPIAwareness.BeforeDPIChange(OldDPI, NewDPI); + end; +} + {----------------------------------------------------------------------------- Unit Name: VCL.Styles.DPIAware Author: PyScripter (https://github.com/pyscripter) @@ -6,9 +35,9 @@ History: -----------------------------------------------------------------------------} { - To use the unit just add it to the implementation uses statement of the main form and add + To use the unit just add it to the implementation uses statement of the main form and add the following code to the FormCreate handler. - + procedure TFrmMain.FormCreate(Sender: TObject); Var StyleDPIAwareness : TStyleDPIAwareness; @@ -16,12 +45,12 @@ StyleDPIAwareness := TStyleDPIAwareness.Create(Self); StyleDPIAwareness.Parent := Self; - By default the component scales the styles at multiples of 100%. You can change that, + By default the component scales the styles at multiples of 100%. You can change that, by adding the line: - + StyleDPIAwareness.RoundScalingFactor := False; - - With this statement styles are scaled to whatever scaling factor results for Screen.PixelsPerInch. + + With this statement styles are scaled to whatever scaling factor results for Screen.PixelsPerInch. Most of the styles would work fine, but a few may show some visual defects. Limitations: @@ -37,21 +66,31 @@ interface Vcl.Controls, Vcl.Forms, Vcl.Themes, Vcl.Styles; Type + TStyleDPI = class(TObject) + private + FCurrentDPI: Integer; + public + property CurrentDPI: Integer read FCurrentDPI write FCurrentDPI; + end; + TStyleDPIAwareness = class(TControl) private FScaledStyles : TStringList; FRoundScalingFactor : Boolean; FUseCustomScalingFactor : Boolean; FCustomPPI : integer; + FOldDPI: Integer; protected procedure CMStyleChanged(var Message: TMessage); message CM_STYLECHANGED; procedure RecreateForms; public constructor Create(AOwner: TComponent); override; destructor Destroy; override; + procedure AfterDPIChange(OldDPI, NewDPI: Integer); + procedure BeforeDPIChange(OldDPI, NewDPI: Integer); procedure ScaleStyle(Style : TCustomStyleServices); + property OldDPI: Integer read FOldDPI write FOldDPI; published - {Rounds the scaling factorto the nearest 100%} property RoundScalingFactor : Boolean read FRoundScalingFactor write FRoundScalingFactor default True; property UseCustomScalingFactor : Boolean read FUseCustomScalingFactor @@ -62,10 +101,7 @@ TStyleDPIAwareness = class(TControl) implementation Uses - System.Rtti, - {$IFDEF VER330} // RAD Studio 10.3 - DDetours; - {$ENDIF VER330} + System.Rtti, DDetours, System.Math; { TStyleDPIAwareness } @@ -94,7 +130,8 @@ constructor TStyleDPIAwareness.Create(AOwner: TComponent); inherited; FRoundScalingFactor := True; FUseCustomScalingFactor := False; - FCustomPPI :=96; + FCustomPPI := 96; + FOldDPI := 96; FScaledStyles := TStringList.Create; FScaledStyles.Sorted := False; @@ -108,54 +145,68 @@ destructor TStyleDPIAwareness.Destroy; inherited; end; +procedure TStyleDPIAwareness.AfterDPIChange(OldDPI, NewDPI: Integer); +begin + ScaleStyle(TStyleManager.ActiveStyle); +end; + +procedure TStyleDPIAwareness.BeforeDPIChange(OldDPI, NewDPI: Integer); +begin + FOldDPI := OldDPI; +end; + procedure TStyleDPIAwareness.RecreateForms; Var i : Integer; begin for i := 0 to Screen.FormCount - 1 do - Screen.Forms[i].Perform(CM_RECREATEWND, 0, 0); + begin + if Screen.Forms[i] <> TForm(Owner) then + Screen.Forms[i].Perform(CM_RECREATEWND, 0, 0); + end; end; procedure TStyleDPIAwareness.ScaleStyle(Style: TCustomStyleServices); Var - DPI : integer; + NewDPI : integer; SeStyle : TObject; SeStyleSource : TObject; BitmapList : TList; BitMap : TBitmap; StyleObjectList : Tlist; - i : integer; + i,n: integer; StyleObject : TComponent; + obj: TStyleDPI; procedure ProcessBitmapLink(BL : TObject); Var BLType : TRTTIType; begin BLType := TRttiContext.Create.GetType(BL.ClassType); - BLType.GetProperty('Bottom').SetValue(BL, mulDiv(BLType.GetProperty('Bottom').GetValue(BL).AsInteger, DPI, 96)); - BLType.GetProperty('Right').SetValue(BL, mulDiv(BLType.GetProperty('Right').GetValue(BL).AsInteger, DPI, 96)); - BLType.GetProperty('Left').SetValue(BL, mulDiv(BLType.GetProperty('Left').GetValue(BL).AsInteger, DPI, 96)); - BLType.GetProperty('Top').SetValue(BL, mulDiv(BLType.GetProperty('Top').GetValue(BL).AsInteger, DPI, 96)); + BLType.GetProperty('Bottom').SetValue(BL, Round((BLType.GetProperty('Bottom').GetValue(BL).AsInteger * NewDPI - 1) / OldDPI)); + BLType.GetProperty('Right').SetValue(BL, Round(BLType.GetProperty('Right').GetValue(BL).AsInteger * NewDPI / OldDPI)); + BLType.GetProperty('Left').SetValue(BL, Round(BLType.GetProperty('Left').GetValue(BL).AsInteger * NewDPI / OldDPI)); + BLType.GetProperty('Top').SetValue(BL, Round(BLType.GetProperty('Top').GetValue(BL).AsInteger * NewDPI / OldDPI)); end; procedure ProcessSO(aSO : TComponent; aSOType : TRTTIType); begin - aSOType.GetProperty('Top').SetValue(aSO, mulDiv(aSOType.GetProperty('Top').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('Left').SetValue(aSO, mulDiv(aSOType.GetProperty('Left').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('Width').SetValue(aSO, mulDiv(aSOType.GetProperty('Width').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('Height').SetValue(aSO, mulDiv(aSOType.GetProperty('Height').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('MarginTop').SetValue(aSO, mulDiv(aSOType.GetProperty('MarginTop').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('MarginLeft').SetValue(aSO, mulDiv(aSOType.GetProperty('MarginLeft').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('MarginBottom').SetValue(aSO, mulDiv(aSOType.GetProperty('MarginBottom').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('MarginRight').SetValue(aSO, mulDiv(aSOType.GetProperty('MarginRight').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('TextMarginTop').SetValue(aSO, mulDiv(aSOType.GetProperty('TextMarginTop').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('TextMarginLeft').SetValue(aSO, mulDiv(aSOType.GetProperty('TextMarginLeft').GetValue(aSO).AsInteger, DPI, 96)); - aSOType.GetProperty('TextMarginRight').SetValue(aSO, mulDiv(aSOType.GetProperty('TextMarginRight').GetValue(aSO).AsInteger, DPI, 96)); + aSOType.GetProperty('Top').SetValue(aSO, Round(aSOType.GetProperty('Top').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('Left').SetValue(aSO, Round(aSOType.GetProperty('Left').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('Width').SetValue(aSO, Round(aSOType.GetProperty('Width').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('Height').SetValue(aSO, Round(aSOType.GetProperty('Height').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('MarginTop').SetValue(aSO, Round(aSOType.GetProperty('MarginTop').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('MarginLeft').SetValue(aSO, Round(aSOType.GetProperty('MarginLeft').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('MarginBottom').SetValue(aSO, Round(aSOType.GetProperty('MarginBottom').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('MarginRight').SetValue(aSO, Round(aSOType.GetProperty('MarginRight').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('TextMarginTop').SetValue(aSO, Round(aSOType.GetProperty('TextMarginTop').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('TextMarginLeft').SetValue(aSO, Round(aSOType.GetProperty('TextMarginLeft').GetValue(aSO).AsInteger * NewDPI / OldDPI)); + aSOType.GetProperty('TextMarginRight').SetValue(aSO, Round(aSOType.GetProperty('TextMarginRight').GetValue(aSO).AsInteger * NewDPI / OldDPI)); end; procedure ProcessStyleObject(SO : TComponent); var - i : integer; + i: integer; ChildSo : TComponent; SOType : TRTTIType; BitmapLink : TObject; @@ -178,7 +229,7 @@ procedure TStyleDPIAwareness.ScaleStyle(Style: TCustomStyleServices); if So.ClassName = 'TSeSystemButton' then begin // Shift the form title to the right if SO.Name = 'btnSysMenu' then - SOType.GetProperty('Width').SetValue(SO, MulDiv(28, DPI, 96)); + SOType.GetProperty('Width').SetValue(SO, MulDiv(28, NewDPI, OldDPI)); BitmapLink := TRttiContext.Create.GetType(SO.ClassType).GetField('FBitmap').GetValue(SO).AsObject;; ProcessBitmapLink(BitmapLink); BitmapLink := TRttiContext.Create.GetType(SO.ClassType).GetField('FActiveBitmap').GetValue(SO).AsObject;; @@ -209,28 +260,29 @@ procedure TStyleDPIAwareness.ScaleStyle(Style: TCustomStyleServices); end; begin - // Check if Style already scaled then - if FScaledStyles.IndexOf(Style.Name) >= 0 then - Exit; + n := FScaledStyles.IndexOf(TStyleManager.ActiveStyle.Name); + if n >= 0 then + begin + obj := TStyleDPI(FScaledStyles.Objects[n]); + OldDPI := obj.FCurrentDPI; + end; if UseCustomScalingFactor then - DPI := CustomPPI - else begin - DPI := Screen.PixelsPerInch; - if FRoundScalingFactor then - DPI := Round(DPI / 96) * 96; - end; + NewDPI := CustomPPI + else + NewDPI := TForm(Owner).Monitor.PixelsPerInch; - if (Style = TStyleManager.SystemStyle) or (DPI <= 120) then + if (Style = TStyleManager.SystemStyle) then Exit; SeStyle := TRttiContext.Create.GetType(Style.ClassType).GetField('FSource').GetValue(Style).AsObject; SeStyleSource := TRttiContext.Create.GetType(SeStyle.ClassType).GetField('FCleanCopy').GetValue(SeStyle).AsObject; BitMapList := TRttiContext.Create.GetType(SeStyleSource.ClassType).GetField('FBitmaps').GetValue(SeStyleSource).AsObject as TList; - if BitMapList.Count = 1 then begin + if BitMapList.Count = 1 then + begin Bitmap := TObject(BitmapList[0]) as TBitmap; - ResizeBitmap(Bitmap, MulDiv(Bitmap.Width, DPI, 96), Muldiv(Bitmap.Height, DPI, 96)); + ResizeBitmap(Bitmap, Round(Bitmap.Width * NewDPI / OldDPI), Round(Bitmap.Height * NewDPI / OldDPI)); StyleObjectList := TRttiContext.Create.GetType(SeStyleSource.ClassType).GetField('FObjects').GetValue(SeStyleSource).AsObject as TList; for i := 0 to StyleObjectList.Count -1 do begin @@ -240,7 +292,20 @@ procedure TStyleDPIAwareness.ScaleStyle(Style: TCustomStyleServices); TRttiContext.Create.GetType(SeStyle.ClassType).GetMethod('ResetStyle').Invoke(SeStyle, []); end; - FScaledStyles.Add(Style.Name); + + n := FScaledStyles.IndexOf(Style.Name); + if n >= 0 then + begin + obj := TStyleDPI(FScaledStyles.Objects[n]); + obj.FCurrentDPI := NewDPI; + end + else + begin + obj := TStyleDPI.Create; + obj.FCurrentDPI := NewDPI; + FScaledStyles.AddObject(Style.Name, obj); + end; + if Style = TStyleManager.ActiveStyle then RecreateForms; end; @@ -281,9 +346,9 @@ function TFormStyleHookFix.Detour_GetBorderSize: TRect; TMethod(MethodPtr).Code := TMethod(Trampoline_TFormStyleHook_GetBorderSize).Code; TMethod(MethodPtr).Data := Pointer(Self); Result := MethodPtr; - Self.SetStretchedCaptionInc(0); - if (Screen.PixelsPerInch > 96) then - Result.Top := MulDiv(Result.Top, 96, Screen.PixelsPerInch); + Self.SetStretchedCaptionInc(1); + if (Form.Monitor.PixelsPerInch > 96) then + Result.Top := MulDiv(Result.Top, 96, Form.Monitor.PixelsPerInch); end; initialization diff --git a/Common/Vcl.Styles.UxTheme.pas b/Common/Vcl.Styles.UxTheme.pas index b8366a8..84cd2c2 100644 --- a/Common/Vcl.Styles.UxTheme.pas +++ b/Common/Vcl.Styles.UxTheme.pas @@ -4364,57 +4364,58 @@ function Detour_UxTheme_DrawThemeTextEx(hTheme: hTheme; hdc: hdc; iPartId: Integ VCLStylesLock.Leave; end; - if LThemeClass <> '' then + if LThemeClass<>'' then begin if SameText(LThemeClass, VSCLASS_TREEVIEW) then begin - case iPartId of - 1: + case iPartId of + 1 : begin if iStateId = 2 then begin - LCanvas := TCanvas.Create; SaveIndex := SaveDC(hdc); try - LCanvas.Handle := hdc; - if pOptions.dwFlags AND DTT_FONTPROP <> 0 then - begin - ZeroMemory(@plf, SizeOf(plf)); - plf.lfHeight := 13; - plf.lfCharSet := DEFAULT_CHARSET; - StrCopy(plf.lfFaceName, 'Tahoma'); - LCanvas.Font.Handle := CreateFontIndirect(plf); - end; LDetails := StyleServices.GetElementDetails(tlListItemNormal); ThemeTextColor := StyleServices.GetStyleFontColor(sfListItemTextNormal); - StyleServices.DrawText(LCanvas.Handle, LDetails, string(pszText), pRect^, TTextFormatFlags(dwTextFlags), - ThemeTextColor); + if pOptions.dwFlags AND DTT_FONTPROP <> 0 then + begin + LCanvas := TCanvas.Create; + try + LCanvas.Handle := hdc; + ZeroMemory(@plf, SizeOf(plf)); + plf.lfHeight := 13; + plf.lfCharSet := DEFAULT_CHARSET; + StrCopy(plf.lfFaceName, 'Tahoma'); + LCanvas.Font.Handle := CreateFontIndirect(plf); + StyleServices.DrawText(LCanvas.Handle, LDetails, string(pszText), pRect^, TTextFormatFlags(dwTextFlags), ThemeTextColor); + finally + if pOptions.dwFlags AND DTT_FONTPROP <> 0 then + DeleteObject(LCanvas.Font.Handle); + LCanvas.Handle := 0; + LCanvas.Free; + end; + end + else + StyleServices.DrawText(hdc, LDetails, string(pszText), pRect^, TTextFormatFlags(dwTextFlags), ThemeTextColor); finally - if pOptions.dwFlags AND DTT_FONTPROP <> 0 then - DeleteObject(LCanvas.Font.Handle); - LCanvas.Handle := 0; - LCanvas.Free; RestoreDC(hdc, SaveIndex); end; - Result := S_OK; end else begin - // OutputDebugString(PChar(Format('Detour_UxTheme_DrawThemeTextEx hTheme %d iPartId %d iStateId %d text %s', [hTheme, iPartId, iStateId, pszText]))); - exit(Trampoline_UxTheme_DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, - pRect, pOptions)); + //OutputDebugString(PChar(Format('Detour_UxTheme_DrawThemeTextEx hTheme %d iPartId %d iStateId %d text %s', [hTheme, iPartId, iStateId, pszText]))); + Exit(Trampoline_UxTheme_DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, pRect, pOptions)); end; end; - else - begin - // OutputDebugString(PChar(Format('Detour_UxTheme_DrawThemeTextEx hTheme %d iPartId %d iStateId %d text %s', [hTheme, iPartId, iStateId, pszText]))); - exit(Trampoline_UxTheme_DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, pRect, - pOptions)); + else + begin + //OutputDebugString(PChar(Format('Detour_UxTheme_DrawThemeTextEx hTheme %d iPartId %d iStateId %d text %s', [hTheme, iPartId, iStateId, pszText]))); + Exit(Trampoline_UxTheme_DrawThemeTextEx(hTheme, hdc, iPartId, iStateId, pszText, cchText, dwTextFlags, pRect, pOptions)); + end; end; - end; end -{$IFDEF HOOK_ListView} + {$IFDEF HOOK_ListView} else if SameText(LThemeClass, VSCLASS_LISTVIEW) or SameText(LThemeClass, VSCLASS_ITEMSVIEW_LISTVIEW) then begin case iPartId of diff --git a/Packages/VisualStyles.dpk b/Packages/VisualStyles.dpk index ffc5f12..4003802 100644 --- a/Packages/VisualStyles.dpk +++ b/Packages/VisualStyles.dpk @@ -1,6 +1,9 @@ package VisualStyles; + + {$R *.res} + {$IFDEF IMPLICITBUILDING This IFDEF should not be used by users} {$ALIGN 8} {$ASSERTIONS ON} @@ -28,6 +31,7 @@ package VisualStyles; {$IMPLICITBUILD OFF} requires + rtl, vcl, vclimg, @@ -35,6 +39,7 @@ requires Detours; contains + Vcl.Styles.Ext in '..\Common\Vcl.Styles.Ext.pas', Vcl.Styles.Hooks in '..\Common\Vcl.Styles.Hooks.pas', Vcl.Styles.Utils.ComCtrls in '..\Common\Vcl.Styles.Utils.ComCtrls.pas', @@ -49,7 +54,7 @@ contains Vcl.Styles.Preview in '..\Common\Vcl.Styles.Preview.pas', Vcl.Styles.Register in '..\Common\Vcl.Styles.Register.pas', Vcl.Styles.FontAwesome in '..\Common\Vcl.Styles.FontAwesome.pas', - Vcl.Styles.MiscFunctions in '..\Common\Vcl.Styles.MiscFunctions.pas', - Vcl.Styles.DPIAware in '..\Common\Vcl.Styles.DPIAware.pas'; + Vcl.Styles.DPIAware in '..\Common\Vcl.Styles.DPIAware.pas', + Vcl.Styles.Utils.Misc in '..\Common\Vcl.Styles.Utils.Misc.pas'; end. diff --git a/Packages/VisualStyles.dproj b/Packages/VisualStyles.dproj index 6eaa9f0..2bd50fc 100644 --- a/Packages/VisualStyles.dproj +++ b/Packages/VisualStyles.dproj @@ -5,7 +5,7 @@ 18.5 VCL True - Release + Debug Win32 1 Package @@ -107,8 +107,8 @@ - + Cfg_2 Base