Browse Source

Updating DPI scaling to BCB11

– Control's Canvas does not have Font updated when painting, so it needs to be set explicitly in some places
– Page control drawing was not only incompatible, but broken in general, so it was refactored – giving up on trying to make it 100% mimic system drawing
– CM_DPICHANGED replaced with OnAfterMonitorDpiChanged for form-level scaling. Not really necessary, just for style. Though it is not called for initial resize (at least when starting on primary monitor with temporary non-system DPI)
– OnResize is not called for DPI change, so in some instances, some OnResize call was duplicated in OnAfterMonitorDpiChanged/CM_DPICHANGED
– Scaling of thirdparty box still does not work correctly (but Updates box works??)
– Scaling on primary monitor with temporary non-system DPI is bad
– TTreeView enabled DPI scaling when using Images that is not Scaled
– Discaring obsolete FSiteButtonsPadding workaround
– Controls programmatically added to form are automatically scaled to the form's DPI (relevant for message boxes)

Source commit: a86dddbfada30a5bc409687489bdcd12842050af
Martin Prikryl 11 months ago
parent
commit
1bd51d52d1
43 changed files with 171 additions and 410 deletions
  1. 3 1
      source/WinSCP.cpp
  2. 31 57
      source/components/ThemePageControl.cpp
  3. 0 1
      source/components/ThemePageControl.h
  4. 2 1
      source/core/Global.h
  5. 4 12
      source/forms/About.cpp
  6. 1 0
      source/forms/About.dfm
  7. 1 3
      source/forms/About.h
  8. 3 4
      source/forms/Authenticate.cpp
  9. 1 0
      source/forms/Authenticate.dfm
  10. 1 1
      source/forms/Authenticate.h
  11. 3 8
      source/forms/Copy.cpp
  12. 1 0
      source/forms/Copy.dfm
  13. 1 1
      source/forms/Copy.h
  14. 3 8
      source/forms/CopyLocal.cpp
  15. 1 0
      source/forms/CopyLocal.dfm
  16. 1 3
      source/forms/CopyLocal.h
  17. 18 15
      source/forms/CustomScpExplorer.cpp
  18. 1 0
      source/forms/CustomScpExplorer.dfm
  19. 2 2
      source/forms/CustomScpExplorer.h
  20. 2 6
      source/forms/FileFind.cpp
  21. 1 0
      source/forms/FileFind.dfm
  22. 1 1
      source/forms/FileFind.h
  23. 1 0
      source/forms/LocationProfiles.cpp
  24. 0 1
      source/forms/LocationProfiles.dfm
  25. 7 25
      source/forms/Login.cpp
  26. 1 1
      source/forms/Login.dfm
  27. 1 3
      source/forms/Login.h
  28. 12 27
      source/forms/MessageDlg.cpp
  29. 1 0
      source/forms/MessageDlg.dfm
  30. 1 2
      source/forms/MessageDlg.h
  31. 6 7
      source/forms/Preferences.cpp
  32. 1 0
      source/forms/Preferences.dfm
  33. 2 1
      source/forms/Preferences.h
  34. 3 6
      source/forms/SynchronizeChecklist.cpp
  35. 1 0
      source/forms/SynchronizeChecklist.dfm
  36. 1 1
      source/forms/SynchronizeChecklist.h
  37. 1 0
      source/packages/png/PngImageList.pas
  38. 9 1
      source/windows/GUITools.cpp
  39. 1 0
      source/windows/GUITools.h
  40. 34 161
      source/windows/VCLCommon.cpp
  41. 0 1
      source/windows/VCLCommon.h
  42. 0 3
      source/windows/WinApi.h
  43. 6 46
      source/windows/WinMain.cpp

+ 3 - 1
source/WinSCP.cpp

@@ -63,7 +63,9 @@ WINAPI wWinMain(HINSTANCE, HINSTANCE, LPWSTR, int)
     Application->MainFormOnTaskBar = true;
     Application->ModalPopupMode = pmAuto;
     Application->DefaultFont->Name = L"Tahoma";
-    Application->DefaultFont->Height = -11;
+    // The default DefaultFont is calculated as -MulDiv(8, ScreenLogPixels, 72)
+    // But form's font is scaled using DPI, what can resumt in slightly different Height
+    Application->DefaultFont->Height = -MulDiv(11, Screen->PixelsPerInch, Screen->DefaultPixelsPerInch);
     SetEnvironmentVariable(L"WINSCP_PATH",
       ExcludeTrailingBackslash(ExtractFilePath(Application->ExeName)).c_str());
     CoreInitialize();

+ 31 - 57
source/components/ThemePageControl.cpp

@@ -124,9 +124,11 @@ void TThemeTabSheet::UpdateCaption()
 
   if (UseThemes() && (Button != ttbNone))
   {
+    ParentPageControl->Canvas->Font = ParentPageControl->Font;
     int OrigWidth = ParentPageControl->Canvas->TextWidth(ACaption);
     int TabButtonWidth = ParentPageControl->TabButtonSize();
-    while (ParentPageControl->Canvas->TextWidth(ACaption) < OrigWidth + TabButtonWidth)
+    int Padding = ScaleByTextHeight(this, 2);
+    while (ParentPageControl->Canvas->TextWidth(ACaption) < OrigWidth + Padding + TabButtonWidth)
     {
       ACaption += L" ";
     }
@@ -222,6 +224,7 @@ void __fastcall TThemePageControl::PaintWindow(HDC DC)
 
   std::unique_ptr<TCanvas> ACanvas(new TCanvas());
   ACanvas->Handle = DC;
+  ACanvas->Font = Font;
   FTextHeight = CalculateTextHeight(ACanvas.get());
 
   for (int Tab = 0; Tab < PageCount; Tab++)
@@ -319,20 +322,16 @@ void __fastcall TThemePageControl::ItemTabRect(int Item, TRect & Rect)
 //----------------------------------------------------------------------------------------------------------
 void __fastcall TThemePageControl::ItemContentsRect(int Item, TRect & Rect)
 {
-  bool Selected = (Item == TabIndex);
-
   Rect.Left += 6;
-  Rect.Top += 2;
+  Rect.Right -= 3;
 
-  if (Selected)
+  if (Item == TabIndex)
   {
+    // to counter the ItemTabRect
+    Rect.Left += 2;
+    Rect.Right -= 2;
+    Rect.Bottom++;
     Rect.Bottom -= 2;
-    Rect.Top += 1;
-  }
-  else
-  {
-    Rect.Bottom += 2;
-    Rect.Top += 3;
   }
 }
 //----------------------------------------------------------------------------------------------------------
@@ -341,26 +340,6 @@ bool __fastcall TThemePageControl::HasItemImage(int Item)
   return (Images != NULL) && (Pages[Item]->ImageIndex >= 0);
 }
 //----------------------------------------------------------------------------------------------------------
