Forráskód Böngészése

Bug 1696: Dark theme

https://winscp.net/tracker/1696

Source commit: 5093ac8f9a61e0c632fbe5f2d3c55f03a3393dcf
Martin Prikryl 6 éve
szülő
commit
71340fa744

+ 61 - 6
source/forms/CustomScpExplorer.cpp

@@ -996,6 +996,8 @@ void __fastcall TCustomScpExplorerForm::UpdateSessionsPageControlHeight()
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::ConfigurationChanged()
 {
+  Color = WinConfiguration->UseDarkTheme() ? TColor(RGB(43, 43, 43)) : clBtnFace;
+
   DebugAssert(Configuration && RemoteDirView);
   RemoteDirView->DimmHiddenFiles = WinConfiguration->DimmHiddenFiles;
   RemoteDirView->ShowHiddenFiles = WinConfiguration->ShowHiddenFiles;
@@ -8318,6 +8320,31 @@ UnicodeString __fastcall TCustomScpExplorerForm::PathForCaption()
   return Result;
 }
 //---------------------------------------------------------------------------
+TColor __fastcall TCustomScpExplorerForm::DefaultPanelColor()
+{
+  TColor Result = (WinConfiguration->UseDarkTheme() ? static_cast<TColor>(RGB(0x20, 0x20, 0x20)) : clWindow);
+  return Result;
+}
+//---------------------------------------------------------------------------
+TColor __fastcall TCustomScpExplorerForm::PanelColor()
+{
+  TColor Result = (FSessionColor != 0 ? FSessionColor : DefaultPanelColor());
+  return Result;
+}
+//---------------------------------------------------------------------------
+TColor __fastcall TCustomScpExplorerForm::PanelFontColor(TColor BackgroundColor)
+{
+  TColor Result = (IsDarkColor(BackgroundColor) ? clWhite : clWindowText);
+  SetContrast(Result, BackgroundColor, 180);
+  return Result;
+}
+//---------------------------------------------------------------------------
+TColor __fastcall TCustomScpExplorerForm::DisabledPanelColor()
+{
+  TColor Result = (WinConfiguration->UseDarkTheme() ? static_cast<TColor>(RGB(0x40, 0x40, 0x40)) : clBtnFace);
+  return Result;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::UpdateControls()
 {
   TTerminalManager::Instance()->UpdateAppTitle();
@@ -8343,9 +8370,7 @@ void __fastcall TCustomScpExplorerForm::UpdateControls()
           ActiveControl = RemoteDirView;
         }
       }
-      RemoteDriveView->Enabled = true;
-      RemoteDirView->Color = (FSessionColor != 0 ? FSessionColor : clWindow);
-      RemoteDriveView->Color = RemoteDirView->Color;
+      RemoteDirView->Color = PanelColor();
     }
     else
     {
@@ -8356,12 +8381,30 @@ void __fastcall TCustomScpExplorerForm::UpdateControls()
         // but the false is overriden in the constructor later.
         // An even later in TScpCommanderForm::DoShow()
         FRemoteDirViewWasFocused = (ActiveControl == RemoteDirView);
-        EnableControl(RemoteDirView, false);
+        RemoteDirView->Enabled = false;
+        RemoteDirView->Color = DisabledPanelColor();
       }
-      EnableControl(RemoteDriveView, false);
     }
-    EnableControl(QueueView3, HasTerminal && Terminal->IsCapable[fsBackgroundTransfers]);
+
+    RemoteDirView->Font->Color = PanelFontColor(RemoteDirView->Color);
+
+    RemoteDriveView->Enabled = RemoteDirView->Enabled;
+    RemoteDriveView->Color = RemoteDirView->Color;
+    RemoteDriveView->Font->Color = RemoteDirView->Font->Color;
+
+    QueueView3->Enabled = HasTerminal && Terminal->IsCapable[fsBackgroundTransfers];
+    QueueView3->Color = QueueView3->Enabled ? DefaultPanelColor() : DisabledPanelColor();
     QueueLabelUpdateStatus();
+
+    bool UseDarkTheme = WinConfiguration->UseDarkTheme();
+    AllowDarkModeForWindow(RemoteDirView, UseDarkTheme);
+    AllowDarkModeForWindow(RemoteDriveView, UseDarkTheme);
+    AllowDarkModeForWindow(SessionsPageControl, UseDarkTheme);
+    if (QueueView3->HandleAllocated())
+    {
+      AllowDarkModeForWindow(QueueView3, UseDarkTheme);
+    }
+
     reinterpret_cast<TTBCustomItem *>(GetComponent(fcRemotePathComboBox))->Enabled = HasTerminal;
   }
 }