-void __fastcall TThemePageControl::ItemTextRect(int Item, TRect & Rect)
-{
-  if (HasItemImage(Item))
-  {
-    Rect.Left += Images->Width + 3;
-  }
-  else
-  {
-    Rect.Left -= 2;
-  }
-  Rect.Right -= 3;
-  // Shouldn't get here until the control has (started) painted
-  if (DebugAlwaysTrue(FTextHeight > 0))
-  {
-    int AlignOffset = ((Rect.Height() - FTextHeight) / 2) - ScaleByTextHeight(this, 4) + 2;
-    Rect.Top += AlignOffset;
-  }
-  OffsetRect(&Rect, 0, ((Item == TabIndex) ? 0 : -2));
-}
-//----------------------------------------------------------------------------------------------------------
 void TThemePageControl::DrawCross(HDC DC, int Width, COLORREF Color, const TRect & Rect)
 {
   HPEN Pen = CreatePen(PS_SOLID, Width, Color);
@@ -394,6 +373,12 @@ void TThemePageControl::DrawDropDown(HDC DC, int Radius, int X, int Y, COLORREF
   DeleteObject(Pen);
 }
 //----------------------------------------------------------------------------------------------------------
+static int VCenter(const TRect & Rect, int Height)
+{
+  int A = (Rect.Top + Rect.Bottom - Height);
+  return (A / 2) + (A % 2);
+}
+//----------------------------------------------------------------------------------------------------------
 // Draw tab item context: possible icon and text
 void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, int State, bool Shadowed, TTBXTheme * ATabTheme)
 {
@@ -401,29 +386,29 @@ void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, int
   ItemContentsRect(Item, Rect);
 
   UnicodeString Text = Pages[Item]->Caption;
-  bool Selected = (State == TIS_SELECTED);
+
+  std::unique_ptr<TCanvas> Canvas(new TCanvas());
+  Canvas->Handle = DC;
 
   if (HasItemImage(Item))
   {
     int Left;
     if (!Text.IsEmpty())
     {
-      Left = Rect.Left + (Selected ? 2 : 0);
+      Left = Rect.Left;
+      Rect.Left += Images->Width + 3;
     }
     else
     {
       Left = OrigRect.Left + (OrigRect.Right - Images->Width - OrigRect.Left) / 2;
     }
-    int Y = ((Rect.Top + Rect.Bottom - Images->Height) / 2) - 1 + (Selected ? 0 : -2);
-    std::unique_ptr<TCanvas> Canvas(new TCanvas());
-    Canvas->Handle = DC;
-    Images->Draw(Canvas.get(), Left, Y, Pages[Item]->ImageIndex, !Shadowed);
+    int Top = VCenter(Rect, Images->Height);
+    Images->Draw(Canvas.get(), Left, Top, Pages[Item]->ImageIndex, !Shadowed);
   }
 
   int OldMode = SetBkMode(DC, TRANSPARENT);
   if (!Text.IsEmpty())
   {
-    ItemTextRect(Item, Rect);
     if (ATabTheme != NULL)
     {
       SetTextColor(DC, ATabTheme->GetItemTextColor(GetItemInfo(State)));
@@ -431,10 +416,12 @@ void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, int
     HFONT OldFont = (HFONT)SelectObject(DC, Font->Handle);
     wchar_t * Buf = new wchar_t[Text.Length() + 1 + 4];
     wcscpy(Buf, Text.c_str());
-    TRect TextRect(0, 0, Rect.Right - Rect.Left, 20);
+    TRect TextRect(0, 0, Rect.Width(), 20);
     // Truncates too long texts with ellipsis
     ::DrawText(DC, Buf, -1, &TextRect, DT_CALCRECT | DT_SINGLELINE | DT_MODIFYSTRING | DT_END_ELLIPSIS);
+    DebugAssert(FTextHeight == TextRect.Height());
 
+    Rect.Top = VCenter(Rect, FTextHeight);
     DrawText(DC, Buf, -1, &Rect, DT_NOPREFIX | DT_CENTER);
     delete[] Buf;
 
@@ -449,9 +436,7 @@ void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, int
       {
         ButtonItemInfo.HoverKind = hkMouseHover;
 
-        std::unique_ptr<TCanvas> CanvasMem(new TCanvas());
-        CanvasMem->Handle = DC;
-        CurrentTheme->PaintFrame(CanvasMem.get(), Rect, ButtonItemInfo);
+        CurrentTheme->PaintFrame(Canvas.get(), Rect, ButtonItemInfo);
       }
 
       COLORREF BackColor = GetPixel(DC, Rect.Left + (Rect.Width() / 2), Rect.Top + (Rect.Height() / 2));
@@ -498,12 +483,12 @@ void __fastcall TThemePageControl::DrawTabItem(HDC DC, int Item, TRect Rect, int
 //----------------------------------------------------------------------------------------------------------
 int __fastcall TThemePageControl::TabButtonSize()
 {
-  return ScaleByTextHeight(this, 16);
+  return MulDiv(GetTabsHeight(), 8, 13);
 }
 //----------------------------------------------------------------------------------------------------------
 int __fastcall TThemePageControl::GetCrossPadding()
 {
-  return ScaleByTextHeight(this, 4);
+  return MulDiv(GetTabsHeight(), 2, 13);
 }
 //----------------------------------------------------------------------------------------------------------
 TRect __fastcall TThemePageControl::TabButtonRect(int Index)
@@ -511,23 +496,11 @@ TRect __fastcall TThemePageControl::TabButtonRect(int Index)
   TRect Rect = TabRect(Index);
   ItemTabRect(Index, Rect);
   ItemContentsRect(Index, Rect);
-  ItemTextRect(Index, Rect);
 
   int ATabButtonSize = TabButtonSize();
-  int CrossPadding = GetCrossPadding();
 
-  TEXTMETRIC TextMetric;
-  Canvas->Font = Font;
-  GetTextMetrics(Canvas->Handle, &TextMetric);
-
-  // TextMetric.tmAscent is approx the same thing as FTextHeight
-  Rect.Top += TextMetric.tmAscent - ATabButtonSize + CrossPadding;
+  Rect.Top = VCenter(Rect, ATabButtonSize);
   Rect.Left = Rect.Right - ATabButtonSize - ScaleByTextHeight(this, 1);
-  if (Index == TabIndex)
-  {
-    // To counter Inflate(2, 2) in ItemTabRect
-    Rect.Left -= 2;
-  }
   Rect.Right = Rect.Left + ATabButtonSize;
   Rect.Bottom = Rect.Top + ATabButtonSize;
   return Rect;
@@ -721,6 +694,7 @@ void TThemePageControl::UpdateTabsCaptionTruncation()
 
     int TabsWidth = TotalTabsWidth();
     int MaxWidth = ClientWidth - ScaleByTextHeight(this, 8); // arbitrary margin to avoid left/right buttons flicker
+    Canvas->Font = Font;
     if (TabsWidth > MaxWidth)
     {
       int NeedWidth = (TabsWidth - MaxWidth);

+ 0 - 1
source/components/ThemePageControl.h

@@ -87,7 +87,6 @@ private:
   int __fastcall IndexOfTabButtonAt(int X, int Y);
   void __fastcall ItemContentsRect(int Item, TRect & Rect);
   bool __fastcall HasItemImage(int Item);
-  void __fastcall ItemTextRect(int Item, TRect & Rect);
   void __fastcall ItemTabRect(int Item, TRect & Rect);
   TThemeTabSheetButtons __fastcall GetTabButton(int Index);
   void UpdateHotButton(int & Ref, int Index);

+ 2 - 1
source/core/Global.h

@@ -57,7 +57,8 @@ void __fastcall DoAssert(wchar_t * Message, wchar_t * Filename, int LineNumber);
 #define DebugNotNull(p) (p)
 #define TraceInitPtr(p) (p)
 #define TraceInitStr(p) (p)
-#define DebugUsedParam(p) ((&p) == (&p))
+#define DebugUsedParam2(p1, p2) ((&p1) == (&p2))
+#define DebugUsedParam(p) DebugUsedParam2(p, p)
 #define DebugUsedArg(p)
 //---------------------------------------------------------------------------
 #endif

+ 4 - 12
source/forms/About.cpp

@@ -380,20 +380,12 @@ void __fastcall TAboutDialog::IconPaintBoxPaint(TObject * /*Sender*/)
     0, 0, FIconHandle, IconPaintBox->Width, IconPaintBox->Height, 0, NULL, DI_NORMAL);
 }
 //---------------------------------------------------------------------------
-void __fastcall TAboutDialog::Dispatch(void * Message)
+void __fastcall TAboutDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TMessage * M = reinterpret_cast<TMessage*>(Message);
-  if (M->Msg == CM_DPICHANGED)
+  DebugUsedParam2(OldDPI, NewDPI);
+  if (FThirdPartyWebBrowser != NULL)
   {
-    if (FThirdPartyWebBrowser != NULL)
-    {
-      DoLoadThirdParty();
-    }
-    TForm::Dispatch(Message);
-  }
-  else
-  {
-    TForm::Dispatch(Message);
+    DoLoadThirdParty();
   }
 }
 //---------------------------------------------------------------------------

+ 1 - 0
source/forms/About.dfm

@@ -16,6 +16,7 @@ object AboutDialog: TAboutDialog
   Font.Style = []
   KeyPreview = True
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnKeyDown = FormKeyDown
   DesignSize = (
     410

+ 1 - 3
source/forms/About.h

@@ -40,6 +40,7 @@ __published:
           int X, int Y);
   void __fastcall IconPaintBoxPaint(TObject *Sender);
   void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 private:
   TConfiguration * FConfiguration;
   TNotifyEvent FOnRegistrationLink;
@@ -56,9 +57,6 @@ private:
   void __fastcall DoLoadThirdParty();
   void __fastcall ShiftControls(int From, int Diff);
 
-protected:
-  virtual void __fastcall Dispatch(void * Message);
-
 public:
   virtual __fastcall TAboutDialog(TComponent * AOwner,
     TConfiguration * Configuration, bool AllowLicense, TRegistration * Registration,

+ 3 - 4
source/forms/Authenticate.cpp

@@ -623,12 +623,11 @@ void __fastcall TAuthenticateForm::FormResize(TObject * /*Sender*/)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TAuthenticateForm::ChangeScale(int M, int D, bool isDpiChange)
+void __fastcall TAuthenticateForm::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TForm::ChangeScale(M, D, isDpiChange);
-
+  DebugUsedParam2(OldDPI, NewDPI);
   // Recreate the list to re-measure the items according to the new font
-  if (DebugAlwaysTrue(LogView->HandleAllocated()) &&
+  if (LogView->HandleAllocated() &&
       (LogView->Items->Count > 0))
   {
     std::unique_ptr<TStrings> Items(new TStringList());

+ 1 - 0
source/forms/Authenticate.dfm

@@ -17,6 +17,7 @@ object AuthenticateForm: TAuthenticateForm
   Font.Name = 'Tahoma'
   Font.Style = []
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnResize = FormResize
   OnShow = FormShow
   TextHeight = 13

+ 1 - 1
source/forms/Authenticate.h

@@ -68,6 +68,7 @@ __published:
   void __fastcall LogViewMouseMove(TObject *Sender, TShiftState Shift, int X, int Y);
   void __fastcall LabelCopyActionExecute(TObject *Sender);
   void __fastcall LabelOpenLinkAction2Execute(TObject *Sender);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 public:
   __fastcall TAuthenticateForm(TComponent * Owner);
@@ -101,7 +102,6 @@ protected:
   int __fastcall LogItemHeight(int Index);
   void __fastcall RedrawLog();
   void __fastcall CMShowingChanged(TMessage & Message);
-  HIDESBASE DYNAMIC void __fastcall ChangeScale(int M, int D, bool isDpiChange);
   void __fastcall UpdateBannerFont();
   void __fastcall DoAdjustWindow();
   void __fastcall LabelContextPopup(TObject* Sender, const TPoint & MousePos, bool & Handled);

+ 3 - 8
source/forms/Copy.cpp

@@ -630,14 +630,9 @@ void __fastcall TCopyDialog::OkButtonDropDownClick(TObject *)
   MenuPopup(OkMenu, OkButton);
 }
 //---------------------------------------------------------------------------
-void __fastcall TCopyDialog::Dispatch(void * Message)
+void __fastcall TCopyDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TMessage * M = reinterpret_cast<TMessage*>(Message);
-  if (M->Msg == CM_DPICHANGED)
-  {
-    AutoSizeCheckBox(NeverShowAgainCheck);
-  }
-
-  TForm::Dispatch(Message);
+  DebugUsedParam2(OldDPI, NewDPI);
+  AutoSizeCheckBox(NeverShowAgainCheck);
 }
 //---------------------------------------------------------------------------

+ 1 - 0
source/forms/Copy.dfm

@@ -15,6 +15,7 @@ object CopyDialog: TCopyDialog
   Font.Name = 'Tahoma'
   Font.Style = []
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow
   DesignSize = (

+ 1 - 1
source/forms/Copy.h

@@ -52,6 +52,7 @@ __published:
   void __fastcall DownloadItemClick(TObject *Sender);
   void __fastcall BrowseItemClick(TObject *Sender);
   void __fastcall OkButtonDropDownClick(TObject *Sender);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 private:
   bool FDefaultToRemote;
   bool FToRemote;
@@ -84,7 +85,6 @@ protected:
   bool __fastcall RemotePaths();
   void __fastcall CopyParamListPopup(TRect R, int AdditionalOptions);
   int __fastcall ActualCopyParamAttrs();
-  virtual void __fastcall Dispatch(void * Message);
 
   INTERFACE_HOOK;
 

+ 3 - 8
source/forms/CopyLocal.cpp

@@ -156,14 +156,9 @@ void __fastcall TCopyLocalDialog::LocalDirectoryBrowseButtonClick(TObject *)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TCopyLocalDialog::Dispatch(void * Message)
+void __fastcall TCopyLocalDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TMessage * M = reinterpret_cast<TMessage*>(Message);
-  if (M->Msg == CM_DPICHANGED)
-  {
-    AutoSizeCheckBox(NeverShowAgainCheck);
-  }
-
-  TForm::Dispatch(Message);
+  DebugUsedParam2(OldDPI, NewDPI);
+  AutoSizeCheckBox(NeverShowAgainCheck);
 }
 //---------------------------------------------------------------------------

+ 1 - 0
source/forms/CopyLocal.dfm

@@ -15,6 +15,7 @@ object CopyLocalDialog: TCopyLocalDialog
   Font.Name = 'Tahoma'
   Font.Style = []
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow
   DesignSize = (

+ 1 - 3
source/forms/CopyLocal.h

@@ -29,6 +29,7 @@ __published:
   void __fastcall FormCloseQuery(TObject *Sender, bool &CanClose);
   void __fastcall HelpButtonClick(TObject *Sender);
   void __fastcall LocalDirectoryBrowseButtonClick(TObject *Sender);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 private:
   int FOptions;
@@ -41,9 +42,6 @@ private:
 
   INTERFACE_HOOK;
 
-protected:
-  virtual void __fastcall Dispatch(void * Message);
-
 public:
   TCopyLocalDialog(TComponent * Owner, bool Move, int Options);
 

+ 18 - 15
source/forms/CustomScpExplorer.cpp

@@ -9922,10 +9922,6 @@ void __fastcall TCustomScpExplorerForm::Dispatch(void * Message)
       WMDpiChanged(*M);
       break;
 
-    case CM_DPICHANGED:
-      CMDpiChanged(*M);
-      break;
-
     case WM_SETTINGCHANGE:
       WMSettingChange(*M);
       break;
@@ -9975,12 +9971,6 @@ void TCustomScpExplorerForm::RegenerateSessionColorsImageList()
   UpdateImages();
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::CMDpiChanged(TMessage & Message)
-{
-  TForm::Dispatch(&Message);
-  RegenerateSessionColorsImageList();
-}
-//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::WMDpiChanged(TMessage & Message)
 {
   TForm::Dispatch(&Message);
@@ -11168,11 +11158,19 @@ void __fastcall TCustomScpExplorerForm::PrivateKeyUpload()
   TTerminalManager::Instance()->UploadPublicKey(Terminal, NULL, FileName);
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::ChangeScale(int M, int D, bool isDpiChange)
+void __fastcall TCustomScpExplorerForm::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TForm::ChangeScale(M, D, isDpiChange);
-  int APixelsPerInch = GetControlPixelsPerInch(this);
-  GlyphsModule->PixelsPerInch = APixelsPerInch;
+  DebugUsedParam(OldDPI);
+  DebugAssert(NewDPI == GetControlPixelsPerInch(this));
+  GlyphsModule->PixelsPerInch = NewDPI;
+  // Can be called too early when primary monitor has non-system dpi
+  if (FSessionColors != NULL)
+  {
+    RegenerateSessionColorsImageList();
+  }
+  UpdateTabsSize();
+  UpdateSessionsPageControlHeight();
+  QueueFileListColumnAutoSize();
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::DockContextPopup(TObject * Sender, TPoint & MousePos, bool & /*Handled*/)
@@ -11971,7 +11969,7 @@ void TCustomScpExplorerForm::LocalLocalCopyCommand(TFileOperation Operation, TOp
   LocalLocalCopy(Operation, Side, OnFocused, !WinConfiguration->ConfirmTransferring, false, Flags);
 }
 //---------------------------------------------------------------------------
-void __fastcall TCustomScpExplorerForm::SessionsPageControlResize(TObject *)
+void TCustomScpExplorerForm::UpdateTabsSize()
 {
   // 1) Changing tab list triggers the OnResize.
   // 2) Is called in TForm constructor, when we are not ready to call SessionListChanged (particularly when starting with colored session)
@@ -11982,6 +11980,11 @@ void __fastcall TCustomScpExplorerForm::SessionsPageControlResize(TObject *)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::SessionsPageControlResize(TObject *)
+{
+  UpdateTabsSize();
+}
+//---------------------------------------------------------------------------
 UnicodeString TCustomScpExplorerForm::GetSessionPath(TManagedTerminal * ASession, TOperationSide Side)
 {
   if (ASession == ManagedSession)

+ 1 - 0
source/forms/CustomScpExplorer.dfm

@@ -11,6 +11,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
   Font.Name = 'Tahoma'
   Font.Style = []
   KeyPreview = True
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnClose = FormClose
   OnCloseQuery = FormCloseQuery
   OnConstrainedResize = FormConstrainedResize

+ 2 - 2
source/forms/CustomScpExplorer.h

@@ -229,6 +229,7 @@ __published:
     TUnixDirView * Sender, TListItem * Item, TRemoteFile * File, const TSize & Size, TBitmap *& Bitmap);
   void __fastcall RemoteDirViewStartLoading(TObject *Sender);
   void __fastcall RemoteDirViewStartReading(TObject *Sender);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 private:
   TManagedTerminal * FManagedSession;
@@ -363,12 +364,12 @@ private:
   void __fastcall CreateHiddenWindow();
   bool __fastcall IsQueueAutoPopup();
   void __fastcall UpdateSessionsPageControlHeight();
+  void UpdateTabsSize();
   TDragDropFilesEx * __fastcall CreateDragDropFilesEx();
   void __fastcall KeyProcessed(Word & Key, TShiftState Shift);
   void __fastcall CheckCustomCommandShortCut(TCustomCommandList * List, Word & Key, Classes::TShiftState Shift, TShortCut KeyShortCut);
   void __fastcall CMShowingChanged(TMessage & Message);
   void __fastcall WMClose(TMessage & Message);
-  void __fastcall CMDpiChanged(TMessage & Message);
   void __fastcall WMDpiChanged(TMessage & Message);
   void __fastcall DoBookmarkClick(TOperationSide Side, TObject * Sender);
   void __fastcall LocalBookmarkClick(TObject * Sender);
@@ -705,7 +706,6 @@ protected:
   bool __fastcall SelectedAllFilesInDirView(TCustomDirView * DView);
   TSessionData * __fastcall SessionDataForCode();
   void __fastcall RefreshPanel(const UnicodeString & Session, const UnicodeString & Path);
-  HIDESBASE DYNAMIC void __fastcall ChangeScale(int M, int D, bool isDpiChange);
   virtual void __fastcall UpdateImages();
   void __fastcall UpdatePixelsPerInchMainWindowCounter();
   void __fastcall CopyPopup(TControl * DestControl, TControl * SourceControl);

+ 2 - 6
source/forms/FileFind.cpp

@@ -465,19 +465,15 @@ void __fastcall TFileFindDialog::Dispatch(void * Message)
   {
     CMDialogKey(*((TWMKeyDown *)Message));
   }
-  else if (M->Msg == CM_DPICHANGED)
-  {
-    CMDpiChanged(*M);
-  }
   else
   {
     TForm::Dispatch(Message);
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TFileFindDialog::CMDpiChanged(TMessage & Message)
+void __fastcall TFileFindDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TForm::Dispatch(&Message);
+  DebugUsedParam2(OldDPI, NewDPI);
   UpdateImages();
 }
 //---------------------------------------------------------------------------

+ 1 - 0
source/forms/FileFind.dfm

@@ -1362,6 +1362,7 @@ object FileFindDialog: TFileFindDialog
     0000C0030000C0030000C0030000C0030000C0070000C00F0000FFFF0000}
   KeyPreview = True
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnClose = FormClose
   OnCloseQuery = FormCloseQuery
   OnKeyDown = FormKeyDown

+ 1 - 1
source/forms/FileFind.h

@@ -81,6 +81,7 @@ __published:
   void __fastcall DownloadActionExecute(TObject *Sender);
   void __fastcall EditActionExecute(TObject *Sender);
   void __fastcall FileViewCompare(TObject *Sender, TListItem *Item1, TListItem *Item2, int Data, int &Compare);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 public:
   __fastcall TFileFindDialog(TComponent * Owner);
@@ -131,7 +132,6 @@ private:
   void __fastcall FocusFile();
   void __fastcall DoFocusFile(const UnicodeString & Path);
   void __fastcall CMDialogKey(TWMKeyDown & Message);
-  void __fastcall CMDpiChanged(TMessage & Message);
   void __fastcall ClearItem(TListItem * Item);
   void __fastcall FileDeleteFinished(TOperationSide Side, const UnicodeString & FileName, bool Success);
   void __fastcall FileDownloadFinished(TOperationSide Side, const UnicodeString & FileName, bool Success);

+ 1 - 0
source/forms/LocationProfiles.cpp

@@ -198,6 +198,7 @@ __fastcall TLocationProfilesDialog::TLocationProfilesDialog(TComponent * AOwner)
 
   UseSystemSettings(this);
   SelectScaledImageList(BookmarkImageList);
+  SessionProfilesView->Images = TreeViewImageList(BookmarkImageList);
   LoadDialogImage(Image, L"Open folder");
 }
 //---------------------------------------------------------------------

+ 0 - 1
source/forms/LocationProfiles.dfm

@@ -89,7 +89,6 @@ object LocationProfilesDialog: TLocationProfilesDialog
         DoubleBuffered = True
         DragMode = dmAutomatic
         HideSelection = False
-        Images = BookmarkImageList
         Indent = 19
         ParentDoubleBuffered = False
         TabOrder = 0

+ 7 - 25
source/forms/Login.cpp

@@ -90,8 +90,8 @@ __fastcall TLoginDialog::TLoginDialog(TComponent* AOwner)
   FUserNameLabel = UserNameLabel->Caption;
   FPasswordLabel = PasswordLabel->Caption;
 
-  FSiteButtonsPadding = SitesPanel->ClientHeight - ToolsMenuButton->Top - ToolsMenuButton->Height;
   AutoSizeCheckBox(ShowAgainCheck);
+  SessionTree->Images = TreeViewImageList(SessionImageList);
 }
 //---------------------------------------------------------------------
 __fastcall TLoginDialog::~TLoginDialog()
@@ -790,13 +790,6 @@ void __fastcall TLoginDialog::FormShow(TObject * /*Sender*/)
     Init();
   }
 
-  // WORKAROUND for a bug in the VCL layout code for bottom aligned controls
-  // This is probably no longer needed after ComponentsPanel was removed
-  int Offset = (SitesPanel->ClientHeight - FSiteButtonsPadding - ToolsMenuButton->Height) - ToolsMenuButton->Top;
-  ToolsMenuButton->Top = ToolsMenuButton->Top + Offset;
-  ManageButton->Top = ManageButton->Top + Offset;
-  SessionTree->Height = SessionTree->Height + Offset;
-
   // Bit of a hack: Assume an auto open, when we are linked to the main form
   ShowAgainPanel->Visible = (FLinkedForm != NULL);
 
@@ -1734,14 +1727,6 @@ void __fastcall TLoginDialog::CMVisibleChanged(TMessage & Message)
   TForm::Dispatch(&Message);
 }
 //---------------------------------------------------------------------------
-void __fastcall TLoginDialog::CMDpiChanged(TMessage & Message)
-{
-  TForm::Dispatch(&Message);
-  GenerateImages();
-  CenterButtonImage(LoginButton);
-  AutoSizeCheckBox(ShowAgainCheck);
-}
-//---------------------------------------------------------------------------
 void __fastcall TLoginDialog::Dispatch(void * Message)
 {
   TMessage * M = reinterpret_cast<TMessage*>(Message);
@@ -1788,10 +1773,6 @@ void __fastcall TLoginDialog::Dispatch(void * Message)
   {
     CMVisibleChanged(*M);
   }
-  else if (M->Msg == CM_DPICHANGED)
-  {
-    CMDpiChanged(*M);
-  }
   else
   {
     TForm::Dispatch(Message);
@@ -3362,12 +3343,13 @@ void __fastcall TLoginDialog::SearchSiteActionExecute(TObject * /*Sender*/)
   SetSiteSearch(isAll);
 }
 //---------------------------------------------------------------------------
-void __fastcall TLoginDialog::ChangeScale(int M, int D, bool isDpiChange)
+void __fastcall TLoginDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TForm::ChangeScale(M, D, isDpiChange);
-  FSiteButtonsPadding = MulDiv(FSiteButtonsPadding, M, D);
-  FBasicGroupBaseHeight = MulDiv(FBasicGroupBaseHeight, M, D);
-  FNoteGroupOffset = MulDiv(FNoteGroupOffset, M, D);
+  FBasicGroupBaseHeight = MulDiv(FBasicGroupBaseHeight, NewDPI, OldDPI);
+  FNoteGroupOffset = MulDiv(FNoteGroupOffset, NewDPI, OldDPI);
+  GenerateImages();
+  CenterButtonImage(LoginButton);
+  AutoSizeCheckBox(ShowAgainCheck);
 }
 //---------------------------------------------------------------------------
 void __fastcall TLoginDialog::PanelMouseDown(TObject *, TMouseButton, TShiftState, int, int)

+ 1 - 1
source/forms/Login.dfm

@@ -17,6 +17,7 @@ object LoginDialog: TLoginDialog
   Font.Style = []
   KeyPreview = True
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery
   OnShow = FormShow
   TextHeight = 13
@@ -460,7 +461,6 @@ object LoginDialog: TLoginDialog
       DoubleBuffered = True
       DragMode = dmAutomatic
       HideSelection = False
-      Images = SessionImageList
       Indent = 19
       ParentDoubleBuffered = False
       ParentShowHint = False

+ 1 - 3
source/forms/Login.h

@@ -308,6 +308,7 @@ __published:
   void __fastcall ShowAgainCheckClick(TObject *Sender);
   void __fastcall SearchSiteStartActionExecute(TObject *Sender);
   void __fastcall SitesIncrementalSearchPanelContextPopup(TObject *Sender, TPoint &MousePos, bool &Handled);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 private:
   int NoUpdate;
@@ -335,7 +336,6 @@ private:
   TIncrementalSearch FSiteSearch;
   TForm * FLinkedForm;
   TPoint FPrevPos;
-  int FSiteButtonsPadding;
   UnicodeString FUserNameLabel;
   UnicodeString FPasswordLabel;
   int FFixedSessionImages;
@@ -422,7 +422,6 @@ private:
   TModalResult __fastcall DefaultResult();
   int AddLoginButtonImage(int Index, bool Enabled);
   void __fastcall WMWindowPosChanged(TWMWindowPosChanged & Message);
-  void __fastcall CMDpiChanged(TMessage & Message);
   void __fastcall GenerateImages();
   void __fastcall CMVisibleChanged(TMessage & Message);
   void UpdateS3Credentials();
@@ -445,7 +444,6 @@ protected:
   void __fastcall InitControls();
   void __fastcall EditSession();
   void __fastcall Login();
-  HIDESBASE DYNAMIC void __fastcall ChangeScale(int M, int D, bool isDpiChange);
   void SetSiteSearch(TIncrementalSearch SiteSearch);
   __property TSessionData * SelectedSession  = { read=GetSelectedSession };
 

+ 12 - 27
source/forms/MessageDlg.cpp

@@ -79,14 +79,10 @@ __fastcall TMessageForm::TMessageForm(TComponent * AOwner) : TForm(AOwner)
   NeverAskAgainCheck = NULL;
   FUpdateForShiftStateTimer = NULL;
   UseSystemSettingsPre(this);
-  // DFM-based form, to use the custom Tahoma font
-  FDummyForm = new TCustomDialog(EmptyStr);
-  UseSystemSettings(FDummyForm);
 }
 //---------------------------------------------------------------------------
 __fastcall TMessageForm::~TMessageForm()
 {
-  SAFE_DESTROY(FDummyForm);
   SAFE_DESTROY(FUpdateForShiftStateTimer);
 }
 //---------------------------------------------------------------------------
@@ -358,21 +354,22 @@ void __fastcall TMessageForm::Dispatch(void * Message)
   {
     CMShowingChanged(*M);
   }
-  else if (M->Msg == CM_DPICHANGED)
+  else
   {
-    if (MessageBrowser != NULL)
-    {
-      LoadMessageBrowser();
-    }
-    if (NeverAskAgainCheck != NULL)
-    {
-      AutoSizeCheckBox(NeverAskAgainCheck);
-    }
     TForm::Dispatch(Message);
   }
-  else
+}
+//---------------------------------------------------------------------------
+void __fastcall TMessageForm::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
+{
+  DebugUsedParam2(OldDPI, NewDPI);
+  if (MessageBrowser != NULL)
   {
-    TForm::Dispatch(Message);
+    LoadMessageBrowser();
+  }
+  if (NeverAskAgainCheck != NULL)
+  {
+    AutoSizeCheckBox(NeverAskAgainCheck);
   }
 }
 //---------------------------------------------------------------------------
@@ -597,11 +594,6 @@ TButton * __fastcall TMessageForm::CreateButton(
     Button->Name = Name;
     Button->Parent = this;
     Button->Caption = Caption;
-    // Scale buttons using regular font, so that they are as large as buttons
-    // on other dialogs (note that they are still higher than Windows Task dialog
-    // buttons)
-    Button->Height = ScaleByTextHeightRunTime(FDummyForm, Button->Height);
-    Button->Width = ScaleByTextHeightRunTime(FDummyForm, Button->Width);
 
     if (OnSubmit != NULL)
     {
@@ -679,13 +671,6 @@ void __fastcall TMessageForm::NavigateToUrl(const UnicodeString & Url)
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TMessageForm::ReadState(TReader * Reader)
-{
-  TForm::ReadState(Reader);
-  // Before we change form font
-  RecordFormImplicitRescale(this);
-}
-//---------------------------------------------------------------------------
 void __fastcall AnswerNameAndCaption(
   unsigned int Answer, UnicodeString & Name, UnicodeString & Caption)
 {

+ 1 - 0
source/forms/MessageDlg.dfm

@@ -13,5 +13,6 @@ object MessageForm: TMessageForm
   Font.Name = 'Tahoma'
   Font.Style = []
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   TextHeight = 13
 end

+ 1 - 2
source/forms/MessageDlg.h

@@ -9,6 +9,7 @@
 class TMessageForm : public TForm
 {
 __published:
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 public:
   static TForm * __fastcall Create(const UnicodeString & Msg, TStrings * MoreMessages,
@@ -41,7 +42,6 @@ protected:
   void __fastcall UpdateForShiftStateTimer(TObject * Sender);
   DYNAMIC void __fastcall SetZOrder(bool TopMost);
   void __fastcall LoadMessageBrowser();
-  virtual void __fastcall ReadState(TReader * Reader);
 
 private:
   typedef std::map<unsigned int, TButton *> TAnswerButtons;
@@ -54,7 +54,6 @@ private:
   UnicodeString MessageBrowserUrl;
   TShiftState FShiftState;
   TTimer * FUpdateForShiftStateTimer;
-  TForm * FDummyForm;
   bool FShowNoActivate;
   std::map<TObject *, TButtonSubmitEvent> FButtonSubmitEvents;
   TCheckBox * NeverAskAgainCheck;

+ 6 - 7
source/forms/Preferences.cpp

@@ -155,6 +155,7 @@ __fastcall TPreferencesDialog::TPreferencesDialog(
   }
 
   HideComponentsPanel(this);
+  FInitialized = true;
 }
 //---------------------------------------------------------------------------
 __fastcall TPreferencesDialog::~TPreferencesDialog()
@@ -1268,7 +1269,9 @@ int __fastcall TPreferencesDialog::GetCommandListIndex(TCustomCommandList * List
 //---------------------------------------------------------------------------
 void __fastcall TPreferencesDialog::UpdateControls()
 {
-  if (FNoUpdate == 0)
+  // ControlChange gets called when scaling the form while loading it,
+  // when the primary monitor dpi is different than system dpi
+  if (FInitialized && (FNoUpdate == 0))
   {
     NavigationTree->HideSelection = (FSearchResults.get() != NULL);
     if ((SearchEdit->ButtonWidth != 0) != !SearchEdit->Text.IsEmpty()) // optimization
@@ -2263,12 +2266,12 @@ void __fastcall TPreferencesDialog::WMHelp(TWMHelp & Message)
   TForm::Dispatch(&Message);
 }
 //---------------------------------------------------------------------------
-void __fastcall TPreferencesDialog::CMDpiChanged(TMessage & Message)
+void __fastcall TPreferencesDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
+  DebugUsedParam2(OldDPI, NewDPI);
   // To update font sizes - Note that they get scaled automatically, but as we use our own algorithm,
   // we may end up using a slightly different size, so apply it straight away for consistency
   UpdateControls();
-  TForm::Dispatch(&Message);
 }
 //---------------------------------------------------------------------------
 void TPreferencesDialog::WMActivate(TWMActivate & Message)
@@ -2299,10 +2302,6 @@ void __fastcall TPreferencesDialog::Dispatch(void *Message)
   {
     CMDialogKey(*((TWMKeyDown *)Message));
   }
-  else if (M->Msg == CM_DPICHANGED)
-  {
-    CMDpiChanged(*M);
-  }
   else if (M->Msg == WM_HELP)
   {
     WMHelp(*((TWMHelp *)Message));

+ 1 - 0
source/forms/Preferences.dfm

@@ -15,6 +15,7 @@ object PreferencesDialog: TPreferencesDialog
   Font.Name = 'Tahoma'
   Font.Style = []
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnCloseQuery = FormCloseQuery
   OnShortCut = FormShortCut
   OnShow = FormShow

+ 2 - 1
source/forms/Preferences.h

@@ -478,6 +478,7 @@ __published:
   void __fastcall SearchEditChangeEnter(TObject *Sender);
   void __fastcall NavigationTreeEnter(TObject *Sender);
   void __fastcall FormShortCut(TWMKey &Msg, bool &Handled);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 private:
   TPreferencesMode FPreferencesMode;
@@ -513,9 +514,9 @@ private:
   TSshHostCA::TList FSshHostCAPlainList;
   std::unique_ptr<TStrings> FSearchResults;
   bool FHideFocus;
+  bool FInitialized;
   void __fastcall CMDialogKey(TWMKeyDown & Message);
   void __fastcall WMHelp(TWMHelp & Message);
-  void __fastcall CMDpiChanged(TMessage & Message);
   void WMActivate(TWMActivate & Message);
   UnicodeString __fastcall TabSample(UnicodeString Values);
   void __fastcall AddEditCopyParam(TCopyParamPresetMode Mode);

+ 3 - 6
source/forms/SynchronizeChecklist.cpp

@@ -1269,10 +1269,6 @@ void __fastcall TSynchronizeChecklistDialog::Dispatch(void * AMessage)
       TForm::Dispatch(AMessage);
     }
   }
-  else if (Message.Msg == CM_DPICHANGED)
-  {
-    CMDpiChanged(Message);
-  }
   else if (Message.Msg == WM_WANTS_MOUSEWHEEL_INACTIVE)
   {
     Message.Result = FSynchronizing ? 1 : 0;
@@ -1294,10 +1290,11 @@ void __fastcall TSynchronizeChecklistDialog::UpdateImages()
   ListView->SmallImages = ShellImageListForControl(this, ilsSmall);
 }
 //---------------------------------------------------------------------------
-void __fastcall TSynchronizeChecklistDialog::CMDpiChanged(TMessage & Message)
+void __fastcall TSynchronizeChecklistDialog::FormAfterMonitorDpiChanged(TObject *, int OldDPI, int NewDPI)
 {
-  TForm::Dispatch(&Message);
+  DebugUsedParam2(OldDPI, NewDPI);
   UpdateImages();
+  UpdateStatusBarSize();
 }
 //---------------------------------------------------------------------------
 void __fastcall TSynchronizeChecklistDialog::ProcessedItem(void * /*Token*/, const TSynchronizeChecklist::TItem * ChecklistItem)

+ 1 - 0
source/forms/SynchronizeChecklist.dfm

@@ -1360,6 +1360,7 @@ object SynchronizeChecklistDialog: TSynchronizeChecklistDialog
     0000C0030000C0030000C0030000C0030000C0030000C0070000FC7F0000}
   KeyPreview = True
   Position = poOwnerFormCenter
+  OnAfterMonitorDpiChanged = FormAfterMonitorDpiChanged
   OnShow = FormShow
   TextHeight = 13
   object Panel: TPanel

+ 1 - 1
source/forms/SynchronizeChecklist.h

@@ -118,6 +118,7 @@ __published:
   void __fastcall ListViewRecreate(TObject *Sender);
   void __fastcall ToolsMenuButtonClick(TObject *Sender);
   void __fastcall FindMoveCandidateActionExecute(TObject *Sender);
+  void __fastcall FormAfterMonitorDpiChanged(TObject *Sender, int OldDPI, int NewDPI);
 
 public:
   __fastcall TSynchronizeChecklistDialog(
@@ -186,7 +187,6 @@ protected:
   TRect __fastcall GetColumnHeaderRect(int Index);
   virtual void __fastcall Dispatch(void * Message);
   void __fastcall UpdateImages();
-  void __fastcall CMDpiChanged(TMessage & Message);
   bool __fastcall GetWindowParams(UnicodeString & WindowParams);
   void __fastcall ProcessedItem(void * Token, const TSynchronizeChecklist::TItem * ChecklistItem);
   void __fastcall UpdatedSynchronizationChecklistItems(const TSynchronizeChecklist::TItemList & Items);

+ 1 - 0
source/packages/png/PngImageList.pas

@@ -103,6 +103,7 @@ type
     function GetIndexByName(const AName: TImageName): TImageIndex; {$IF CompilerVersion >= 34.0 Delphi 10.4 }override;{$IFEND}
     function GetNameByIndex(AIndex: TImageIndex): TImageName; {$IF CompilerVersion >= 34.0 Delphi 10.4 }override;{$IFEND}
     property ImageName[Index: Integer]: string read GetImageName;
+    property Scaled;
   published
     {$IF CompilerVersion >= 20.0 Delphi 2009 }
     property ColorDepth default cd32Bit;

+ 9 - 1
source/windows/GUITools.cpp

@@ -1167,7 +1167,8 @@ void __fastcall SelectScaledImageList(TImageList * ImageList)
 //---------------------------------------------------------------------------
 void __fastcall CopyImageList(TImageList * TargetList, TImageList * SourceList)
 {
-  // Maybe this is not necessary, once the TPngImageList::Assign was fixed
+  // Maybe this is not necessary, once the TPngImageList::Assign was fixed.
+  // But if we ever use Assign, make sure the target keeps its Scaled property.
   TPngImageList * PngTargetList = dynamic_cast<TPngImageList *>(TargetList);
   TPngImageList * PngSourceList = dynamic_cast<TPngImageList *>(SourceList);
 
@@ -2572,3 +2573,10 @@ void GUIFinalize()
     delete Thread;
   }
 }
+//---------------------------------------------------------------------------
+TCustomImageList * TreeViewImageList(TPngImageList * ImageList)
+{
+  // WORKAROUND Prevent DPI scaling, see TCustomTreeView.SetImages
+  ImageList->Scaled = true;
+  return ImageList;
+}

+ 1 - 0
source/windows/GUITools.h

@@ -111,6 +111,7 @@ using namespace Pngimagelist;
 TPngImageList * __fastcall GetAnimationsImages(TControl * Control);
 TImageList * __fastcall GetButtonImages(TControl * Control);
 TPngImageList * __fastcall GetDialogImages(TControl * Control);
+TCustomImageList * TreeViewImageList(TPngImageList * ImageList);
 void __fastcall ReleaseImagesModules();
 //---------------------------------------------------------------------------
 class TFrameAnimation

+ 34 - 161
source/windows/VCLCommon.cpp

@@ -502,16 +502,10 @@ friend TCanvas * CreateControlCanvas(TControl * Control);
 //---------------------------------------------------------------------
 class TPublicForm : public TForm
 {
-friend void __fastcall ChangeFormPixelsPerInch(TForm * Form, int PixelsPerInch);
 friend void __fastcall ShowAsModal(TForm * Form, void *& Storage, bool BringToFront, bool TriggerModalStarted);
 friend void __fastcall HideAsModal(TForm * Form, void *& Storage);
 friend void __fastcall ShowFormNoActivate(TForm * Form);
 };
-//---------------------------------------------------------------------
-class TPublicTreeView : public TCustomTreeView
-{
-friend void __fastcall ChangeControlScale(TControl * Control, int M, int D);
-};
 //---------------------------------------------------------------------------
 void __fastcall RealignControl(TControl * Control)
 {
@@ -521,17 +515,6 @@ void __fastcall RealignControl(TControl * Control)
 //---------------------------------------------------------------------------
 static Forms::TMonitor * LastMonitor = NULL;
 //---------------------------------------------------------------------------
-static int __fastcall GetTextHeightAtPixelsPerInch(TForm * Form, int PixelsPerInch)
-{
-  std::unique_ptr<TCanvas> Canvas(new TCanvas());
-  Canvas->Handle = GetDC(0);
-  Canvas->Font->Assign(Form->Font);
-  Canvas->Font->PixelsPerInch = PixelsPerInch; // Must be set BEFORE size
-  Canvas->Font->Size = Form->Font->Size;
-  // VCLCOPY: this is what TCustomForm.GetTextHeight does
-  return Canvas->TextHeight(L"0");
-}
-//---------------------------------------------------------------------------
 class TRescaleComponent : public TComponent
 {
 public:
@@ -564,33 +547,8 @@ void __fastcall SetRescaleFunction(
   Component->InsertComponent(RescaleComponent);
 }
 //---------------------------------------------------------------------------
-static void __fastcall ChangeControlScale(TControl * Control, int M, int D)
+static void __fastcall ChangeControlScale(TControl * Control)
 {
-  // VCLCOPY This is what TCustomListView.ChangeScale does,
-  // but it does that for initial loading only, when ScalingFlags includes sfWidth.
-  // But ScalingFlags is reset in TControl.ChangeScale (seems like a bug).
-  TCustomListView * CustomListView = dynamic_cast<TCustomListView *>(Control);
-  if (CustomListView != NULL)
-  {
-    TListView * ListView = reinterpret_cast<TListView *>(CustomListView);
-    for (int Index = 0; Index < ListView->Columns->Count; Index++)
-    {
-      TListColumn * Column = ListView->Columns->Items[Index];
-      if ((Column->Width != LVSCW_AUTOSIZE) &&
-          (Column->Width != LVSCW_AUTOSIZE_USEHEADER))
-      {
-        Column->Width = MulDiv(Column->Width, M, D);
-      }
-    }
-  }
-
-  TCustomTreeView * CustomTreeView = dynamic_cast<TCustomTreeView *>(Control);
-  if (CustomTreeView != NULL)
-  {
-    TPublicTreeView * PublicTreeView = static_cast<TPublicTreeView *>(CustomTreeView);
-    PublicTreeView->Indent = MulDiv(PublicTreeView->Indent, M, D);
-  }
-
   TCustomCombo * CustomCombo = dynamic_cast<TCustomCombo *>(Control);
   if (CustomCombo != NULL)
   {
@@ -609,7 +567,7 @@ static void __fastcall ChangeControlScale(TControl * Control, int M, int D)
     TControl * ChildControl = dynamic_cast<TControl *>(Component);
     if (ChildControl != NULL)
     {
-      ChangeControlScale(ChildControl, M, D);
+      ChangeControlScale(ChildControl);
     }
 
     TRescaleComponent * RescaleComponent =
@@ -620,7 +578,7 @@ static void __fastcall ChangeControlScale(TControl * Control, int M, int D)
     }
   }
 
-  Control->Perform(CM_DPICHANGED, M, D);
+  Control->Perform(CM_DPICHANGED, 0, 0);
 }
 //---------------------------------------------------------------------------
 typedef std::pair<int, int> TRatio;
@@ -632,14 +590,9 @@ public:
   __fastcall TFormCustomizationComponent() :
     TComponent(NULL)
   {
-    ImplicitRescaleAdded = false;
-    Rescaling = false;
     WindowStateBeforeMimimize = wsNormal;
   }
 
-  bool Rescaling;
-  bool ImplicitRescaleAdded;
-  TRatioMap RatioMap;
   TWindowState WindowStateBeforeMimimize;
 };
 //---------------------------------------------------------------------------
@@ -656,92 +609,11 @@ static TFormCustomizationComponent * GetFormCustomizationComponent(TForm * Form)
   return FormCustomizationComponent;
 }
 //---------------------------------------------------------------------------
-void __fastcall RecordFormImplicitRescale(TForm * Form)
+static void __fastcall ChangeFormPixelsPerInch(TForm * Form)
 {
-  if (Form->Scaled)
-  {
-    TFormCustomizationComponent * FormCustomizationComponent = GetFormCustomizationComponent(Form);
-    if (!FormCustomizationComponent->ImplicitRescaleAdded)
-    {
-      TRatio Ratio;
-      GetFormScaleRatio(Form, Ratio.first, Ratio.second);
-      TRatio RescaleKeyRatio(Form->PixelsPerInch, USER_DEFAULT_SCREEN_DPI);
-      FormCustomizationComponent->RatioMap[RescaleKeyRatio] = Ratio;
-      FormCustomizationComponent->ImplicitRescaleAdded = true;
-    }
-  }
-}
-//---------------------------------------------------------------------------
-static void GetFormRescaleRatio(TForm * Form, int PixelsPerInch, int & M, int & D)
-{
-  TFormCustomizationComponent * FormCustomizationComponent = GetFormCustomizationComponent(Form);
-  TRatio ReverseRescaleKeyRatio(Form->PixelsPerInch, PixelsPerInch);
-  if (FormCustomizationComponent->RatioMap.count(ReverseRescaleKeyRatio) > 0)
-  {
-    M = FormCustomizationComponent->RatioMap[ReverseRescaleKeyRatio].second;
-    D = FormCustomizationComponent->RatioMap[ReverseRescaleKeyRatio].first;
-  }
-  else
-  {
-    M = GetTextHeightAtPixelsPerInch(Form, PixelsPerInch);
-    D = GetTextHeightAtPixelsPerInch(Form, Form->PixelsPerInch);
-  }
-
-
-  TRatio RescaleKeyRatio(PixelsPerInch, Form->PixelsPerInch);
-  FormCustomizationComponent->RatioMap[RescaleKeyRatio] = TRatio(M, D);
-}
-//---------------------------------------------------------------------------
-static void __fastcall ChangeFormPixelsPerInch(TForm * Form, int PixelsPerInch)
-{
-  RecordFormImplicitRescale(Form);
-
-  TFormCustomizationComponent * FormCustomizationComponent = GetFormCustomizationComponent(Form);
-
-  if ((Form->PixelsPerInch != PixelsPerInch) && // optimization
-      !FormCustomizationComponent->Rescaling)
-  {
-    AppLogFmt(L"Scaling window %s", (Form->Caption));
-    TAutoFlag RescalingFlag(FormCustomizationComponent->Rescaling);
-
-    int M, D;
-    GetFormRescaleRatio(Form, PixelsPerInch, M, D);
-
-    Form->PixelsPerInch = PixelsPerInch;
-    TPublicForm * PublicCustomForm = static_cast<TPublicForm *>(Form);
-
-    // WORKAROUND
-    // TCustomForm.ChangeScale scales contraints only after rescaling the size,
-    // so the unscaled constraints apply to the new size
-    // (e.g. with TLoginDialog)
-    std::unique_ptr<TSizeConstraints> Constraints(new TSizeConstraints(NULL));
-    Constraints->Assign(PublicCustomForm->Constraints);
-    std::unique_ptr<TSizeConstraints> NoConstraints(new TSizeConstraints(NULL));
-    PublicCustomForm->Constraints = NoConstraints.get();
-
-    // Does not seem to have any effect
-    PublicCustomForm->DisableAlign();
-    try
-    {
-      int PrevFontHeight = Form->Font->Height;
-      PublicCustomForm->ChangeScale(M, D);
-      // Re-rescale by Height as it has higher granularity, hence lower risk of loosing precision due to rounding
-      Form->Font->Height = MulDiv(PrevFontHeight, M, D);
-
-      ChangeControlScale(Form, M, D);
 
-      Constraints->MinWidth = MulDiv(Constraints->MinWidth, M, D);
-      Constraints->MaxWidth = MulDiv(Constraints->MaxWidth, M, D);
-      Constraints->MinHeight = MulDiv(Constraints->MinHeight, M, D);
-      Constraints->MaxHeight = MulDiv(Constraints->MaxHeight, M, D);
-      PublicCustomForm->Constraints = Constraints.get();
-    }
-    __finally
-    {
-      PublicCustomForm->EnableAlign();
-    }
-
-  }
+  AppLogFmt(L"Scaling window %s", (Form->Caption));
+  ChangeControlScale(Form);
 }
 //---------------------------------------------------------------------------
 static void __fastcall FormShowingChanged(TForm * Form, TWndMethod WndProc, TMessage & Message)
@@ -844,18 +716,6 @@ static void __fastcall FormShowingChanged(TForm * Form, TWndMethod WndProc, TMes
     }
   }
 
-  if (Form->Showing)
-  {
-    // At least on single monitor setup, monitor DPI is 100% but system DPI is higher,
-    // WM_DPICHANGED is not sent. But VCL scales the form using system DPI.
-    // Also we have to do this always for implicitly placed forms (poDefaultPosOnly), like TEditorForm,
-    // as they never get the WM_DPICHANGED.
-    // Call this before WndProc below, i.e. before OnShow event (particularly important for the TEditorForm::FormShow).
-
-    // GetControlPixelsPerInch would return Form.PixelsPerInch, but we want to get a new DPI of the form monitor.
-    ChangeFormPixelsPerInch(Form, GetMonitorPixelsPerInch(GetMonitorFromControl(Form)));
-  }
-
   bool WasFormCenter =
     (Form->Position == poMainFormCenter) ||
     (Form->Position == poOwnerFormCenter);
@@ -977,6 +837,21 @@ void __fastcall CountClicksForWindowPrint(TForm * Form)
   }
 }
 //---------------------------------------------------------------------------