@@ -8689,6 +8732,14 @@ void __fastcall TCustomScpExplorerForm::QueueSplitterDblClick(TObject * /*Sender
   PostComponentHide(fcQueueView);
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::WMWinIniChange(TMessage & Message)
+{
+  WinConfiguration->ResetSysDarkTheme();
+  ConfigurationChanged();
+  ConfigureInterface();
+  TForm::Dispatch(&Message);
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::Dispatch(void * Message)
 {
   TMessage * M = static_cast<TMessage*>(Message);
@@ -8773,6 +8824,10 @@ void __fastcall TCustomScpExplorerForm::Dispatch(void * Message)
       CMDpiChanged(*M);
       break;
 
+    case WM_WININICHANGE:
+      WMWinIniChange(*M);
+      break;
+
     default:
       TForm::Dispatch(Message);
       break;

+ 0 - 2
source/forms/CustomScpExplorer.dfm

@@ -50,9 +50,7 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       Height = 240
       Cursor = crSizeWE
       AutoSnap = False
-      Color = clBtnFace
       MinSize = 70
-      ParentColor = False
       ResizeStyle = rsUpdate
     end
     object RemoteStatusBar: TTBXStatusBar

+ 5 - 0
source/forms/CustomScpExplorer.h

@@ -651,6 +651,11 @@ protected:
   void __fastcall CloseSessionTab(int Index);
   void __fastcall DoFileColorsChanged(TCustomDirView * DirView);
   virtual void __fastcall FileColorsChanged();
+  TColor __fastcall DefaultPanelColor();
+  TColor __fastcall PanelColor();
+  TColor __fastcall PanelFontColor(TColor BackgroundColor);
+  TColor __fastcall DisabledPanelColor();
+  void __fastcall WMWinIniChange(TMessage & Message);
 
 public:
   virtual __fastcall ~TCustomScpExplorerForm();

+ 4 - 0
source/forms/Preferences.cpp

@@ -569,6 +569,8 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
     BOOLPROP(CopyParamAutoSelectNotice);
 
     // interface
+    ComboAutoSwitchLoad(ThemeCombo, WinConfiguration->DarkTheme);
+
     switch (CustomWinConfiguration->Interface)
     {
       case ifCommander:
@@ -877,6 +879,8 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
     BOOLPROP(CopyParamAutoSelectNotice);
 
     // interface
+    WinConfiguration->DarkTheme = ComboAutoSwitchSave(ThemeCombo);
+
     if (GetInterface() != CustomWinConfiguration->Interface)
     {
       Configuration->Usage->Inc(L"InterfaceChanges");

+ 36 - 3
source/forms/Preferences.dfm

@@ -503,19 +503,19 @@ object PreferencesDialog: TPreferencesDialog
           398)
         object InterfaceChangeLabel: TLabel
           Left = 8
-          Top = 222
+          Top = 280
           Width = 177
           Height = 13
           Caption = 'Changes will apply on the next start.'
         end
         object InterfaceGroup: TGroupBox
           Left = 8
-          Top = 8
+          Top = 66
           Width = 389
           Height = 208
           Anchors = [akLeft, akTop, akRight, akBottom]
           Caption = 'User Interface'
-          TabOrder = 0
+          TabOrder = 1
           DesignSize = (
             389
             208)
@@ -584,6 +584,39 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = ControlChange
           end
         end
+        object ThemeGroup: TGroupBox
+          Left = 8
+          Top = 8
+          Width = 389
+          Height = 52
+          Anchors = [akLeft, akTop, akRight]
+          Caption = 'Theme'
+          TabOrder = 0
+          DesignSize = (
+            389
+            52)
+          object Label7: TLabel
+            Left = 16
+            Top = 23
+            Width = 82
+            Height = 13
+            Caption = 'Interface &theme:'
+            FocusControl = ThemeCombo
+          end
+          object ThemeCombo: TComboBox
+            Left = 132
+            Top = 18
+            Width = 141
+            Height = 21
+            Style = csDropDownList
+            Anchors = [akLeft, akTop, akRight]
+            TabOrder = 0
+            Items.Strings = (
+              'Automatic'
+              'Light'
+              'Dark')
+          end
+        end
       end
       object PanelsSheet: TTabSheet
         Tag = 4

+ 3 - 0
source/forms/Preferences.h

@@ -330,6 +330,9 @@ __published:
   TButton *UpFileColorButton;
   TButton *DownFileColorButton;
   TButton *EditFileColorButton;
+  TGroupBox *ThemeGroup;
+  TLabel *Label7;
+  TComboBox *ThemeCombo;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall EditorFontButtonClick(TObject *Sender);

+ 11 - 1
source/forms/ScpCommander.cpp

@@ -806,8 +806,18 @@ void __fastcall TScpCommanderForm::UpdateControls()
     // command line combo width needs to be updated as caption width has probably changed
     ToolBarResize(CommandLineToolbar);
   }
-  LocalDirView->Color = (SessionColor != 0 ? SessionColor : clWindow);
+  if (LocalDirView->HandleAllocated())
+  {
+    AllowDarkModeForWindow(LocalDirView, WinConfiguration->UseDarkTheme());
+  }
+  if (LocalDriveView->HandleAllocated())
+  {
+    AllowDarkModeForWindow(LocalDriveView, WinConfiguration->UseDarkTheme());
+  }
+  LocalDirView->Color = PanelColor();
   LocalDriveView->Color = LocalDirView->Color;
+  LocalDirView->Font->Color = PanelFontColor(LocalDirView->Color);
+  LocalDriveView->Font->Color = LocalDirView->Font->Color;
 
   bool LocalSide = (FCurrentSide == osLocal);
   TAction * CurrentCopyAction = LocalSide ? NonVisualDataModule->LocalCopyAction : NonVisualDataModule->RemoteCopyAction;

+ 3 - 7
source/forms/ScpCommander.dfm

@@ -1030,6 +1030,7 @@ inherited ScpCommanderForm: TScpCommanderForm
     Height = 298
     Constraints.MinHeight = 220
     Constraints.MinWidth = 185
+    ParentColor = True
     TabOrder = 1
     object RemotePathLabel: TPathLabel [0]
       Left = 0
@@ -1118,7 +1119,6 @@ inherited ScpCommanderForm: TScpCommanderForm
       Top = 0
       Width = 458
       Height = 79
-      Color = clBtnFace
       FixAlign = True
       OnContextPopup = DockContextPopup
       object RemoteHistoryToolbar: TTBXToolbar
@@ -1313,7 +1313,6 @@ inherited ScpCommanderForm: TScpCommanderForm
       Top = 270
       Width = 458
       Height = 9
-      Color = clBtnFace
       FixAlign = True
       Position = dpBottom
     end
@@ -1322,6 +1321,7 @@ inherited ScpCommanderForm: TScpCommanderForm
     Top = 532
     Width = 898
     Height = 116
+    ParentColor = True
     TabOrder = 2
     inherited QueueLabel: TPathLabel
       Width = 898
@@ -1346,10 +1346,10 @@ inherited ScpCommanderForm: TScpCommanderForm
     Height = 298
     Align = alLeft
     BevelOuter = bvNone
-    Color = clWindow
     Constraints.MinHeight = 220
     Constraints.MinWidth = 185
     ParentBackground = False
+    ParentColor = True
     TabOrder = 0
     object LocalPathLabel: TPathLabel
       Left = 0
@@ -1378,9 +1378,7 @@ inherited ScpCommanderForm: TScpCommanderForm
         'ctory trees equal.'
       Align = alTop
       AutoSnap = False
-      Color = clBtnFace
       MinSize = 70
-      ParentColor = False
       ResizeStyle = rsUpdate
     end
     object LocalStatusBar: TTBXStatusBar
@@ -1467,7 +1465,6 @@ inherited ScpCommanderForm: TScpCommanderForm
       Top = 0
       Width = 435
       Height = 79
-      Color = clBtnFace
       FixAlign = True
       OnContextPopup = DockContextPopup
       object LocalHistoryToolbar: TTBXToolbar
@@ -1676,7 +1673,6 @@ inherited ScpCommanderForm: TScpCommanderForm
       Top = 270
       Width = 435
       Height = 9
-      Color = clBtnFace
       FixAlign = True
       Position = dpBottom
     end

+ 24 - 3
source/packages/filemng/CustomDirView.pas

@@ -183,6 +183,7 @@ type
     procedure LVMSetExtendedListViewStyle(var Message: TMessage); message LVM_SETEXTENDEDLISTVIEWSTYLE;
     procedure CMRecreateWnd(var Message: TMessage); message CM_RECREATEWND;
     procedure CMDPIChanged(var Message: TMessage); message CM_DPICHANGED;
+    procedure CMEnabledChanged(var Message: TMessage); message CM_ENABLEDCHANGED;
 
     procedure DumbCustomDrawItem(Sender: TCustomListView; Item: TListItem;
       State: TCustomDrawState; var DefaultDraw: Boolean);
@@ -1115,6 +1116,27 @@ begin
   NeedImageLists(True);
 end;
 
+const
+  RequiredStyles = LVS_EX_DOUBLEBUFFER or LVS_EX_TRANSPARENTBKGND;
+
+procedure TCustomDirView.CMEnabledChanged(var Message: TMessage);
+var
+  ListViewStyle: DWORD;
+begin
+  inherited;
+  // We need this so that we can control background color of disabled file panel for dark theme.
+  // See comment in LVMSetExtendedListViewStyle for an explanation.
+  ListViewStyle := ListView_GetExtendedListViewStyle(Handle);
+  if Enabled then
+  begin
+    ListView_SetExtendedListViewStyle(Handle, (ListViewStyle or RequiredStyles));
+  end
+    else
+  begin
+    ListView_SetExtendedListViewStyle(Handle, (ListViewStyle and (not RequiredStyles)));
+  end;
+end;
+
 procedure TCustomDirView.FreeImageLists;
 begin
   FreeAndNil(FImageList16);
@@ -1143,11 +1165,10 @@ procedure TCustomDirView.LVMSetExtendedListViewStyle(var Message: TMessage);
 // keyboard (Page-up/Down). This gets fixed by LVS_EX_TRANSPARENTBKGND,
 // but that works on Vista and newer only. See WMKeyDown
 // for workaround on earlier systems.
-const
-  RequiredStyles = LVS_EX_DOUBLEBUFFER or LVS_EX_TRANSPARENTBKGND;
 begin
   // This prevents TCustomListView.ResetExStyles resetting our styles
-  if (Message.WParam = 0) and
+  if Enabled and
+     (Message.WParam = 0) and
      ((Message.LParam and RequiredStyles) <> RequiredStyles) then
   begin
     ListView_SetExtendedListViewStyle(Handle, Message.LParam or RequiredStyles);

+ 1 - 0
source/packages/tbx/TBX.pas

@@ -3464,6 +3464,7 @@ end;
 initialization
   FixPlaySoundDelay;
   RegisterTBXTheme('OfficeXP', TTBXOfficeXPTheme);
+  RegisterTBXTheme('DarkOfficeXP', TTBXDarkOfficeXPTheme);
   TBXNexus := TTBXNexus.Create('OfficeXP');
   TBXMenuAnimation := TTBXMenuAnimation.Create;
 

+ 2 - 2
source/packages/tbx/TBXExtItems.pas

@@ -839,8 +839,8 @@ begin
     Fnt := Item.EditorFontSettings.CreateTransformedFont(TTBViewAccess(View).GetFont.Handle, C);
     OldFnt := SelectObject(DC, Fnt);
     SetBkMode(DC, TRANSPARENT);
-    SetBkColor(DC, GetSysColor(FillColors[Item.Enabled]));
-    SetTextColor(DC, GetSysColor(TextColors[Item.Enabled]));
+    SetBkColor(DC, CurrentTheme.GetSysColor(FillColors[Item.Enabled]));
+    SetTextColor(DC, CurrentTheme.GetSysColor(TextColors[Item.Enabled]));
     // WinSCP: Align edit text with toolbar labels
     InflateRect(R, 0, -1);
     DrawText(DC, PChar(S), Length(S), R, DT_SINGLELINE or DT_NOPREFIX or Alignments[Item.Alignment]);

+ 106 - 23
source/packages/tbx/TBXOfficeXPTheme.pas

@@ -42,12 +42,15 @@ type
     ToolbarSeparatorColor: TColor;
     IconShadowColor: TColor;
     StatusPanelFrameColor: TColor;
+    FDark: Boolean;
 
     procedure SetupColorCache; virtual;
   protected
     { Internal Methods }
     function GetPartColor(const ItemInfo: TTBXItemInfo; ItemPart: TItemPart): TColor;
     function GetBtnColor(const ItemInfo: TTBXItemInfo; ItemPart: TItemPart): TColor;
+    function GetStandardColor(Color: TColor): TColor;
+    procedure Init;
   public
     constructor Create(const AName: string); override;
     destructor Destroy; override;
@@ -64,6 +67,7 @@ type
     procedure GetViewBorder(Control: TControl; ViewType: Integer; out Border: TPoint); override;
     function  GetViewColor(AViewType: Integer): TColor; override;
     procedure GetViewMargins(ViewType: Integer; out Margins: TTBXMargins); override;
+    function GetSysColor(nIndex: Integer): DWORD; override;
 
     { Painting routines }
     procedure PaintBackgnd(Canvas: TCanvas; const ADockRect, ARect, AClipRect: TRect; AColor: TColor; Transparent: Boolean; AViewType: Integer); override;
@@ -86,6 +90,12 @@ type
     procedure PaintStatusBar(Control: TWinControl; Canvas: TCanvas; R: TRect; Part: Integer); override;
   end;
 
+type
+  TTBXDarkOfficeXPTheme = class(TTBXOfficeXPTheme)
+  public
+    constructor Create(const AName: string); override;
+  end;
+
 function GetSelectedBodyColor: TColor;
 
 implementation
@@ -165,7 +175,7 @@ begin
   end
   else if (AViewType and VT_POPUP) = VT_POPUP then
   begin
-    if (AViewType and PVT_LISTBOX) = PVT_LISTBOX then Result := clWindow
+    if (AViewType and PVT_LISTBOX) = PVT_LISTBOX then Result := GetStandardColor(clWindow)
     else Result := PopupColor;
   end
   else if (AViewType and VT_DOCKPANEL) = VT_DOCKPANEL then Result := DockPanelColor
@@ -461,8 +471,8 @@ begin
           R := ARect;
           if not Embedded then
           begin
-            FrameRectEx(DC, R, clWindow, False);
-            C := clWindow;
+            FrameRectEx(DC, R, GetStandardColor(clWindow), False);
+            C := GetStandardColor(clWindow);
           end
           else C := GetBtnColor(ItemInfo, ipFrame);
           DrawLineEx(DC, R.Left - 1, R.Top, R.Left - 1, R.Bottom, C);
@@ -489,8 +499,8 @@ begin
           BR.Left := ARect.Left; BR.Top := ARect.Top; BR.Right := ARect.Right;
           if not Embedded then
           begin
-            FrameRectEx(DC, BR, clWindow, False);
-            C := clWindow;
+            FrameRectEx(DC, BR, GetStandardColor(clWindow), False);
+            C := GetStandardColor(clWindow);
           end
           else C := GetBtnColor(ItemInfo, ipFrame);
           DrawLineEx(DC, BR.Left - 1, BR.Top, BR.Left - 1, BR.Bottom, C);
@@ -515,8 +525,8 @@ begin
           BR.Left := ARect.Left; BR.Bottom := ARect.Bottom; BR.Right := ARect.Right;
           if not Embedded then
           begin
-            FrameRectEx(DC, BR, clWindow, False);
-            C := clWindow;
+            FrameRectEx(DC, BR, GetStandardColor(clWindow), False);
+            C := GetStandardColor(clWindow);
           end
           else C := GetBtnColor(ItemInfo, ipFrame);
           DrawLineEx(DC, BR.Left - 1, BR.Top, BR.Left - 1, BR.Bottom, C);
@@ -556,10 +566,10 @@ begin
     if ((ItemInfo.ViewType and VT_TOOLBAR) <> VT_TOOLBAR) and (GetPartColor(ItemInfo, ipFrame) = clNone) then
       FrameRectEx(DC, R, ToolbarColor, False)
     else
-      FrameRectEx(DC, R, clWindow, False);
+      FrameRectEx(DC, R, GetStandardColor(clWindow), False);
 
     InflateRect(R, -1, -1);
-    FillRectEx(DC, R, clWindow);
+    FillRectEx(DC, R, GetStandardColor(clWindow));
     if ((ItemInfo.ViewType and VT_TOOLBAR) <> VT_TOOLBAR) and (GetPartColor(ItemInfo, ipFrame) = clNone) then
     begin
       R := ARect;
@@ -780,7 +790,7 @@ begin
 
     if not Enabled then
     begin
-      HiContrast := ColorIntensity(GetItemImageBackground(ItemInfo)) < 80;
+      HiContrast := (not FDark) and (ColorIntensity(GetItemImageBackground(ItemInfo)) < 80);
       if not HiContrast then
         DrawTBXIconShadow(Canvas, ARect, ImageList, ImageIndex, 0)
       else
@@ -1192,9 +1202,18 @@ begin
  else
   begin
     { View/Window Colors }
-    MenubarColor := clBtnFace;
-    ToolbarColor := Blend(clWindow, clBtnFace, 165);
-    PopupColor := Blend(clBtnFace, clWindow, 143);
+    if not FDark then
+    begin
+      MenubarColor := clBtnFace;
+      ToolbarColor := Blend(clWindow, clBtnFace, 165);
+      PopupColor := Blend(clBtnFace, clWindow, 143);
+    end
+      else
+    begin
+      MenubarColor := clBlack;
+      ToolbarColor := RGB(32, 32, 32);
+      PopupColor := RGB(43, 43, 43);
+    end;
     DockPanelColor := PopupColor;
     PopupFrameColor := Blend(clBtnText, clBtnShadow, 20);
     SetContrast(PopupFrameColor, PopupColor, 100);
@@ -1222,15 +1241,25 @@ begin
     SetContrast(PnlFrameColors[wfsInactive, wfpCaptionText], clBtnFace, 120);
 
     BtnItemColors[bisNormal, ipBody]           := clNone;
-    BtnItemColors[bisNormal, ipText]           := clBtnText;
+    if not FDark then
+      BtnItemColors[bisNormal, ipText]         := clBtnText
+    else
+      BtnItemColors[bisNormal, ipText]         := clWhite;
     SetContrast(BtnItemColors[bisNormal, ipText], ToolbarColor, 180);
     BtnItemColors[bisNormal, ipFrame]          := clNone;
     BtnItemColors[bisDisabled, ipBody]         := clNone;
     BtnItemColors[bisDisabled, ipText]         := DisabledText;
     SetContrast(BtnItemColors[bisDisabled, ipText], ToolbarColor, 80);
     BtnItemColors[bisDisabled, ipFrame]        := clNone;
-    BtnItemColors[bisSelected, ipBody]         := GetSelectedBodyColor;
-    SetContrast(BtnItemColors[bisSelected, ipBody], ToolbarColor, 5);
+    if not FDark then
+    begin
+      BtnItemColors[bisSelected, ipBody]       := GetSelectedBodyColor;
+      SetContrast(BtnItemColors[bisSelected, ipBody], ToolbarColor, 5);
+    end
+      else
+    begin
+      BtnItemColors[bisSelected, ipBody]       := clBlack;
+    end;
     BtnItemColors[bisSelected, ipText]         := BtnItemColors[bisNormal, ipText];
     BtnItemColors[bisSelected, ipFrame]        := clHighlight;
     BtnItemColors[bisPressed, ipBody]          := Blend(clHighlight, clWindow, 50);
@@ -1255,8 +1284,15 @@ begin
     BtnItemColors[bisPopupParent, ipFrame]     := PopupFrameColor;
 
     MenuItemColors[misNormal, ipBody]          := clNone;
-    MenuItemColors[misNormal, ipText]          := clWindowText;
-    SetContrast(MenuItemColors[misNormal, ipText], PopupColor, 180);
+    if not FDark then
+    begin
+      MenuItemColors[misNormal, ipText]        := clWindowText;
+      SetContrast(MenuItemColors[misNormal, ipText], PopupColor, 180);
+    end
+      else
+    begin
+      MenuItemColors[misNormal, ipText]        := clWhite;
+    end;
     MenuItemColors[misNormal, ipFrame]         := clNone;
     MenuItemColors[misDisabled, ipBody]        := clNone;
     MenuItemColors[misDisabled, ipText]        := Blend(clGrayText, clWindow, 70);
@@ -1270,14 +1306,23 @@ begin
     MenuItemColors[misDisabledHot, ipFrame]    := clHighlight;
 
     { Other Colors }
-    DragHandleColor := Blend(clBtnShadow, clWindow, 75);
+    if not FDark then
+    begin
+      DragHandleColor := Blend(clBtnShadow, clWindow, 75);
+      ToolbarSeparatorColor := Blend(clBtnShadow, clWindow, 70);
+      StatusPanelFrameColor := Blend(clWindow, clBtnFace, 30);
+      SetContrast(StatusPanelFrameColor, clBtnFace, 30);
+    end
+      else
+    begin
+      DragHandleColor := RGB(65, 65, 65);
+      ToolbarSeparatorColor := DragHandleColor;
+      StatusPanelFrameColor := DragHandleColor;
+    end;
     SetContrast(DragHandleColor, ToolbarColor, 85);
     IconShadowColor := Blend(clBlack, HotBtnFace, 25);
-    ToolbarSeparatorColor := Blend(clBtnShadow, clWindow, 70);
     SetContrast(ToolbarSeparatorColor, ToolbarColor, 50);
     PopupSeparatorColor := ToolbarSeparatorColor;
-    StatusPanelFrameColor := Blend(clWindow, clBtnFace, 30);
-    SetContrast(StatusPanelFrameColor, clBtnFace, 30);
 
     Undither(MenubarColor);
     Undither(ToolbarColor);
@@ -1348,6 +1393,12 @@ end;
 constructor TTBXOfficeXPTheme.Create(const AName: string);
 begin
   inherited;
+  FDark := False;
+  Init;
+end;
+
+procedure TTBXOfficeXPTheme.Init;
+begin
   if CounterLock = 0 then InitializeStock;
   Inc(CounterLock);
   AddTBXSysChangeNotification(Self);
@@ -1370,6 +1421,26 @@ begin
   Margins.BottomHeight := 0;
 end;
 
+function TTBXOfficeXPTheme.GetSysColor(nIndex: Integer): DWORD;
+begin
+  if FDark and (nIndex = COLOR_WINDOWTEXT) then
+    Result := clWhite
+  else
+    Result := Windows.GetSysColor(nIndex);
+end;
+
+function TTBXOfficeXPTheme.GetStandardColor(Color: TColor): TColor;
+begin
+  if FDark then
+  begin
+    if Color = clWindow then
+    begin
+      Color := RGB(65, 65, 65);
+    end;
+  end;
+  Result := Color;
+end;
+
 procedure TTBXOfficeXPTheme.PaintStatusBar(Control: TWinControl; Canvas: TCanvas; R: TRect; Part: Integer);
 var
   D: Integer;
@@ -1384,12 +1455,15 @@ var
 
 var
   Theme: THandle;
+  C: TColor;
 begin
   DC := Canvas.Handle;
   case Part of
     SBP_BODY:
       begin
-        FillRectEx(DC, R, clBtnFace);
+        if not FDark then C := clBtnFace
+          else C := ToolbarColor;
+        FillRectEx(DC, R, C);
       end;
     SBP_PANE, SBP_LASTPANE:
       begin
@@ -1416,4 +1490,13 @@ begin
   Result := Blend(clHighlight, Blend(clBtnFace, clWindow, 50), 10);
 end;
 
+{ TTBXDarkOfficeXPTheme }
+
+constructor TTBXDarkOfficeXPTheme.Create(const AName: string);
+begin
+  inherited;
+  FDark := True;
+  Init;
+end;
+
 end.

+ 1 - 0
source/packages/tbx/TBXThemes.pas

@@ -294,6 +294,7 @@ type
     procedure GetViewBorder(Control: TControl; ViewType: Integer; out Border: TPoint); virtual; abstract;
     function  GetViewColor(ViewType: Integer): TColor; virtual; abstract;
     procedure GetViewMargins(ViewType: Integer; out Margins: TTBXMargins); virtual; abstract;
+    function GetSysColor(nIndex: Integer): DWORD; virtual; abstract;
 
     { General painting routines }
     procedure PaintBackgnd(Canvas: TCanvas; const ADockRect, ARect, AClipRect: TRect; AColor: TColor; Transparent: Boolean; AViewType: Integer); virtual; abstract;

+ 3 - 0
source/packages/tbx/TBXUtils.pas

@@ -544,6 +544,9 @@ begin
       S := Src^;
       if S <> Dst^ then
       begin
+        // The algorithm does not work well against a black background
+        if ColorIntensity(Dst^) < 80 then
+          Dst^ := $00909090;
         CBRB := Dst^ and $00FF00FF;
         CBG  := Dst^ and $0000FF00;
         C := ((S and $00FF0000) shr 16 * 29 + (S and $0000FF00) shr 8 * 150 +

+ 8 - 0
source/windows/UserInterface.cpp

@@ -376,6 +376,14 @@ void __fastcall ConfigureInterface()
     AdjustLocaleFlag(LoadStr(BIDI_MODE), WinConfiguration->BidiModeOverride, false, bdRightToLeft, bdLeftToRight);
   Application->BiDiMode = static_cast<TBiDiMode>(BidiModeFlag);
   SetTBXSysParam(TSP_XPVISUALSTYLE, XPVS_AUTOMATIC);
+  if (WinConfiguration != NULL)
+  {
+    UnicodeString Theme = WinConfiguration->UseDarkTheme() ? L"DarkOfficeXP" : L"OfficeXP";
+    if (!SameText(TBXCurrentTheme(), Theme))
+    {
+      TBXSetTheme(Theme);
+    }
+  }
   // Has any effect on Wine only
   // (otherwise initial UserDocumentDirectory is equivalent to GetPersonalFolder())
   UserDocumentDirectory = GetPersonalFolder();

+ 28 - 0
source/windows/VCLCommon.cpp

@@ -2564,6 +2564,11 @@ void TDesktopFontManager::UpdateControl(TControl * Control)
     DesktopFont->PixelsPerInch = PublicControl->Font->PixelsPerInch;
   }
 
+  // Neither CreateFontIndirect nor RestoreFont set  color, so we should should have the default set by TFont constructor here.
+  DebugAssert(DesktopFont->Color == clWindowText);
+  // Preserve color (particularly whice color of file panel font in dark mode)
+  DesktopFont->Color = PublicControl->Font->Color;
+
   PublicControl->Font->Assign(DesktopFont.get());
 }
 //---------------------------------------------------------------------------
@@ -2716,3 +2721,26 @@ TPanel * __fastcall CreateBlankPanel(TComponent * Owner)
   Panel->BevelKind = bkNone;
   return Panel;
 }
+//---------------------------------------------------------------------------
+typedef bool (WINAPI * AllowDarkModeForWindowProc)(HWND Handle, bool Allow);
+AllowDarkModeForWindowProc AAllowDarkModeForWindow;
+bool AllowDarkModeForWindowLoaded = false;
+void AllowDarkModeForWindow(TWinControl * Control, bool Allow)
+{
+  if (!AllowDarkModeForWindowLoaded)
+  {
+    HMODULE UxThemeLib = GetModuleHandle(L"UxTheme");
+    if (DebugAlwaysTrue(UxThemeLib != NULL))
+    {
+      AAllowDarkModeForWindow =
+        reinterpret_cast<AllowDarkModeForWindowProc>(GetProcAddress(UxThemeLib, MAKEINTRESOURCEA(133)));
+    }
+    AllowDarkModeForWindowLoaded = true;
+  }
+
+  if ((AAllowDarkModeForWindow != NULL) &&
+      DebugAlwaysTrue(Control->HandleAllocated()))
+  {
+    AAllowDarkModeForWindow(Control->Handle, Allow);
+  }
+}

+ 1 - 0
source/windows/VCLCommon.h

@@ -80,6 +80,7 @@ void __fastcall HookFormActivation(TCustomForm * Form);
 void __fastcall UnhookFormActivation(TCustomForm * Form);
 void __fastcall ShowFormNoActivate(TForm * Form);
 TPanel * __fastcall CreateBlankPanel(TComponent * Owner);
+void AllowDarkModeForWindow(TWinControl * Control, bool Allow);
 typedef void __fastcall (*TRescaleEvent)(TComponent * Sender, TObject * Token);
 void __fastcall SetRescaleFunction(
   TComponent * Component, TRescaleEvent OnRescale, TObject * Token = NULL, bool OwnsToken = false);

+ 55 - 0
source/windows/WinConfiguration.cpp

@@ -479,6 +479,7 @@ bool __fastcall TEditorList::IsDefaultList() const
 //---------------------------------------------------------------------------
 __fastcall TWinConfiguration::TWinConfiguration(): TCustomWinConfiguration()
 {
+  ResetSysDarkTheme();
   FInvalidDefaultTranslationMessage = L"";
   FDDExtInstalled = -1;
   FBookmarks = new TBookmarks();
@@ -566,6 +567,7 @@ void __fastcall TWinConfiguration::Default()
   FTemporaryDirectoryCleanup = true;
   FConfirmTemporaryDirectoryCleanup = true;
   FPreservePanelState = true;
+  FDarkTheme = asAuto;
   FLastStoredSession = L"";
   // deliberately not being saved, so that when saving ad-hoc workspace,
   // we do not offer to overwrite the last saved workspace, what may be undesirable
@@ -970,6 +972,7 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool & SessionList)
     KEY(Bool,     TemporaryDirectoryCleanup); \
     KEY(Bool,     ConfirmTemporaryDirectoryCleanup); \
     KEY(Bool,     PreservePanelState); \
+    KEY(Integer,  DarkTheme); \
     KEY(String,   LastStoredSession); \
     KEY(Bool,     AutoSaveWorkspace); \
     KEY(Bool,     AutoSaveWorkspacePasswords); \
@@ -2073,6 +2076,57 @@ void __fastcall TWinConfiguration::SetPreservePanelState(bool value)
   SET_CONFIG_PROPERTY(PreservePanelState);
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetDarkTheme(TAutoSwitch value)
+{
+  SET_CONFIG_PROPERTY_EX(DarkTheme, ConfigureInterface());
+}
+//---------------------------------------------------------------------------
+static int __fastcall SysDarkTheme(HKEY RootKey)
+{
+  std::unique_ptr<TRegistry> Registry(new TRegistry());
+  Registry->RootKey = RootKey;
+  UnicodeString ThemesPersonalizeKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize";
+  UnicodeString AppsUseLightThemeValue = L"AppsUseLightTheme";
+  int Result = -1;
+  if (Registry->OpenKeyReadOnly(ThemesPersonalizeKey) &&
+      Registry->ValueExists(AppsUseLightThemeValue))
+  {
+    Result = Registry->ReadBool(AppsUseLightThemeValue) ? 0 : 1;
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::ResetSysDarkTheme()
+{
+  FSysDarkTheme = -1;
+}
+//---------------------------------------------------------------------------
+bool __fastcall TWinConfiguration::UseDarkTheme()
+{
+  if (FSysDarkTheme < 0)
+  {
+    FSysDarkTheme = SysDarkTheme(HKEY_CURRENT_USER);
+    if (FSysDarkTheme < 0)
+    {
+      FSysDarkTheme = SysDarkTheme(HKEY_LOCAL_MACHINE);
+      if (FSysDarkTheme < 0)
+      {
+        FSysDarkTheme = 0;
+      }
+    }
+  }
+
+  switch (WinConfiguration->DarkTheme)
+  {
+    case asOn:
+      return true;
+    case asOff:
+      return false;
+    default:
+      return (FSysDarkTheme > 0);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetLastStoredSession(UnicodeString value)
 {
   SET_CONFIG_PROPERTY(LastStoredSession);
@@ -2723,6 +2777,7 @@ void __fastcall TWinConfiguration::UpdateStaticUsage()
   Usage->Set(L"Beta", IsBeta);
 
   Usage->Set(L"Interface", Interface);
+  Usage->Set(L"ThemeDark", UseDarkTheme());
   Usage->Set(L"CustomCommandsCount", (FCustomCommandsDefaults ? 0 : FCustomCommandList->Count));
   Usage->Set(L"UsingLocationProfiles", UseLocationProfiles);
   Usage->Set(L"UsingMasterPassword", UseMasterPassword);

+ 6 - 0
source/windows/WinConfiguration.h

@@ -399,6 +399,8 @@ private:
   UnicodeString FDefaultTranslationFile;
   UnicodeString FInvalidDefaultTranslationMessage;
   bool FPreservePanelState;
+  TAutoSwitch FDarkTheme;
+  int FSysDarkTheme;
   UnicodeString FLastStoredSession;
   UnicodeString FLastWorkspace;
   bool FAutoSaveWorkspace;
@@ -502,6 +504,7 @@ private:
   void __fastcall SetTemporaryDirectoryCleanup(bool value);
   void __fastcall SetConfirmTemporaryDirectoryCleanup(bool value);
   void __fastcall SetPreservePanelState(bool value);
+  void __fastcall SetDarkTheme(TAutoSwitch value);
   void __fastcall SetLastStoredSession(UnicodeString value);
   void __fastcall SetAutoSaveWorkspace(bool value);
   void __fastcall SetAutoSaveWorkspacePasswords(bool value);
@@ -635,6 +638,8 @@ public:
   UnicodeString __fastcall GetProvisionaryExtensionId(const UnicodeString & FileName);
   bool __fastcall IsDDExtRunning();
   bool __fastcall IsDDExtBroken();
+  bool __fastcall UseDarkTheme();
+  void __fastcall ResetSysDarkTheme();
 
   static void __fastcall RestoreFont(const TFontConfiguration & Configuration, TFont * Font);
   static void __fastcall StoreFont(TFont * Font, TFontConfiguration & Configuration);
@@ -688,6 +693,7 @@ public:
   __property bool TemporaryDirectoryCleanup = { read = FTemporaryDirectoryCleanup, write = SetTemporaryDirectoryCleanup };
   __property bool ConfirmTemporaryDirectoryCleanup = { read = FConfirmTemporaryDirectoryCleanup, write = SetConfirmTemporaryDirectoryCleanup };
   __property bool PreservePanelState = { read = FPreservePanelState, write = SetPreservePanelState };
+  __property TAutoSwitch DarkTheme = { read = FDarkTheme, write = SetDarkTheme };
   __property UnicodeString LastStoredSession = { read = FLastStoredSession, write = SetLastStoredSession };
   __property UnicodeString LastWorkspace = { read = FLastWorkspace, write = FLastWorkspace };
   __property bool AutoSaveWorkspace = { read = FAutoSaveWorkspace, write = SetAutoSaveWorkspace };