+struct TWMDpiChangedData
+{
+  TMonitorDpiChangedEvent OnAfterMonitorDpiChanged;
+};
+//---------------------------------------------------------------------------
+static void __fastcall AfterMonitorDpiChanged(void * AData, TObject * Sender, int OldDPI, int NewDPI)
+{
+  TWMDpiChangedData * Data = static_cast<TWMDpiChangedData *>(AData);
+  ChangeFormPixelsPerInch(DebugNotNull(dynamic_cast<TForm *>(Sender)));
+  if (Data->OnAfterMonitorDpiChanged != NULL)
+  {
+    Data->OnAfterMonitorDpiChanged(Sender, OldDPI, NewDPI);
+  }
+}
+//---------------------------------------------------------------------------
 inline void __fastcall DoFormWindowProc(TCustomForm * Form, TWndMethod WndProc,
   TMessage & Message)
 {
@@ -1014,20 +889,19 @@ inline void __fastcall DoFormWindowProc(TCustomForm * Form, TWndMethod WndProc,
       WndProc(Message);
     }
   }
-  else if (Message.Msg == WM_GETDPISCALEDSIZE)
-  {
-    WndProc(Message);
-    int M, D;
-    GetFormRescaleRatio(AForm, LOWORD(Message.WParam), M, D);
-    SIZE & Size = *(reinterpret_cast<SIZE *>(Message.LParam));
-    Size.cx = MulDiv(Size.cx, M, D);
-    Size.cy = MulDiv(Size.cy, M, D);
-    Message.Result = TRUE;
-  }
   else if (Message.Msg == WM_DPICHANGED)
   {
-    ChangeFormPixelsPerInch(AForm, LOWORD(Message.WParam));
-    WndProc(Message);
+    TWMDpiChangedData WMDpiChangedData;
+    WMDpiChangedData.OnAfterMonitorDpiChanged = AForm->OnAfterMonitorDpiChanged;
+    AForm->OnAfterMonitorDpiChanged = MakeMethod<TMonitorDpiChangedEvent>(&WMDpiChangedData, AfterMonitorDpiChanged);
+    try
+    {
+      WndProc(Message);
+    }
+    __finally
+    {
+      AForm->OnAfterMonitorDpiChanged = WMDpiChangedData.OnAfterMonitorDpiChanged;
+    }
   }
   else if ((Message.Msg == WM_LBUTTONDOWN) || (Message.Msg == WM_LBUTTONDBLCLK))
   {
@@ -1190,9 +1064,7 @@ void __fastcall UseSystemSettingsPre(TForm * Control)
 
   // We have legacy XE6 font (Tahoma) explicitly set in DFMs to match the layout.
   // That unlinks the font fonts from the Application->DefaultFont. We set the DefaultFont to Tahoma in WinSCP.cpp.
-  // So we can link the form's font back to it now.
   DebugAssert(SameFont(Control->Font, Application->DefaultFont));
-  Control->ParentFont = true;
 
   ApplySystemSettingsOnControl(Control);
 };
@@ -1390,7 +1262,8 @@ bool __fastcall SelectDirectory(UnicodeString & Path, const UnicodeString Prompt
       Directory = ExtractFilePath(Path);
       FileName = ExtractFileName(Path);
     }
-    Result = SelectDirectory(Prompt, L"", Directory);
+    TSelectDirExtOpts Opts = TSelectDirExtOpts() << sdNewUI;
+    Result = SelectDirectory(Prompt, EmptyStr, Directory, Opts);
     if (Result)
     {
       Path = Directory;

+ 0 - 1
source/windows/VCLCommon.h

@@ -90,7 +90,6 @@ TPanel * __fastcall CreateBlankPanel(TComponent * Owner);
 typedef void __fastcall (*TRescaleEvent)(TComponent * Sender, TObject * Token);
 void __fastcall SetRescaleFunction(
   TComponent * Component, TRescaleEvent OnRescale, TObject * Token = NULL, bool OwnsToken = false);
-void __fastcall RecordFormImplicitRescale(TForm * Form);
 TWindowState GetWindowStateBeforeMimimize(TForm * Form);
 void __fastcall CountClicksForWindowPrint(TForm * Form);
 bool IsButtonBeingClicked(TButtonControl * Button);

+ 0 - 3
source/windows/WinApi.h

@@ -11,9 +11,6 @@ typedef BOOL WINAPI (* ChangeWindowMessageFilterExProc)(
 typedef BOOL WINAPI (* AddClipboardFormatListenerProc)(HWND hwnd);
 typedef BOOL WINAPI (* RemoveClipboardFormatListenerProc)(HWND hwnd);
 //---------------------------------------------------------------------------
-typedef HRESULT WINAPI (* GetDpiForMonitorProc)(
-  HMONITOR hmonitor, MONITOR_DPI_TYPE dpiType, UINT * dpiX, UINT * dpiY);
-//---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 // Taken from https://www.codeproject.com/Articles/35197/Undocumented-List-View-Features
 //---------------------------------------------------------------------------

+ 6 - 46
source/windows/WinMain.cpp

@@ -548,61 +548,21 @@ void __fastcall UpdateStaticUsage()
   Configuration->Usage->Set(L"EncodingMultiByteAnsi", !TEncoding::Default->IsSingleByte);
   Configuration->Usage->Set(L"PixelsPerInch", Screen->PixelsPerInch);
 
-  bool PixelsPerInchSystemDiffers = false;
+  int PrimaryPixelsPerInch = Screen->PrimaryMonitor->PixelsPerInch;
   bool PixelsPerInchMonitorsDiffer = false;
-  bool PixelsPerInchAxesDiffer = false;
-
-  HINSTANCE ShCoreLibrary = LoadLibrary(L"shcore.dll");
-  if (ShCoreLibrary != NULL)
+  for (int Index = 0; Index < Screen->MonitorCount; Index++)
   {
-    GetDpiForMonitorProc GetDpiForMonitor =
-      (GetDpiForMonitorProc)GetProcAddress(ShCoreLibrary, "GetDpiForMonitor");
-
-    if (GetDpiForMonitor != NULL)
+    if (Screen->Monitors[Index]->PixelsPerInch != PrimaryPixelsPerInch)
     {
-      unsigned int PrimaryDpiX;
-      unsigned int PrimaryDpiY;
-
-      for (int Index = 0; Index < Screen->MonitorCount; Index++)
-      {
-        unsigned int DpiX;
-        unsigned int DpiY;
-        GetDpiForMonitor(Screen->Monitors[Index]->Handle, MDT_DEFAULT, &DpiX, &DpiY);
-
-        if (DpiX != DpiY)
-        {
-          PixelsPerInchAxesDiffer = true;
-        }
-
-        if (Index == 0)
-        {
-          PrimaryDpiX = DpiX;
-          PrimaryDpiY = DpiY;
-
-          // PixelsPerInch is GetDeviceCaps(DC, LOGPIXELSY)
-          if (DpiY != (unsigned int)Screen->PixelsPerInch)
-          {
-            PixelsPerInchSystemDiffers = true;
-          }
-        }
-        else
-        {
-          if ((DpiX != PrimaryDpiX) ||
-              (DpiY != PrimaryDpiY))
-          {
-            PixelsPerInchMonitorsDiffer = true;
-          }
-        }
-      }
+      PixelsPerInchMonitorsDiffer = true;
     }
   }
 
-  if (PixelsPerInchSystemDiffers)
+  if (PrimaryPixelsPerInch != Screen->PixelsPerInch)
   {
-    Configuration->Usage->Inc(L"PixelsPerInchSystemDiffered");
+    Configuration->Usage->Inc(L"PixelsPerInchSystemDiffered2");
   }
   Configuration->Usage->Set(L"PixelsPerInchMonitorsDiffer", PixelsPerInchMonitorsDiffer);
-  Configuration->Usage->Set(L"PixelsPerInchAxesDiffer", PixelsPerInchAxesDiffer);
 
   Configuration->Usage->Set(L"WorkAreaWidth", Screen->WorkAreaWidth);
   Configuration->Usage->Set(L"WorkAreaHeight", Screen->WorkAreaHeight);