Add Dark Mode for not attracting bugs

Toolbar, status bar, tab bar, menu bar, dark scrollbar, handle switching between light and dark, docking splitters and headers, flicker fixes. Minor tweaks to DarkMode.h. Mostly handles the dark mode request for the main interface for #7692 and also follows the windows system theme settings like #9183.

Fix #9183, fix #7692, close #9587
This commit is contained in:
Adam D. Walling 2021-02-22 08:55:45 -05:00 committed by Don HO
parent acdc2517c6
commit 5a3bf49c86
33 changed files with 1909 additions and 50 deletions

1
.gitignore vendored
View File

@ -85,6 +85,7 @@ PowerEditor/installer/zipped.package.releaseArm64/
PowerEditor/visual.net/Unicode Debug/
PowerEditor/visual.net/Unicode Release/
PowerEditor/visual.net/notepadPlus.sln
PowerEditor/visual.net/notepadPlus.vcxproj.filters
PowerEditor/visual.net/x64/Unicode Debug/
PowerEditor/visual.net/x64/Unicode Release/
PowerEditor/visual.net/ARM64

View File

@ -250,7 +250,7 @@ CXX = $(CROSS_COMPILE)g++
CXXFLAGS = $(INCLUDESPECIAL) -D_WIN32_WINNT=0x0600 -DTIXML_USE_STL -DTIXMLA_USE_STL $(UNICODE) -std=c++17 -fpermissive
INCLUDES = $(patsubst %,-I%,$(DIRS)) -I./include
LDFLAGS = -Wl,--subsystem,windows -municode -mwindows
LIBS = -lcomdlg32 -lcomctl32 -lgdi32 -lole32 -loleacc -lshell32 -lshlwapi -ldbghelp -lversion -lcrypt32 -lsensapi -lwininet -lwintrust -luuid
LIBS = -lcomdlg32 -lcomctl32 -lgdi32 -lole32 -loleacc -lshell32 -lshlwapi -ldbghelp -lversion -lcrypt32 -lsensapi -lwininet -lwintrust -luuid -luxtheme
LIBSCILEX = -L$(SCI_DIR)/../bin -lscilexer -loleaut32 -limm32 -lmsimg32
RC = $(CROSS_COMPILE)windres

View File

@ -0,0 +1,291 @@
#pragma once
#include <Windows.h>
#include "DarkMode.h"
#include "IatHook.h"
#include <Uxtheme.h>
#include <Vssym32.h>
#include <unordered_set>
#include <mutex>
enum IMMERSIVE_HC_CACHE_MODE
{
IHCM_USE_CACHED_VALUE,
IHCM_REFRESH
};
// 1903 18362
enum PreferredAppMode
{
Default,
AllowDark,
ForceDark,
ForceLight,
Max
};
enum WINDOWCOMPOSITIONATTRIB
{
WCA_UNDEFINED = 0,
WCA_NCRENDERING_ENABLED = 1,
WCA_NCRENDERING_POLICY = 2,
WCA_TRANSITIONS_FORCEDISABLED = 3,
WCA_ALLOW_NCPAINT = 4,
WCA_CAPTION_BUTTON_BOUNDS = 5,
WCA_NONCLIENT_RTL_LAYOUT = 6,
WCA_FORCE_ICONIC_REPRESENTATION = 7,
WCA_EXTENDED_FRAME_BOUNDS = 8,
WCA_HAS_ICONIC_BITMAP = 9,
WCA_THEME_ATTRIBUTES = 10,
WCA_NCRENDERING_EXILED = 11,
WCA_NCADORNMENTINFO = 12,
WCA_EXCLUDED_FROM_LIVEPREVIEW = 13,
WCA_VIDEO_OVERLAY_ACTIVE = 14,
WCA_FORCE_ACTIVEWINDOW_APPEARANCE = 15,
WCA_DISALLOW_PEEK = 16,
WCA_CLOAK = 17,
WCA_CLOAKED = 18,
WCA_ACCENT_POLICY = 19,
WCA_FREEZE_REPRESENTATION = 20,
WCA_EVER_UNCLOAKED = 21,
WCA_VISUAL_OWNER = 22,
WCA_HOLOGRAPHIC = 23,
WCA_EXCLUDED_FROM_DDA = 24,
WCA_PASSIVEUPDATEMODE = 25,
WCA_USEDARKMODECOLORS = 26,
WCA_LAST = 27
};
struct WINDOWCOMPOSITIONATTRIBDATA
{
WINDOWCOMPOSITIONATTRIB Attrib;
PVOID pvData;
SIZE_T cbData;
};
using fnRtlGetNtVersionNumbers = void (WINAPI *)(LPDWORD major, LPDWORD minor, LPDWORD build);
using fnSetWindowCompositionAttribute = BOOL (WINAPI *)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*);
// 1809 17763
using fnShouldAppsUseDarkMode = bool (WINAPI *)(); // ordinal 132
using fnAllowDarkModeForWindow = bool (WINAPI *)(HWND hWnd, bool allow); // ordinal 133
using fnAllowDarkModeForApp = bool (WINAPI *)(bool allow); // ordinal 135, in 1809
using fnFlushMenuThemes = void (WINAPI *)(); // ordinal 136
using fnRefreshImmersiveColorPolicyState = void (WINAPI *)(); // ordinal 104
using fnIsDarkModeAllowedForWindow = bool (WINAPI *)(HWND hWnd); // ordinal 137
using fnGetIsImmersiveColorUsingHighContrast = bool (WINAPI *)(IMMERSIVE_HC_CACHE_MODE mode); // ordinal 106
using fnOpenNcThemeData = HTHEME(WINAPI *)(HWND hWnd, LPCWSTR pszClassList); // ordinal 49
// 1903 18362
using fnShouldSystemUseDarkMode = bool (WINAPI *)(); // ordinal 138
using fnSetPreferredAppMode = PreferredAppMode (WINAPI *)(PreferredAppMode appMode); // ordinal 135, in 1903
using fnIsDarkModeAllowedForApp = bool (WINAPI *)(); // ordinal 139
fnSetWindowCompositionAttribute _SetWindowCompositionAttribute = nullptr;
fnShouldAppsUseDarkMode _ShouldAppsUseDarkMode = nullptr;
fnAllowDarkModeForWindow _AllowDarkModeForWindow = nullptr;
fnAllowDarkModeForApp _AllowDarkModeForApp = nullptr;
fnFlushMenuThemes _FlushMenuThemes = nullptr;
fnRefreshImmersiveColorPolicyState _RefreshImmersiveColorPolicyState = nullptr;
fnIsDarkModeAllowedForWindow _IsDarkModeAllowedForWindow = nullptr;
fnGetIsImmersiveColorUsingHighContrast _GetIsImmersiveColorUsingHighContrast = nullptr;
fnOpenNcThemeData _OpenNcThemeData = nullptr;
// 1903 18362
fnShouldSystemUseDarkMode _ShouldSystemUseDarkMode = nullptr;
fnSetPreferredAppMode _SetPreferredAppMode = nullptr;
bool g_darkModeSupported = false;
bool g_darkModeEnabled = false;
DWORD g_buildNumber = 0;
bool ShouldAppsUseDarkMode()
{
if (!_ShouldAppsUseDarkMode)
{
return false;
}
return _ShouldAppsUseDarkMode();
}
bool AllowDarkModeForWindow(HWND hWnd, bool allow)
{
if (g_darkModeSupported)
return _AllowDarkModeForWindow(hWnd, allow);
return false;
}
bool IsHighContrast()
{
HIGHCONTRASTW highContrast = { sizeof(highContrast) };
if (SystemParametersInfoW(SPI_GETHIGHCONTRAST, sizeof(highContrast), &highContrast, FALSE))
return highContrast.dwFlags & HCF_HIGHCONTRASTON;
return false;
}
void RefreshTitleBarThemeColor(HWND hWnd)
{
BOOL dark = FALSE;
if (_IsDarkModeAllowedForWindow(hWnd) &&
_ShouldAppsUseDarkMode() &&
!IsHighContrast())
{
dark = TRUE;
}
if (g_buildNumber < 18362)
SetPropW(hWnd, L"UseImmersiveDarkModeColors", reinterpret_cast<HANDLE>(static_cast<INT_PTR>(dark)));
else if (_SetWindowCompositionAttribute)
{
WINDOWCOMPOSITIONATTRIBDATA data = { WCA_USEDARKMODECOLORS, &dark, sizeof(dark) };
_SetWindowCompositionAttribute(hWnd, &data);
}
}
bool IsColorSchemeChangeMessage(LPARAM lParam)
{
bool is = false;
if (lParam && (0 == lstrcmpi(reinterpret_cast<LPCWCH>(lParam), L"ImmersiveColorSet")))
{
_RefreshImmersiveColorPolicyState();
is = true;
}
_GetIsImmersiveColorUsingHighContrast(IHCM_REFRESH);
return is;
}
bool IsColorSchemeChangeMessage(UINT message, LPARAM lParam)
{
if (message == WM_SETTINGCHANGE)
return IsColorSchemeChangeMessage(lParam);
return false;
}
void AllowDarkModeForApp(bool allow)
{
if (_AllowDarkModeForApp)
_AllowDarkModeForApp(allow);
else if (_SetPreferredAppMode)
_SetPreferredAppMode(allow ? AllowDark : Default);
}
// limit dark scroll bar to specific windows and their children
std::unordered_set<HWND> g_darkScrollBarWindows;
std::mutex g_darkScrollBarMutex;
void EnableDarkScrollBarForWindowAndChildren(HWND hwnd)
{
std::lock_guard<std::mutex> lock(g_darkScrollBarMutex);
g_darkScrollBarWindows.insert(hwnd);
}
bool IsWindowOrParentUsingDarkScrollBar(HWND hwnd)
{
HWND hwndRoot = GetAncestor(hwnd, GA_ROOT);
std::lock_guard<std::mutex> lock(g_darkScrollBarMutex);
if (g_darkScrollBarWindows.count(hwnd)) {
return true;
}
if (hwnd != hwndRoot && g_darkScrollBarWindows.count(hwndRoot)) {
return true;
}
return false;
}
void FixDarkScrollBar()
{
HMODULE hComctl = LoadLibraryExW(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hComctl)
{
auto addr = FindDelayLoadThunkInModule(hComctl, "uxtheme.dll", 49); // OpenNcThemeData
if (addr)
{
DWORD oldProtect;
if (VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), PAGE_READWRITE, &oldProtect))
{
auto MyOpenThemeData = [](HWND hWnd, LPCWSTR classList) -> HTHEME {
if (wcscmp(classList, L"ScrollBar") == 0)
{
if (IsWindowOrParentUsingDarkScrollBar(hWnd)) {
hWnd = nullptr;
classList = L"Explorer::ScrollBar";
}
}
return _OpenNcThemeData(hWnd, classList);
};
addr->u1.Function = reinterpret_cast<ULONG_PTR>(static_cast<fnOpenNcThemeData>(MyOpenThemeData));
VirtualProtect(addr, sizeof(IMAGE_THUNK_DATA), oldProtect, &oldProtect);
}
}
}
}
constexpr bool CheckBuildNumber(DWORD buildNumber)
{
return (buildNumber == 17763 || // 1809
buildNumber == 18362 || // 1903
buildNumber == 18363 || // 1909
buildNumber == 19041 || // 2004
buildNumber == 19042 || // 20H2
buildNumber == 19043); // 21H1
}
void InitDarkMode(bool fixDarkScrollbar)
{
auto RtlGetNtVersionNumbers = reinterpret_cast<fnRtlGetNtVersionNumbers>(GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "RtlGetNtVersionNumbers"));
if (RtlGetNtVersionNumbers)
{
DWORD major, minor;
RtlGetNtVersionNumbers(&major, &minor, &g_buildNumber);
g_buildNumber &= ~0xF0000000;
if (major == 10 && minor == 0 && CheckBuildNumber(g_buildNumber))
{
HMODULE hUxtheme = LoadLibraryExW(L"uxtheme.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
if (hUxtheme)
{
_OpenNcThemeData = reinterpret_cast<fnOpenNcThemeData>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(49)));
_RefreshImmersiveColorPolicyState = reinterpret_cast<fnRefreshImmersiveColorPolicyState>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(104)));
_GetIsImmersiveColorUsingHighContrast = reinterpret_cast<fnGetIsImmersiveColorUsingHighContrast>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(106)));
_ShouldAppsUseDarkMode = reinterpret_cast<fnShouldAppsUseDarkMode>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(132)));
_AllowDarkModeForWindow = reinterpret_cast<fnAllowDarkModeForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(133)));
auto ord135 = GetProcAddress(hUxtheme, MAKEINTRESOURCEA(135));
if (g_buildNumber < 18362)
_AllowDarkModeForApp = reinterpret_cast<fnAllowDarkModeForApp>(ord135);
else
_SetPreferredAppMode = reinterpret_cast<fnSetPreferredAppMode>(ord135);
//_FlushMenuThemes = reinterpret_cast<fnFlushMenuThemes>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(136)));
_IsDarkModeAllowedForWindow = reinterpret_cast<fnIsDarkModeAllowedForWindow>(GetProcAddress(hUxtheme, MAKEINTRESOURCEA(137)));
_SetWindowCompositionAttribute = reinterpret_cast<fnSetWindowCompositionAttribute>(GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetWindowCompositionAttribute"));
if (_OpenNcThemeData &&
_RefreshImmersiveColorPolicyState &&
_ShouldAppsUseDarkMode &&
_AllowDarkModeForWindow &&
(_AllowDarkModeForApp || _SetPreferredAppMode) &&
//_FlushMenuThemes &&
_IsDarkModeAllowedForWindow)
{
g_darkModeSupported = true;
AllowDarkModeForApp(true);
_RefreshImmersiveColorPolicyState();
g_darkModeEnabled = _ShouldAppsUseDarkMode() && !IsHighContrast();
if (fixDarkScrollbar)
{
FixDarkScrollBar();
}
}
}
}
}
}

View File

@ -0,0 +1,16 @@
#pragma once
extern bool g_darkModeSupported;
extern bool g_darkModeEnabled;
bool ShouldAppsUseDarkMode();
bool AllowDarkModeForWindow(HWND hWnd, bool allow);
bool IsHighContrast();
void RefreshTitleBarThemeColor(HWND hWnd);
bool IsColorSchemeChangeMessage(LPARAM lParam);
bool IsColorSchemeChangeMessage(UINT message, LPARAM lParam);
void AllowDarkModeForApp(bool allow);
void EnableDarkScrollBarForWindowAndChildren(HWND hwnd);
void InitDarkMode(bool fixDarkScrollbar);

View File

@ -0,0 +1,94 @@
// This file contains code from
// https://github.com/stevemk14ebr/PolyHook_2_0/blob/master/sources/IatHook.cpp
// which is licensed under the MIT License.
// See PolyHook_2_0-LICENSE for more information.
#pragma once
#include <stdint.h>
template <typename T, typename T1, typename T2>
constexpr T RVA2VA(T1 base, T2 rva)
{
return reinterpret_cast<T>(reinterpret_cast<ULONG_PTR>(base) + rva);
}
template <typename T>
constexpr T DataDirectoryFromModuleBase(void *moduleBase, size_t entryID)
{
auto dosHdr = reinterpret_cast<PIMAGE_DOS_HEADER>(moduleBase);
auto ntHdr = RVA2VA<PIMAGE_NT_HEADERS>(moduleBase, dosHdr->e_lfanew);
auto dataDir = ntHdr->OptionalHeader.DataDirectory;
return RVA2VA<T>(moduleBase, dataDir[entryID].VirtualAddress);
}
PIMAGE_THUNK_DATA FindAddressByName(void *moduleBase, PIMAGE_THUNK_DATA impName, PIMAGE_THUNK_DATA impAddr, const char *funcName)
{
for (; impName->u1.Ordinal; ++impName, ++impAddr)
{
if (IMAGE_SNAP_BY_ORDINAL(impName->u1.Ordinal))
continue;
auto import = RVA2VA<PIMAGE_IMPORT_BY_NAME>(moduleBase, impName->u1.AddressOfData);
if (strcmp(import->Name, funcName) != 0)
continue;
return impAddr;
}
return nullptr;
}
PIMAGE_THUNK_DATA FindAddressByOrdinal(void *moduleBase, PIMAGE_THUNK_DATA impName, PIMAGE_THUNK_DATA impAddr, uint16_t ordinal)
{
UNREFERENCED_PARAMETER(moduleBase);
for (; impName->u1.Ordinal; ++impName, ++impAddr)
{
if (IMAGE_SNAP_BY_ORDINAL(impName->u1.Ordinal) && IMAGE_ORDINAL(impName->u1.Ordinal) == ordinal)
return impAddr;
}
return nullptr;
}
PIMAGE_THUNK_DATA FindIatThunkInModule(void *moduleBase, const char *dllName, const char *funcName)
{
auto imports = DataDirectoryFromModuleBase<PIMAGE_IMPORT_DESCRIPTOR>(moduleBase, IMAGE_DIRECTORY_ENTRY_IMPORT);
for (; imports->Name; ++imports)
{
if (_stricmp(RVA2VA<LPCSTR>(moduleBase, imports->Name), dllName) != 0)
continue;
auto origThunk = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->OriginalFirstThunk);
auto thunk = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->FirstThunk);
return FindAddressByName(moduleBase, origThunk, thunk, funcName);
}
return nullptr;
}
PIMAGE_THUNK_DATA FindDelayLoadThunkInModule(void *moduleBase, const char *dllName, const char *funcName)
{
auto imports = DataDirectoryFromModuleBase<PIMAGE_DELAYLOAD_DESCRIPTOR>(moduleBase, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
for (; imports->DllNameRVA; ++imports)
{
if (_stricmp(RVA2VA<LPCSTR>(moduleBase, imports->DllNameRVA), dllName) != 0)
continue;
auto impName = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportNameTableRVA);
auto impAddr = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportAddressTableRVA);
return FindAddressByName(moduleBase, impName, impAddr, funcName);
}
return nullptr;
}
PIMAGE_THUNK_DATA FindDelayLoadThunkInModule(void *moduleBase, const char *dllName, uint16_t ordinal)
{
auto imports = DataDirectoryFromModuleBase<PIMAGE_DELAYLOAD_DESCRIPTOR>(moduleBase, IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT);
for (; imports->DllNameRVA; ++imports)
{
if (_stricmp(RVA2VA<LPCSTR>(moduleBase, imports->DllNameRVA), dllName) != 0)
continue;
auto impName = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportNameTableRVA);
auto impAddr = RVA2VA<PIMAGE_THUNK_DATA>(moduleBase, imports->ImportAddressTableRVA);
return FindAddressByOrdinal(moduleBase, impName, impAddr, ordinal);
}
return nullptr;
}

View File

@ -0,0 +1,74 @@
#pragma once
// MIT license, see LICENSE
// Copyright(c) 2021 adzm / Adam D. Walling
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool UAHDarkModeWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr);
// window messages related to menu bar drawing
#define WM_UAHDESTROYWINDOW 0x0090 // handled by DefWindowProc
#define WM_UAHDRAWMENU 0x0091 // lParam is UAHMENU
#define WM_UAHDRAWMENUITEM 0x0092 // lParam is UAHDRAWMENUITEM
#define WM_UAHINITMENU 0x0093 // handled by DefWindowProc
#define WM_UAHMEASUREMENUITEM 0x0094 // lParam is UAHMEASUREMENUITEM
#define WM_UAHNCPAINTMENUPOPUP 0x0095 // handled by DefWindowProc
// describes the sizes of the menu bar or menu item
typedef union tagUAHMENUITEMMETRICS
{
// cx appears to be 14 / 0xE less than rcItem's width!
// cy 0x14 seems stable, i wonder if it is 4 less than rcItem's height which is always 24 atm
struct {
DWORD cx;
DWORD cy;
} rgsizeBar[2];
struct {
DWORD cx;
DWORD cy;
} rgsizePopup[4];
} UAHMENUITEMMETRICS;
// not really used in our case but part of the other structures
typedef struct tagUAHMENUPOPUPMETRICS
{
DWORD rgcx[4];
DWORD fUpdateMaxWidths : 2; // from kernel symbols, padded to full dword
} UAHMENUPOPUPMETRICS;
// hmenu is the main window menu; hdc is the context to draw in
typedef struct tagUAHMENU
{
HMENU hmenu;
HDC hdc;
DWORD dwFlags; // no idea what these mean, in my testing it's either 0x00000a00 or sometimes 0x00000a10
} UAHMENU;
// menu items are always referred to by iPosition here
typedef struct tagUAHMENUITEM
{
int iPosition; // 0-based position of menu item in menubar
UAHMENUITEMMETRICS umim;
UAHMENUPOPUPMETRICS umpm;
} UAHMENUITEM;
// the DRAWITEMSTRUCT contains the states of the menu items, as well as
// the position index of the item in the menu, which is duplicated in
// the UAHMENUITEM's iPosition as well
typedef struct UAHDRAWMENUITEM
{
DRAWITEMSTRUCT dis; // itemID looks uninitialized
UAHMENU um;
UAHMENUITEM umi;
} UAHDRAWMENUITEM;
// the MEASUREITEMSTRUCT is intended to be filled with the size of the item
// height appears to be ignored, but width can be modified
typedef struct tagUAHMEASUREMENUITEM
{
MEASUREITEMSTRUCT mis;
UAHMENU um;
UAHMENUITEM umi;
} UAHMEASUREMENUITEM;

View File

@ -39,6 +39,7 @@
#include "functionListPanel.h"
#include "fileBrowser.h"
#include "Common.h"
#include "NppDarkMode.h"
using namespace std;
@ -280,8 +281,16 @@ LRESULT Notepad_plus::init(HWND hwnd)
_subEditView.execute(SCI_SETFONTQUALITY, SC_EFF_QUALITY_LCD_OPTIMIZED);
}
if (NppDarkMode::isEnabled())
{
_mainEditView.setBorderEdge(false);
_subEditView.setBorderEdge(false);
}
else
{
_mainEditView.setBorderEdge(svp._showBorderEdge);
_subEditView.setBorderEdge(svp._showBorderEdge);
}
_mainEditView.execute(SCI_SETCARETLINEVISIBLEALWAYS, true);
_subEditView.execute(SCI_SETCARETLINEVISIBLEALWAYS, true);
@ -7491,6 +7500,12 @@ void Notepad_plus::restoreMinimizeDialogs()
}
}
void Notepad_plus::refreshDarkMode()
{
SendMessage(_pPublicInterface->getHSelf(), NPPM_SETEDITORBORDEREDGE, 0, NppParameters::getInstance().getSVP()._showBorderEdge);
RedrawWindow(_pPublicInterface->getHSelf(), nullptr, nullptr, RDW_INVALIDATE | RDW_ERASE | RDW_FRAME | RDW_ALLCHILDREN);
}
void Notepad_plus::launchDocumentBackupTask()
{
HANDLE hThread = ::CreateThread(NULL, 0, backupDocument, NULL, 0, NULL);

View File

@ -263,6 +263,8 @@ public:
void minimizeDialogs();
void restoreMinimizeDialogs();
void refreshDarkMode();
private:
Notepad_plus_Window *_pPublicInterface = nullptr;
Window *_pMainWindow = nullptr;

View File

@ -28,6 +28,7 @@
#include "documentMap.h"
#include "functionListPanel.h"
#include "fileBrowser.h"
#include "NppDarkMode.h"
using namespace std;
@ -68,6 +69,12 @@ LRESULT CALLBACK Notepad_plus_Window::Notepad_plus_Proc(HWND hwnd, UINT message,
Notepad_plus_Window *pM30ide = static_cast<Notepad_plus_Window *>((reinterpret_cast<LPCREATESTRUCT>(lParam))->lpCreateParams);
pM30ide->_hSelf = hwnd;
::SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pM30ide));
if (NppDarkMode::isExperimentalEnabled() && NppDarkMode::isScrollbarHackEnabled())
{
NppDarkMode::enableDarkScrollBarForWindowAndChildren(hwnd);
}
return TRUE;
}
@ -87,8 +94,29 @@ LRESULT Notepad_plus_Window::runProc(HWND hwnd, UINT message, WPARAM wParam, LPA
{
try
{
if (NppDarkMode::isExperimentalEnabled())
{
NppDarkMode::allowDarkModeForWindow(hwnd, true);
NppDarkMode::refreshTitleBarThemeColor(hwnd);
}
_notepad_plus_plus_core._pPublicInterface = this;
return _notepad_plus_plus_core.init(hwnd);
LRESULT lRet = _notepad_plus_plus_core.init(hwnd);
if (NppDarkMode::isExperimentalEnabled())
{
RECT rcClient;
GetWindowRect(hwnd, &rcClient);
// Inform application of the frame change.
SetWindowPos(hwnd,
NULL,
rcClient.left, rcClient.top,
rcClient.right - rcClient.left, rcClient.bottom - rcClient.top,
SWP_FRAMECHANGED);
}
return lRet;
}
catch (std::exception& ex)
{
@ -122,6 +150,11 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
LRESULT result = FALSE;
NppParameters& nppParam = NppParameters::getInstance();
if (NppDarkMode::isDarkMenuEnabled() && NppDarkMode::runUAHWndProc(hwnd, message, wParam, lParam, &result))
{
return result;
}
switch (message)
{
case WM_NCACTIVATE:
@ -131,6 +164,38 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
case WM_ERASEBKGND:
{
if (NppDarkMode::isEnabled())
{
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getBackgroundBrush());
return 0;
}
else
{
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
}
case WM_SETTINGCHANGE:
{
if (NppDarkMode::handleSettingChange(hwnd, lParam))
{
// dark mode may have been toggled by the OS
NppDarkMode::refreshDarkMode(hwnd, true);
}
return ::DefWindowProc(hwnd, message, wParam, lParam);
}
case NPPM_INTERNAL_REFRESHDARKMODE:
{
refreshDarkMode();
return TRUE;
}
case WM_DRAWITEM:
{
DRAWITEMSTRUCT *dis = reinterpret_cast<DRAWITEMSTRUCT *>(lParam);
@ -1439,8 +1504,16 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
case NPPM_SETEDITORBORDEREDGE:
{
bool withBorderEdge = (lParam == 1);
if (NppDarkMode::isEnabled())
{
_mainEditView.setBorderEdge(false);
_subEditView.setBorderEdge(false);
}
else
{
_mainEditView.setBorderEdge(withBorderEdge);
_subEditView.setBorderEdge(withBorderEdge);
}
return TRUE;
}
@ -1545,6 +1618,26 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
case WM_NOTIFY:
{
NMHDR* nmhdr = reinterpret_cast<NMHDR*>(lParam);
if (nmhdr->code == NM_CUSTOMDRAW && (nmhdr->hwndFrom == _toolBar.getHSelf()))
{
NMTBCUSTOMDRAW* nmtbcd = reinterpret_cast<NMTBCUSTOMDRAW*>(lParam);
if (nmtbcd->nmcd.dwDrawStage == CDDS_PREERASE)
{
if (NppDarkMode::isEnabled())
{
FillRect(nmtbcd->nmcd.hdc, &nmtbcd->nmcd.rc, NppDarkMode::getPureBackgroundBrush());
nmtbcd->clrText = NppDarkMode::getTextColor();
SetTextColor(nmtbcd->nmcd.hdc, NppDarkMode::getTextColor());
return CDRF_SKIPDEFAULT;
}
else
{
return CDRF_DODEFAULT;
}
}
}
SCNotification *notification = reinterpret_cast<SCNotification *>(lParam);
if (notification->nmhdr.code == SCN_UPDATEUI)
@ -1667,7 +1760,7 @@ LRESULT Notepad_plus::process(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
case WM_ACTIVATE:
{
if (wParam != WA_INACTIVE)
if (wParam != WA_INACTIVE && _pEditView && _pNonEditView)
{
_pEditView->getFocus();
auto x = _pEditView->execute(SCI_GETXOFFSET);

View File

@ -0,0 +1,608 @@
#include "nppDarkMode.h"
#include "DarkMode/DarkMode.h"
#include "DarkMode/UAHMenuBar.h"
#include <Uxtheme.h>
#include <Vssym32.h>
#include "Parameters.h"
#include "resource.h"
#include <Shlwapi.h>
#pragma comment(lib, "uxtheme.lib")
namespace NppDarkMode
{
static Options _options; // actual runtime options
const Options& configuredOptions()
{
return NppParameters::getInstance().getNppGUI()._darkmode;
}
void initDarkMode()
{
_options = configuredOptions();
if (_options.enableExperimental)
{
initExperimentalDarkMode(_options.enableScrollbarHack);
}
}
// attempts to apply new options from NppParameters, sends NPPM_INTERNAL_REFRESHDARKMODE to hwnd's top level parent
void refreshDarkMode(HWND hwnd, bool forceRefresh)
{
bool supportedChanged = false;
auto& config = configuredOptions();
if (_options.enable != config.enable)
{
supportedChanged = true;
_options.enable = config.enable;
}
if (_options.enableMenubar != config.enableMenubar)
{
supportedChanged = true;
_options.enableMenubar = config.enableMenubar;
}
// other options not supported to change at runtime currently
if (!supportedChanged && !forceRefresh)
{
// nothing to refresh, changes were not supported.
return;
}
HWND hwndRoot = GetAncestor(hwnd, GA_ROOTOWNER);
::SendMessage(hwndRoot, NPPM_INTERNAL_REFRESHDARKMODE, 0, 0);
}
bool isEnabled()
{
return _options.enable;
}
bool isDarkMenuEnabled()
{
return _options.enableMenubar;
}
bool isExperimentalEnabled()
{
return _options.enableExperimental;
}
bool isScrollbarHackEnabled()
{
return _options.enableScrollbarHack;
}
bool isExperimentalActive()
{
return g_darkModeEnabled;
}
bool isExperimentalSupported()
{
return g_darkModeSupported;
}
COLORREF invertLightness(COLORREF c)
{
WORD h = 0;
WORD s = 0;
WORD l = 0;
ColorRGBToHLS(c, &h, &l, &s);
l = 240 - l;
COLORREF invert_c = ColorHLSToRGB(h, l, s);
return invert_c;
}
COLORREF invertLightnessSofter(COLORREF c)
{
WORD h = 0;
WORD s = 0;
WORD l = 0;
ColorRGBToHLS(c, &h, &l, &s);
l = min(240 - l, 211);
COLORREF invert_c = ColorHLSToRGB(h, l, s);
return invert_c;
}
COLORREF getBackgroundColor()
{
return RGB(0x20, 0x20, 0x20);
}
COLORREF getSofterBackgroundColor()
{
return RGB(0x2B, 0x2B, 0x2B);
}
COLORREF getHotBackgroundColor()
{
return RGB(0x4D, 0x4D, 0x4D);
}
COLORREF getPureBackgroundColor()
{
return RGB(0, 0, 0);
}
COLORREF getTextColor()
{
return RGB(0xE0, 0xE0, 0xE0);
}
COLORREF getDarkerTextColor()
{
return RGB(0xC0, 0xC0, 0xC0);
}
COLORREF getEdgeColor()
{
return RGB(0x80, 0x80, 0x80);
}
COLORREF getErrorBackgroundColor()
{
return RGB(0xB0, 0x00, 0x00);
}
HBRUSH getBackgroundBrush()
{
static HBRUSH g_hbrBackground = ::CreateSolidBrush(getBackgroundColor());
return g_hbrBackground;
}
HBRUSH getSofterBackgroundBrush()
{
static HBRUSH g_hbrSofterBackground = ::CreateSolidBrush(getSofterBackgroundColor());
return g_hbrSofterBackground;
}
HBRUSH getHotBackgroundBrush()
{
static HBRUSH g_hbrHotBackground = ::CreateSolidBrush(getHotBackgroundColor());
return g_hbrHotBackground;
}
HBRUSH getPureBackgroundBrush()
{
static HBRUSH g_hbrPureBackground = (HBRUSH)::GetStockObject(BLACK_BRUSH);
return g_hbrPureBackground;
}
HBRUSH getErrorBackgroundBrush()
{
static HBRUSH g_hbrErrorBackground = ::CreateSolidBrush(getErrorBackgroundColor());
return g_hbrErrorBackground;
}
// handle events
bool handleSettingChange(HWND hwnd, LPARAM lParam) // true if dark mode toggled
{
if (!isExperimentalEnabled())
{
return false;
}
bool toggled = false;
if (IsColorSchemeChangeMessage(lParam))
{
bool darkModeWasEnabled = g_darkModeEnabled;
g_darkModeEnabled = ShouldAppsUseDarkMode() && !IsHighContrast();
NppDarkMode::refreshTitleBarThemeColor(hwnd);
if (!!darkModeWasEnabled != !!g_darkModeEnabled)
{
toggled = true;
}
}
return toggled;
}
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool runUAHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr)
{
static HTHEME g_menuTheme = nullptr;
UNREFERENCED_PARAMETER(wParam);
switch (message)
{
case WM_UAHDRAWMENU:
{
UAHMENU* pUDM = (UAHMENU*)lParam;
RECT rc = { 0 };
// get the menubar rect
{
MENUBARINFO mbi = { sizeof(mbi) };
GetMenuBarInfo(hWnd, OBJID_MENU, 0, &mbi);
RECT rcWindow;
GetWindowRect(hWnd, &rcWindow);
// the rcBar is offset by the window rect
rc = mbi.rcBar;
OffsetRect(&rc, -rcWindow.left, -rcWindow.top);
rc.top -= 1;
}
FillRect(pUDM->hdc, &rc, NppDarkMode::getPureBackgroundBrush());
*lr = 0;
return true;
}
case WM_UAHDRAWMENUITEM:
{
UAHDRAWMENUITEM* pUDMI = (UAHDRAWMENUITEM*)lParam;
// get the menu item string
wchar_t menuString[256] = { 0 };
MENUITEMINFO mii = { sizeof(mii), MIIM_STRING };
{
mii.dwTypeData = menuString;
mii.cch = (sizeof(menuString) / 2) - 1;
GetMenuItemInfo(pUDMI->um.hmenu, pUDMI->umi.iPosition, TRUE, &mii);
}
// get the item state for drawing
DWORD dwFlags = DT_CENTER | DT_SINGLELINE | DT_VCENTER;
int iTextStateID = MPI_NORMAL;
int iBackgroundStateID = MPI_NORMAL;
{
if ((pUDMI->dis.itemState & ODS_INACTIVE) | (pUDMI->dis.itemState & ODS_DEFAULT))
{
// normal display
iTextStateID = MPI_NORMAL;
iBackgroundStateID = MPI_NORMAL;
}
if (pUDMI->dis.itemState & ODS_HOTLIGHT)
{
// hot tracking
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
}
if (pUDMI->dis.itemState & ODS_SELECTED)
{
// clicked -- MENU_POPUPITEM has no state for this, though MENU_BARITEM does
iTextStateID = MPI_HOT;
iBackgroundStateID = MPI_HOT;
}
if ((pUDMI->dis.itemState & ODS_GRAYED) || (pUDMI->dis.itemState & ODS_DISABLED))
{
// disabled / grey text
iTextStateID = MPI_DISABLED;
iBackgroundStateID = MPI_DISABLED;
}
if (pUDMI->dis.itemState & ODS_NOACCEL)
{
dwFlags |= DT_HIDEPREFIX;
}
}
if (!g_menuTheme)
{
g_menuTheme = OpenThemeData(hWnd, L"Menu");
}
if (iBackgroundStateID == MPI_NORMAL || iBackgroundStateID == MPI_DISABLED)
{
FillRect(pUDMI->um.hdc, &pUDMI->dis.rcItem, NppDarkMode::getPureBackgroundBrush());
}
else if (iBackgroundStateID == MPI_HOT || iBackgroundStateID == MPI_DISABLEDHOT)
{
FillRect(pUDMI->um.hdc, &pUDMI->dis.rcItem, NppDarkMode::getHotBackgroundBrush());
}
else
{
DrawThemeBackground(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iBackgroundStateID, &pUDMI->dis.rcItem, nullptr);
}
DTTOPTS dttopts = { sizeof(dttopts) };
if (iTextStateID == MPI_NORMAL || iTextStateID == MPI_HOT)
{
dttopts.dwFlags |= DTT_TEXTCOLOR;
dttopts.crText = NppDarkMode::getTextColor();
}
DrawThemeTextEx(g_menuTheme, pUDMI->um.hdc, MENU_POPUPITEM, iTextStateID, menuString, mii.cch, dwFlags, &pUDMI->dis.rcItem, &dttopts);
*lr = 0;
return true;
}
case WM_THEMECHANGED:
{
if (g_menuTheme)
{
CloseThemeData(g_menuTheme);
g_menuTheme = nullptr;
}
// continue processing in main wndproc
return false;
}
default:
return false;
}
}
// from DarkMode.h
void initExperimentalDarkMode(bool fixDarkScrollbar)
{
::InitDarkMode(fixDarkScrollbar);
}
void allowDarkModeForApp(bool allow)
{
::AllowDarkModeForApp(allow);
}
bool allowDarkModeForWindow(HWND hWnd, bool allow)
{
return ::AllowDarkModeForWindow(hWnd, allow);
}
void refreshTitleBarThemeColor(HWND hWnd)
{
::RefreshTitleBarThemeColor(hWnd);
}
void enableDarkScrollBarForWindowAndChildren(HWND hwnd)
{
::EnableDarkScrollBarForWindowAndChildren(hwnd);
}
struct ButtonData
{
HTHEME hTheme = nullptr;
~ButtonData()
{
closeTheme();
}
bool ensureTheme(HWND hwnd)
{
if (!hTheme)
{
hTheme = OpenThemeData(hwnd, L"Button");
}
return hTheme != nullptr;
}
void closeTheme()
{
if (hTheme)
{
CloseThemeData(hTheme);
hTheme = nullptr;
}
}
};
void paintButton(HWND hwnd, HDC hdc, ButtonData& buttonData)
{
RECT rcClient = { 0 };
WCHAR szText[256] = { 0 };
DWORD nState = static_cast<DWORD>(SendMessage(hwnd, BM_GETSTATE, 0, 0));
bool isEnabled = IsWindowEnabled(hwnd);;
DWORD uiState = static_cast<DWORD>(SendMessage(hwnd, WM_QUERYUISTATE, 0, 0));
DWORD nStyle = GetWindowLong(hwnd, GWL_STYLE);
DWORD nButtonStyle = nStyle & 0xF;
int iPartID = BP_CHECKBOX;
if (nButtonStyle == BS_CHECKBOX || nButtonStyle == BS_AUTOCHECKBOX)
{
iPartID = BP_CHECKBOX;
}
else if (nButtonStyle == BS_RADIOBUTTON || nButtonStyle == BS_AUTORADIOBUTTON)
{
iPartID = BP_RADIOBUTTON;
}
else
{
assert(false);
}
// states of BP_CHECKBOX and BP_RADIOBUTTON are the same
int iStateID = RBS_UNCHECKEDNORMAL;
if (!isEnabled) iStateID = RBS_UNCHECKEDDISABLED;
else if (nState & BST_PUSHED) iStateID = RBS_UNCHECKEDPRESSED;
else if (nState & BST_HOT) iStateID = RBS_UNCHECKEDHOT;
if (nState & BST_CHECKED) iStateID += 4;
HFONT hFont = nullptr;
HFONT hOldFont = nullptr;
HFONT hCreatedFont = nullptr;
LOGFONT lf = { 0 };
if (SUCCEEDED(GetThemeFont(buttonData.hTheme, hdc, iPartID, iStateID, TMT_FONT, &lf)))
{
hCreatedFont = CreateFontIndirect(&lf);
hFont = hCreatedFont;
}
if (!hFont) {
hFont = reinterpret_cast<HFONT>(SendMessage(hwnd, WM_GETFONT, 0, 0));
}
SelectObject(hdc, hFont);
DWORD dtFlags = DT_LEFT; // DT_LEFT is 0
dtFlags |= (nStyle & BS_MULTILINE) ? DT_WORDBREAK : DT_SINGLELINE;
dtFlags |= ((nStyle & BS_CENTER) == BS_CENTER) ? DT_CENTER : (nStyle & BS_RIGHT) ? DT_RIGHT : 0;
dtFlags |= ((nStyle & BS_VCENTER) == BS_VCENTER) ? DT_VCENTER : (nStyle & BS_BOTTOM) ? DT_BOTTOM : 0;
dtFlags |= (uiState & UISF_HIDEACCEL) ? DT_HIDEPREFIX : 0;
if (!(nStyle & BS_MULTILINE) && !(nStyle & BS_BOTTOM) && !(nStyle & BS_TOP))
{
dtFlags |= DT_VCENTER;
}
GetClientRect(hwnd, &rcClient);
GetWindowText(hwnd, szText, _countof(szText));
SIZE szBox = { 13, 13 };
GetThemePartSize(buttonData.hTheme, hdc, iPartID, iStateID, NULL, TS_DRAW, &szBox);
RECT rcText = rcClient;
GetThemeBackgroundContentRect(buttonData.hTheme, hdc, iPartID, iStateID, &rcClient, &rcText);
RECT rcBackground = rcClient;
if (dtFlags & DT_SINGLELINE)
{
rcBackground.top += (rcText.bottom - rcText.top - szBox.cy) / 2;
}
rcBackground.bottom = rcBackground.top + szBox.cy;
rcBackground.right = rcBackground.left + szBox.cx;
rcText.left = rcBackground.right + 3;
DrawThemeParentBackground(hwnd, hdc, &rcClient);
DrawThemeBackground(buttonData.hTheme, hdc, iPartID, iStateID, &rcBackground, nullptr);
DTTOPTS dtto = { sizeof(DTTOPTS), DTT_TEXTCOLOR };
dtto.crText = NppDarkMode::getTextColor();
DrawThemeTextEx(buttonData.hTheme, hdc, iPartID, iStateID, szText, -1, dtFlags, &rcText, &dtto);
if ((nState & BST_FOCUS) && !(uiState & UISF_HIDEFOCUS))
{
RECT rcTextOut = rcText;
dtto.dwFlags |= DTT_CALCRECT;
DrawThemeTextEx(buttonData.hTheme, hdc, iPartID, iStateID, szText, -1, dtFlags | DT_CALCRECT, &rcTextOut, &dtto);
RECT rcFocus = rcTextOut;
rcFocus.bottom++;
rcFocus.left--;
rcFocus.right++;
DrawFocusRect(hdc, &rcFocus);
}
if (hCreatedFont) DeleteObject(hCreatedFont);
SelectObject(hdc, hOldFont);
}
constexpr UINT_PTR g_buttonSubclassID = 42;
LRESULT CALLBACK ButtonSubclass(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
UNREFERENCED_PARAMETER(uIdSubclass);
auto pButtonData = reinterpret_cast<ButtonData*>(dwRefData);
switch (uMsg)
{
case WM_UPDATEUISTATE:
if (HIWORD(wParam) & (UISF_HIDEACCEL | UISF_HIDEFOCUS))
{
InvalidateRect(hWnd, nullptr, FALSE);
}
break;
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, ButtonSubclass, g_buttonSubclassID);
delete pButtonData;
break;
case WM_ERASEBKGND:
if (NppDarkMode::isEnabled() && pButtonData->ensureTheme(hWnd))
{
return TRUE;
}
else
{
break;
}
case WM_THEMECHANGED:
pButtonData->closeTheme();
break;
case WM_PRINTCLIENT:
case WM_PAINT:
if (NppDarkMode::isEnabled() && pButtonData->ensureTheme(hWnd))
{
PAINTSTRUCT ps = { 0 };
HDC hdc = reinterpret_cast<HDC>(wParam);
if (!hdc)
{
hdc = BeginPaint(hWnd, &ps);
}
paintButton(hWnd, hdc, *pButtonData);
if (ps.hdc)
{
EndPaint(hWnd, &ps);
}
return 0;
}
else
{
break;
}
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void subclassButtonControl(HWND hwnd)
{
DWORD_PTR pButtonData = reinterpret_cast<DWORD_PTR>(new ButtonData());
SetWindowSubclass(hwnd, ButtonSubclass, g_buttonSubclassID, pButtonData);
}
constexpr UINT_PTR g_toolbarSubclassID = 42;
LRESULT CALLBACK ToolbarSubclass(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
UNREFERENCED_PARAMETER(uIdSubclass);
UNREFERENCED_PARAMETER(dwRefData);
switch (uMsg)
{
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, ToolbarSubclass, g_toolbarSubclassID);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void subclassToolbarControl(HWND hwnd)
{
SetWindowSubclass(hwnd, ToolbarSubclass, g_toolbarSubclassID, 0);
}
}

View File

@ -0,0 +1,63 @@
#pragma once
#include <Windows.h>
namespace NppDarkMode
{
struct Options
{
bool enable = false;
bool enableExperimental = false;
bool enableMenubar = false;
bool enableScrollbarHack = false;
};
void initDarkMode(); // pulls options from NppParameters
void refreshDarkMode(HWND hwnd, bool forceRefresh = false); // attempts to apply new options from NppParameters, sends NPPM_INTERNAL_REFRESHDARKMODE to hwnd's top level parent
bool isEnabled();
bool isDarkMenuEnabled();
bool isExperimentalEnabled();
bool isScrollbarHackEnabled();
bool isExperimentalActive();
bool isExperimentalSupported();
COLORREF invertLightness(COLORREF c);
COLORREF invertLightnessSofter(COLORREF c);
COLORREF getBackgroundColor();
COLORREF getSofterBackgroundColor();
COLORREF getHotBackgroundColor();
COLORREF getPureBackgroundColor();
COLORREF getTextColor();
COLORREF getDarkerTextColor();
COLORREF getEdgeColor();
COLORREF getErrorBackgroundColor();
HBRUSH getBackgroundBrush();
HBRUSH getPureBackgroundBrush();
HBRUSH getSofterBackgroundBrush();
HBRUSH getHotBackgroundBrush();
HBRUSH getErrorBackgroundBrush();
// handle events
bool handleSettingChange(HWND hwnd, LPARAM lParam); // true if dark mode toggled
// processes messages related to UAH / custom menubar drawing.
// return true if handled, false to continue with normal processing in your wndproc
bool runUAHWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, LRESULT* lr);
// from DarkMode.h
void initExperimentalDarkMode(bool fixDarkScrollbar);
void allowDarkModeForApp(bool allow);
bool allowDarkModeForWindow(HWND hWnd, bool allow);
void refreshTitleBarThemeColor(HWND hWnd);
// enhancements to DarkMode.h
void enableDarkScrollBarForWindowAndChildren(HWND hwnd);
void subclassButtonControl(HWND hwnd);
void subclassToolbarControl(HWND hwnd);
}

View File

@ -1646,6 +1646,17 @@ bool NppParameters::isInFontList(const generic_string& fontName2Search) const
return false;
}
HFONT NppParameters::getDefaultUIFont()
{
static HFONT g_defaultMessageFont = []() {
NONCLIENTMETRICS ncm = { sizeof(ncm) };
SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0);
return CreateFontIndirect(&ncm.lfMessageFont);
}();
return g_defaultMessageFont;
}
void NppParameters::getLangKeywordsFromXmlTree()
{
TiXmlNode *root =
@ -5318,6 +5329,25 @@ void NppParameters::feedGUIParameters(TiXmlNode *node)
_nppGUI._commandLineInterpreter.assign(cli);
}
}
else if (!lstrcmp(nm, TEXT("DarkMode")))
{
auto parseYesNoBoolAttribute = [&element](const TCHAR* name, bool defaultValue = false)->bool {
const TCHAR* val = element->Attribute(name);
if (val)
{
if (!lstrcmp(val, TEXT("yes")))
return true;
else if (!lstrcmp(val, TEXT("no")))
return false;
}
return defaultValue;
};
_nppGUI._darkmode.enable = parseYesNoBoolAttribute(TEXT("enable"));
_nppGUI._darkmode.enableExperimental = parseYesNoBoolAttribute(TEXT("enableExperimental"));
_nppGUI._darkmode.enableMenubar = parseYesNoBoolAttribute(TEXT("enableMenubar"));
_nppGUI._darkmode.enableScrollbarHack = parseYesNoBoolAttribute(TEXT("enableScrollbarHack"));
}
}
}
@ -6305,6 +6335,22 @@ void NppParameters::createXmlTreeFromGUIParams()
GUIConfigElement->InsertEndChild(TiXmlText(_nppGUI._commandLineInterpreter.c_str()));
}
// <GUIConfig name="DarkMode" enable="no" enableExperimental="no" enableMenubar="no" enableScrollbarHack="no" />
{
TiXmlElement* GUIConfigElement = (newGUIRoot->InsertEndChild(TiXmlElement(TEXT("GUIConfig"))))->ToElement();
GUIConfigElement->SetAttribute(TEXT("name"), TEXT("DarkMode"));
auto setYesNoBoolAttribute = [&GUIConfigElement](const TCHAR* name, bool value) {
const TCHAR* pStr = value ? TEXT("yes") : TEXT("no");
GUIConfigElement->SetAttribute(name, pStr);
};
setYesNoBoolAttribute(TEXT("enable"), _nppGUI._darkmode.enable);
setYesNoBoolAttribute(TEXT("enableExperimental"), _nppGUI._darkmode.enableExperimental);
setYesNoBoolAttribute(TEXT("enableMenubar"), _nppGUI._darkmode.enableMenubar);
setYesNoBoolAttribute(TEXT("enableScrollbarHack"), _nppGUI._darkmode.enableScrollbarHack);
}
// <GUIConfig name="ScintillaPrimaryView" lineNumberMargin="show" bookMarkMargin="show" indentGuideLine="show" folderMarkStyle="box" lineWrapMethod="aligned" currentLineHilitingShow="show" scrollBeyondLastLine="no" rightClickKeepsSelection="no" disableAdvancedScrolling="no" wrapSymbolShow="hide" Wrap="no" borderEdge="yes" edge="no" edgeNbColumn="80" zoom="0" zoom2="0" whiteSpaceShow="hide" eolShow="hide" borderWidth="2" smoothFont="no" />
writeScintillaParams();

View File

@ -26,6 +26,7 @@
#include "shortcut.h"
#include "ContextMenu.h"
#include "dpiManager.h"
#include "NppDarkMode.h"
#include <assert.h>
#include <tchar.h>
@ -921,6 +922,8 @@ struct NppGUI final
bool _isDocPeekOnTab = false;
bool _isDocPeekOnMap = false;
NppDarkMode::Options _darkmode;
};
struct ScintillaViewParams
@ -1471,6 +1474,8 @@ public:
bool isInFontList(const generic_string& fontName2Search) const;
const std::vector<generic_string>& getFontList() const { return _fontlist; }
HFONT getDefaultUIFont();
int getNbUserLang() const {return _nbUserLang;}
UserLangContainer & getULCFromIndex(size_t i) {return *_userLangArray[i];};
UserLangContainer * getULCFromName(const TCHAR *userLangName);

View File

@ -4313,8 +4313,25 @@ INT_PTR CALLBACK FindIncrementDlg::run_dlgProc(UINT message, WPARAM wParam, LPAR
// Make edit field red if not found
case WM_CTLCOLOREDIT :
{
if (NppDarkMode::isEnabled())
{
if (FSNotFound != getFindStatus())
{
SetTextColor((HDC)wParam, NppDarkMode::getTextColor());
SetBkColor((HDC)wParam, NppDarkMode::getPureBackgroundColor());
return (LRESULT)NppDarkMode::getPureBackgroundBrush();
}
else // text not found
{
SetTextColor((HDC)wParam, NppDarkMode::getTextColor());
SetBkColor((HDC)wParam, NppDarkMode::getErrorBackgroundColor());
return (LRESULT)NppDarkMode::getErrorBackgroundBrush();
}
}
// if the text not found modify the background color of the editor
static HBRUSH hBrushBackground = CreateSolidBrush(BCKGRD_COLOR);
if (FSNotFound != getFindStatus())
return FALSE; // text found, use the default color
@ -4324,6 +4341,27 @@ INT_PTR CALLBACK FindIncrementDlg::run_dlgProc(UINT message, WPARAM wParam, LPAR
return (LRESULT)hBrushBackground;
}
case WM_CTLCOLORDLG:
case WM_CTLCOLORSTATIC:
{
if (!NppDarkMode::isEnabled())
{
return DefWindowProc(getHSelf(), message, wParam, lParam);
}
SetTextColor((HDC)wParam, NppDarkMode::getTextColor());
SetBkColor((HDC)wParam, NppDarkMode::getPureBackgroundColor());
return (LRESULT)GetStockObject(BLACK_BRUSH);
}
case WM_INITDIALOG:
{
LRESULT lr = DefWindowProc(getHSelf(), message, wParam, lParam);
NppDarkMode::subclassButtonControl(GetDlgItem(getHSelf(), IDC_INCFINDHILITEALL));
NppDarkMode::subclassButtonControl(GetDlgItem(getHSelf(), IDC_INCFINDMATCHCASE));
return lr;
}
case WM_COMMAND :
{
bool updateSearch = false;

View File

@ -94,8 +94,8 @@ BEGIN
PUSHBUTTON "X",IDCANCEL,2,3,16,14
RTEXT "Find :",IDC_INCSTATIC,20,6,25,12
EDITTEXT IDC_INCFINDTEXT,45,6,175,10,ES_AUTOHSCROLL | ES_WANTRETURN | NOT WS_BORDER | WS_TABSTOP, WS_EX_STATICEDGE
PUSHBUTTON "<",IDC_INCFINDPREVOK | WS_TABSTOP,223,3,16,14
PUSHBUTTON ">",IDC_INCFINDNXTOK | WS_TABSTOP,243,3,16,14
PUSHBUTTON "<",IDC_INCFINDPREVOK,223,3,16,14, WS_TABSTOP
PUSHBUTTON ">",IDC_INCFINDNXTOK,243,3,16,14, WS_TABSTOP
CONTROL "&Highlight all", IDC_INCFINDHILITEALL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,270,5,65,12
CONTROL "Match &case", IDC_INCFINDMATCHCASE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,335,5,60,12
LTEXT "",IDC_INCFINDSTATUS,400,6,180,12

View File

@ -188,7 +188,7 @@ void ScintillaEditView::init(HINSTANCE hInst, HWND hPere)
Window::init(hInst, hPere);
_hSelf = ::CreateWindowEx(
WS_EX_CLIENTEDGE,\
0,\
TEXT("Scintilla"),\
TEXT("Notepad++"),\
WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN | WS_EX_RTLREADING,\

View File

@ -20,6 +20,7 @@
#include "SplitterContainer.h"
#include "ToolTip.h"
#include "Parameters.h"
#include "NppDarkMode.h"
using namespace std;
@ -452,6 +453,13 @@ void DockingCont::drawCaptionItem(DRAWITEMSTRUCT *pDrawItemStruct)
// begin with paint
::SetBkMode(hDc, TRANSPARENT);
if (NppDarkMode::isEnabled())
{
bgbrush = ::CreateSolidBrush(_isActive ? NppDarkMode::getSofterBackgroundColor() : NppDarkMode::getBackgroundColor());
SetTextColor(hDc, NppDarkMode::getTextColor());
}
else
{
if (_isActive == TRUE)
{
bgbrush = ::CreateSolidBrush(::GetSysColor(COLOR_ACTIVECAPTION));
@ -461,6 +469,7 @@ void DockingCont::drawCaptionItem(DRAWITEMSTRUCT *pDrawItemStruct)
{
bgbrush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
}
}
// set text and/or caption grid
if (_isTopCaption == TRUE)
@ -558,6 +567,30 @@ void DockingCont::drawCaptionItem(DRAWITEMSTRUCT *pDrawItemStruct)
::DeleteObject(bgbrush);
// draw button
if (NppDarkMode::isEnabled())
{
::SelectObject(hDc, NppParameters::getInstance().getDefaultUIFont());
rc = pDrawItemStruct->rcItem;
if (_isTopCaption == TRUE)
{
rc.left = rc.right - _closeButtonWidth - _closeButtonPosLeftDynamic;
}
else
{
rc.bottom = rc.top + _closeButtonWidth + _closeButtonPosLeftDynamic; // non-dark uses Left instead of Top for the button pos so being consistent
}
if ((_isMouseOver == TRUE) && (_isMouseDown == TRUE))
{
::SetTextColor(hDc, RGB(0xFF, 0xFF, 0xFF));
}
::DrawText(hDc, L"x", 1, &rc, DT_VCENTER | DT_CENTER | DT_SINGLELINE);
}
else {
HDC dcMem = ::CreateCompatibleDC(NULL);
// select correct bitmap
@ -583,6 +616,7 @@ void DockingCont::drawCaptionItem(DRAWITEMSTRUCT *pDrawItemStruct)
::DeleteObject(hBmpCur);
::DeleteObject(hBmpNew);
::DeleteDC(dcMem);
}
::RestoreDC(hDc, nSavedDC);
}
@ -936,6 +970,18 @@ INT_PTR CALLBACK DockingCont::run_dlgProc(UINT Message, WPARAM wParam, LPARAM lP
onSize();
break;
}
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
break;
}
RECT rc = { 0 };
getClientRect(rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getBackgroundBrush());
return TRUE;
}
case WM_DRAWITEM :
{
// draw tab or caption

View File

@ -24,6 +24,7 @@
#include <shlwapi.h>
#include "Common.h"
#include "StaticDialog.h"
#include "NppDarkMode.h"
@ -91,9 +92,21 @@ protected :
generic_string _pluginName;
bool _isClosed = false;
virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM, LPARAM lParam) {
virtual INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam) {
switch (message)
{
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
break;
}
RECT rc = { 0 };
getClientRect(rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getBackgroundBrush());
return 1;
}
case WM_NOTIFY:
{
LPNMHDR pnmh = reinterpret_cast<LPNMHDR>(lParam);

View File

@ -19,6 +19,7 @@
#include "DockingSplitter.h"
#include "Notepad_plus_msgs.h"
#include "Parameters.h"
#include "NppDarkMode.h"
BOOL DockingSplitter::_isVertReg = FALSE;
BOOL DockingSplitter::_isHoriReg = FALSE;
@ -142,6 +143,20 @@ LRESULT DockingSplitter::runProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM
}
break;
}
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
break;
}
RECT rc = { 0 };
getClientRect(rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getSofterBackgroundBrush());
return 1;
}
default :
break;
}

View File

@ -103,7 +103,7 @@ INT_PTR CALLBACK FileBrowser::run_dlgProc(UINT message, WPARAM wParam, LPARAM lP
case WM_INITDIALOG :
{
NppParameters& nppParam = NppParameters::getInstance();
int style = WS_CHILD | WS_VISIBLE | CCS_ADJUSTABLE | TBSTYLE_AUTOSIZE | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT | BTNS_AUTOSIZE | BTNS_SEP | TBSTYLE_TOOLTIPS;
int style = WS_CHILD | WS_VISIBLE | CCS_ADJUSTABLE | TBSTYLE_AUTOSIZE | TBSTYLE_FLAT | TBSTYLE_LIST | TBSTYLE_TRANSPARENT | BTNS_AUTOSIZE | BTNS_SEP | TBSTYLE_TOOLTIPS | TBSTYLE_CUSTOMERASE;
_hToolbarMenu = CreateWindowEx(WS_EX_LAYOUTRTL, TOOLBARCLASSNAME, NULL, style, 0, 0, 0, 0, _hSelf, nullptr, _hInst, NULL);
TBBUTTON tbButtons[3];
// Add the bmap image into toolbar's imagelist
@ -703,6 +703,24 @@ void FileBrowser::notified(LPNMHDR notification)
break;
}
}
else if (notification->code == NM_CUSTOMDRAW && (notification->hwndFrom == _hToolbarMenu))
{
NMTBCUSTOMDRAW* nmtbcd = reinterpret_cast<NMTBCUSTOMDRAW*>(notification);
if (nmtbcd->nmcd.dwDrawStage == CDDS_PREERASE)
{
if (NppDarkMode::isEnabled())
{
FillRect(nmtbcd->nmcd.hdc, &nmtbcd->nmcd.rc, NppDarkMode::getPureBackgroundBrush());
nmtbcd->clrText = NppDarkMode::getTextColor();
SetTextColor(nmtbcd->nmcd.hdc, NppDarkMode::getTextColor());
SetWindowLongPtr(_hSelf, DWLP_MSGRESULT, CDRF_SKIPDEFAULT);
}
else
{
SetWindowLongPtr(_hSelf, DWLP_MSGRESULT, CDRF_DODEFAULT);
}
}
}
}
BrowserNodeType FileBrowser::getNodeType(HTREEITEM hItem)

View File

@ -88,7 +88,20 @@ BEGIN
CONTROL "Disable advanced scrolling feature due to touchpad issue",IDC_CHECK_DISABLEADVANCEDSCROLL,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,54,155,270,10
END
IDD_PREFERENCE_SUB_DARKMODE DIALOGEX 0, 0, 455, 185
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD
FONT 8, "MS Shell Dlg", 0, 0, 0x1
BEGIN
CONTROL "Enable &dark mode",IDC_CHECK_DARKMODE_ENABLE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,20,289,10
CONTROL "Override &menubar drawing", IDC_CHECK_DARKMODE_ENABLE_MENUBAR,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,66,289,10
CONTROL "&Use system dark mode API* (dark title bar and menus)",IDC_CHECK_DARKMODE_ENABLE_EXPERIMENTAL,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,20,80,289,10
CONTROL "Override &scrollbar themes*", IDC_CHECK_DARKMODE_ENABLE_SCROLLBAR_HACK,
"Button", BS_AUTOCHECKBOX | WS_TABSTOP, 20,94,289,10
GROUPBOX "Experimental Dark Mode Options",IDC_GROUPBOX_DARKMODE,11,47,307,70,BS_CENTER
LTEXT "*Notepad++ must be restarted to take effect",IDC_STATIC_DARKMODE,11,119,308,16
END
IDD_PREFERENCE_SUB_MARGING_BORDER_EDGE DIALOGEX 0, 0, 455, 185
STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD

View File

@ -106,6 +106,9 @@ INT_PTR CALLBACK PreferenceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM
_editingSubDlg.init(_hInst, _hSelf);
_editingSubDlg.create(IDD_PREFERENCE_SUB_EDITING, false, false);
_darkModeSubDlg.init(_hInst, _hSelf);
_darkModeSubDlg.create(IDD_PREFERENCE_SUB_DARKMODE, false, false);
_marginsBorderEdgeSubDlg.init(_hInst, _hSelf);
_marginsBorderEdgeSubDlg.create(IDD_PREFERENCE_SUB_MARGING_BORDER_EDGE, false, false);
@ -154,9 +157,9 @@ INT_PTR CALLBACK PreferenceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM
_searchEngineSubDlg.init(_hInst, _hSelf);
_searchEngineSubDlg.create(IDD_PREFERENCE_SUB_SEARCHENGINE, false, false);
_wVector.push_back(DlgInfo(&_generalSubDlg, TEXT("General"), TEXT("Global")));
_wVector.push_back(DlgInfo(&_editingSubDlg, TEXT("Editing"), TEXT("Scintillas")));
_wVector.push_back(DlgInfo(&_darkModeSubDlg, TEXT("Dark Mode"), TEXT("DarkMode")));
_wVector.push_back(DlgInfo(&_marginsBorderEdgeSubDlg, TEXT("Margins/Border/Edge"), TEXT("MarginsBorderEdge")));
_wVector.push_back(DlgInfo(&_newDocumentSubDlg, TEXT("New Document"), TEXT("NewDoc")));
_wVector.push_back(DlgInfo(&_defaultDirectorySubDlg, TEXT("Default Directory"), TEXT("DefaultDir")));
@ -185,6 +188,7 @@ INT_PTR CALLBACK PreferenceDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM
_generalSubDlg.reSizeTo(rc);
_editingSubDlg.reSizeTo(rc);
_darkModeSubDlg.reSizeTo(rc);
_marginsBorderEdgeSubDlg.reSizeTo(rc);
_miscSubDlg.reSizeTo(rc);
_newDocumentSubDlg.reSizeTo(rc);
@ -338,6 +342,7 @@ void PreferenceDlg::destroy()
{
_generalSubDlg.destroy();
_editingSubDlg.destroy();
_darkModeSubDlg.destroy();
_marginsBorderEdgeSubDlg.destroy();
_miscSubDlg.destroy();
_fileAssocDlg.destroy();
@ -751,6 +756,71 @@ INT_PTR CALLBACK EditingSubDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM
return FALSE;
}
void DarkModeSubDlg::enableDependentControls()
{
bool experimentalEnabled = isCheckedOrNot(IDC_CHECK_DARKMODE_ENABLE_EXPERIMENTAL);
EnableWindow(GetDlgItem(_hSelf, IDC_CHECK_DARKMODE_ENABLE_SCROLLBAR_HACK), experimentalEnabled ? TRUE : FALSE);
}
INT_PTR CALLBACK DarkModeSubDlg::run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
NppParameters& nppParam = NppParameters::getInstance();
NppGUI& nppGUI = nppParam.getNppGUI();
switch (message)
{
case WM_INITDIALOG:
{
::SendDlgItemMessage(_hSelf, IDC_CHECK_DARKMODE_ENABLE, BM_SETCHECK, nppGUI._darkmode.enable, 0);
::SendDlgItemMessage(_hSelf, IDC_CHECK_DARKMODE_ENABLE_EXPERIMENTAL, BM_SETCHECK, nppGUI._darkmode.enableExperimental, 0);
::SendDlgItemMessage(_hSelf, IDC_CHECK_DARKMODE_ENABLE_MENUBAR, BM_SETCHECK, nppGUI._darkmode.enableMenubar, 0);
::SendDlgItemMessage(_hSelf, IDC_CHECK_DARKMODE_ENABLE_SCROLLBAR_HACK, BM_SETCHECK, nppGUI._darkmode.enableScrollbarHack, 0);
enableDependentControls();
ETDTProc enableDlgTheme = (ETDTProc)nppParam.getEnableThemeDlgTexture();
if (enableDlgTheme)
enableDlgTheme(_hSelf, ETDT_ENABLETAB);
return TRUE;
}
case WM_COMMAND:
{
bool changed = false;
switch (wParam)
{
case IDC_CHECK_DARKMODE_ENABLE:
nppGUI._darkmode.enable = isCheckedOrNot(static_cast<int>(wParam));
changed = true;
break;
case IDC_CHECK_DARKMODE_ENABLE_EXPERIMENTAL:
nppGUI._darkmode.enableExperimental = isCheckedOrNot(static_cast<int>(wParam));
changed = true;
break;
case IDC_CHECK_DARKMODE_ENABLE_MENUBAR:
nppGUI._darkmode.enableMenubar = isCheckedOrNot(static_cast<int>(wParam));
changed = true;
break;
case IDC_CHECK_DARKMODE_ENABLE_SCROLLBAR_HACK:
nppGUI._darkmode.enableScrollbarHack = isCheckedOrNot(static_cast<int>(wParam));
changed = true;
break;
}
if (changed)
{
NppDarkMode::refreshDarkMode(_hSelf);
enableDependentControls();
return TRUE;
}
return FALSE;
}
}
return FALSE;
}
void MarginsBorderEdgeSubDlg::initScintParam()
{
NppParameters& nppParam = NppParameters::getInstance();

View File

@ -51,6 +51,16 @@ private :
void initScintParam();
};
class DarkModeSubDlg : public StaticDialog
{
public:
DarkModeSubDlg() = default;
private:
INT_PTR CALLBACK run_dlgProc(UINT message, WPARAM wParam, LPARAM lParam);
void enableDependentControls();
};
class MarginsBorderEdgeSubDlg : public StaticDialog
{
public :
@ -265,6 +275,7 @@ private :
WindowVector _wVector;
GeneralSubDlg _generalSubDlg;
EditingSubDlg _editingSubDlg;
DarkModeSubDlg _darkModeSubDlg;
MarginsBorderEdgeSubDlg _marginsBorderEdgeSubDlg;
MiscSubDlg _miscSubDlg;
RegExtDlg _fileAssocDlg;

View File

@ -362,4 +362,12 @@
#define IDC_CHECK_FINDDLG_ALWAYS_VISIBLE (IDD_PREFERENCE_SUB_SEARCHING + 3)
#define IDC_CHECK_CONFIRMREPLOPENDOCS (IDD_PREFERENCE_SUB_SEARCHING + 4)
#define IDD_PREFERENCE_SUB_DARKMODE 7100 //(IDD_PREFERENCE_BOX + 1100)
#define IDC_CHECK_DARKMODE_ENABLE (IDD_PREFERENCE_SUB_DARKMODE + 1)
#define IDC_CHECK_DARKMODE_ENABLE_EXPERIMENTAL (IDD_PREFERENCE_SUB_DARKMODE + 2)
#define IDC_CHECK_DARKMODE_ENABLE_MENUBAR (IDD_PREFERENCE_SUB_DARKMODE + 3)
#define IDC_CHECK_DARKMODE_ENABLE_SCROLLBAR_HACK (IDD_PREFERENCE_SUB_DARKMODE + 4)
#define IDC_GROUPBOX_DARKMODE (IDD_PREFERENCE_SUB_DARKMODE + 5)
#define IDC_STATIC_DARKMODE (IDD_PREFERENCE_SUB_DARKMODE + 6)
#endif //PREFERENCE_RC_H

View File

@ -17,6 +17,7 @@
#include <stdexcept>
#include <windows.h>
#include "Splitter.h"
#include "NppDarkMode.h"
bool Splitter::_isHorizontalRegistered = false;
bool Splitter::_isVerticalRegistered = false;
@ -281,7 +282,7 @@ LRESULT CALLBACK Splitter::spliterWndProc(UINT uMsg, WPARAM wParam, LPARAM lPara
if (isInLeftTopZone(p) || isInRightBottomZone(p))
{
//::SetCursor(::LoadCursor(_hInst, MAKEINTRESOURCE(IDC_UP_ARROW)));
::SetCursor(::LoadCursor(NULL, IDC_ARROW));
::SetCursor(::LoadCursor(NULL, IDC_HAND));
return TRUE;
}
@ -387,6 +388,21 @@ LRESULT CALLBACK Splitter::spliterWndProc(UINT uMsg, WPARAM wParam, LPARAM lPara
return 0;
}
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
break;
}
RECT rc = { 0 };
getClientRect(rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getSofterBackgroundBrush());
return 1;
}
case WM_PAINT:
{
drawSplitter();
@ -504,6 +520,16 @@ void Splitter::drawSplitter()
HDC hdc = ::BeginPaint(_hSelf, &ps);
getClientRect(rc);
bool isDarkMode = NppDarkMode::isEnabled();
HPEN holdPen = nullptr;
if (isDarkMode)
{
static HPEN g_hpen = CreatePen(PS_SOLID, 1, NppDarkMode::getDarkerTextColor());
holdPen = (HPEN)SelectObject(hdc, g_hpen);
FillRect(hdc, &rc, NppDarkMode::getSofterBackgroundBrush());
}
if ((_splitterSize >= 4) && (_dwFlags & SV_RESIZEWTHPERCNT))
{
adjustZoneToDraw(TLrc, ZONE_TYPE::topLeft);
@ -534,7 +560,7 @@ void Splitter::drawSplitter()
else
bottom = rc.bottom;
while (rcToDraw1.bottom <= bottom)
while (!isDarkMode && rcToDraw1.bottom <= bottom)
{
if (isVertical())
{
@ -573,6 +599,11 @@ void Splitter::drawSplitter()
if ((_splitterSize >= 4) && (_dwFlags & SV_RESIZEWTHPERCNT))
paintArrow(hdc, BRrc, isVertical() ? Arrow::right : Arrow::down);
if (isDarkMode)
{
SelectObject(hdc, holdPen);
}
::EndPaint(_hSelf, &ps);
}

View File

@ -21,6 +21,8 @@
#include "StatusBar.h"
#include <algorithm>
#include <cassert>
#include "Parameters.h"
#include "NppDarkMode.h"
//#define IDC_STATUSBAR 789
@ -45,6 +47,97 @@ void StatusBar::init(HINSTANCE /*hInst*/, HWND /*hPere*/)
assert(false and "should never be called");
}
constexpr UINT_PTR g_statusBarSubclassID = 42;
LRESULT CALLBACK StatusBarSubclass(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
UNREFERENCED_PARAMETER(dwRefData);
UNREFERENCED_PARAMETER(uIdSubclass);
switch (uMsg) {
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
RECT rc;
GetClientRect(hWnd, &rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getBackgroundBrush());
return TRUE;
}
case WM_PAINT:
{
if (!NppDarkMode::isEnabled())
{
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
struct {
int horizontal;
int vertical;
int between;
} borders = { 0 };
SendMessage(hWnd, SB_GETBORDERS, 0, (LPARAM)&borders);
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
HFONT holdFont = (HFONT)::SelectObject(hdc, NppParameters::getInstance().getDefaultUIFont());
RECT rcClient;
GetClientRect(hWnd, &rcClient);
FillRect(hdc, &ps.rcPaint, NppDarkMode::getBackgroundBrush());
int nParts = static_cast<int>(SendMessage(hWnd, SB_GETPARTS, 0, 0));
std::vector<wchar_t> str;
for (int i = 0; i < nParts; ++i)
{
RECT rcPart = { 0 };
SendMessage(hWnd, SB_GETRECT, i, (LPARAM)&rcPart);
RECT rcIntersect = { 0 };
if (!IntersectRect(&rcIntersect, &rcPart, &ps.rcPaint))
{
continue;
}
RECT rcDivider = { rcPart.right - borders.vertical, rcPart.top, rcPart.right, rcPart.bottom };
DWORD cchText = LOWORD(SendMessage(hWnd, SB_GETTEXTLENGTH, i, 0));
str.resize(cchText + 1);
SendMessage(hWnd, SB_GETTEXT, i, (LPARAM)str.data());
SetBkMode(hdc, TRANSPARENT);
SetTextColor(hdc, NppDarkMode::getTextColor());
rcPart.left += borders.between;
rcPart.right -= borders.vertical;
DrawText(hdc, str.data(), cchText, &rcPart, DT_SINGLELINE | DT_VCENTER | DT_LEFT);
FillRect(hdc, &rcDivider, NppDarkMode::getSofterBackgroundBrush());
}
::SelectObject(hdc, holdFont);
EndPaint(hWnd, &ps);
return 0;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, StatusBarSubclass, g_statusBarSubclassID);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void StatusBar::init(HINSTANCE hInst, HWND hPere, int nbParts)
{
@ -63,6 +156,10 @@ void StatusBar::init(HINSTANCE hInst, HWND hPere, int nbParts)
if (!_hSelf)
throw std::runtime_error("StatusBar::init : CreateWindowEx() function return null");
if (nbParts > 0)
{
SetWindowSubclass(_hSelf, StatusBarSubclass, g_statusBarSubclassID, 0);
}
_partWidthArray.clear();
if (nbParts > 0)

View File

@ -19,6 +19,7 @@
#include <stdexcept>
#include "TabBar.h"
#include "Parameters.h"
#include "NppDarkMode.h"
#define IDC_DRAG_TAB 1404
#define IDC_DRAG_INTERDIT_TAB 1405
@ -855,13 +856,133 @@ LRESULT TabBarPlus::runProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lPara
}
return TRUE;
}
case WM_ERASEBKGND:
{
if (!NppDarkMode::isEnabled())
{
break;
}
RECT rc = { 0 };
GetClientRect(hwnd, &rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getPureBackgroundBrush());
return 1;
}
case WM_PAINT:
{
if (!NppDarkMode::isEnabled())
{
break;
}
LONG_PTR dwStyle = GetWindowLongPtr(hwnd, GWL_STYLE);
if (!(dwStyle & TCS_OWNERDRAWFIXED) || (dwStyle & TCS_BOTTOM) || (dwStyle & TCS_BUTTONS))
{
break;
}
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
FillRect(hdc, &ps.rcPaint, NppDarkMode::getPureBackgroundBrush());
UINT id = ::GetDlgCtrlID(hwnd);
static HPEN g_hpen = CreatePen(PS_SOLID, 1, NppDarkMode::getEdgeColor());
HPEN holdPen = (HPEN)SelectObject(hdc, g_hpen);
HRGN holdClip = CreateRectRgn(0, 0, 0, 0);
if (1 != GetClipRgn(hdc, holdClip))
{
DeleteObject(holdClip);
holdClip = nullptr;
}
int topBarHeight = NppParameters::getInstance()._dpiManager.scaleX(4);
int nTabs = TabCtrl_GetItemCount(hwnd);
int nFocusTab = TabCtrl_GetCurFocus(hwnd);
int nSelTab = TabCtrl_GetCurSel(hwnd);
for (int i = 0; i < nTabs; ++i)
{
DRAWITEMSTRUCT dis = { ODT_TAB, id, (UINT)i, ODA_DRAWENTIRE, ODS_DEFAULT, hwnd, hdc };
TabCtrl_GetItemRect(hwnd, i, &dis.rcItem);
if (i == nFocusTab)
{
dis.itemState |= ODS_FOCUS;
}
if (i == nSelTab)
{
dis.itemState |= ODS_SELECTED;
}
dis.itemState |= ODS_NOFOCUSRECT; // maybe, does it handle it already?
RECT rcIntersect = { 0 };
if (IntersectRect(&rcIntersect, &ps.rcPaint, &dis.rcItem))
{
if (dwStyle & TCS_VERTICAL)
{
POINT edges[] = {
{dis.rcItem.left, dis.rcItem.bottom - 1},
{dis.rcItem.right, dis.rcItem.bottom - 1}
};
if (i != nSelTab && (i != nSelTab - 1))
{
edges[0].x += topBarHeight;
}
Polyline(hdc, edges, _countof(edges));
dis.rcItem.bottom -= 1;
}
else
{
POINT edges[] = {
{dis.rcItem.right - 1, dis.rcItem.top},
{dis.rcItem.right - 1, dis.rcItem.bottom}
};
if (i != nSelTab && (i != nSelTab - 1))
{
edges[0].y += topBarHeight;
}
Polyline(hdc, edges, _countof(edges));
dis.rcItem.right -= 1;
}
HRGN hClip = CreateRectRgnIndirect(&dis.rcItem);
SelectClipRgn(hdc, hClip);
drawItem(&dis, true);
DeleteObject(hClip);
SelectClipRgn(hdc, holdClip);
}
}
SelectClipRgn(hdc, holdClip);
if (holdClip)
{
DeleteObject(holdClip);
holdClip = nullptr;
}
SelectObject(hdc, holdPen);
EndPaint(hwnd, &ps);
return 0;
}
}
return ::CallWindowProc(_tabBarDefaultProc, hwnd, Message, wParam, lParam);
}
void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct)
void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct, bool isDarkMode)
{
RECT rect = pDrawItemStruct->rcItem;
@ -887,14 +1008,14 @@ void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct)
int nSavedDC = ::SaveDC(hDC);
::SetBkMode(hDC, TRANSPARENT);
HBRUSH hBrush = ::CreateSolidBrush(::GetSysColor(COLOR_BTNFACE));
HBRUSH hBrush = ::CreateSolidBrush(!isDarkMode ? ::GetSysColor(COLOR_BTNFACE) : NppDarkMode::getBackgroundColor());
::FillRect(hDC, &rect, hBrush);
::DeleteObject((HGDIOBJ)hBrush);
// equalize drawing areas of active and inactive tabs
int paddingDynamicTwoX = NppParameters::getInstance()._dpiManager.scaleX(2);
int paddingDynamicTwoY = NppParameters::getInstance()._dpiManager.scaleY(2);
if (isSelected)
if (isSelected && !isDarkMode)
{
// the drawing area of the active tab extends on all borders by default
rect.top += ::GetSystemMetrics(SM_CYEDGE);
@ -948,6 +1069,10 @@ void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct)
RECT barRect = rect;
if (isSelected)
{
if (isDarkMode)
{
::FillRect(hDC, &barRect, NppDarkMode::getSofterBackgroundBrush());
}
if (_drawTopBar)
{
int topBarHeight = NppParameters::getInstance()._dpiManager.scaleX(4);
@ -975,7 +1100,7 @@ void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct)
{
if (_drawInactiveTab)
{
hBrush = ::CreateSolidBrush(_inactiveBgColour);
hBrush = ::CreateSolidBrush(!isDarkMode ? _inactiveBgColour : NppDarkMode::getBackgroundColor());
::FillRect(hDC, &barRect, hBrush);
::DeleteObject((HGDIOBJ)hBrush);
}
@ -1112,7 +1237,13 @@ void TabBarPlus::drawItem(DRAWITEMSTRUCT *pDrawItemStruct)
rect.left += spaceUnit;
}
::SetTextColor(hDC, isSelected ? _activeTextColour : _inactiveTextColour);
COLORREF textColor = isSelected ? _activeTextColour : _inactiveTextColour;
if (isDarkMode)
{
textColor = NppDarkMode::invertLightnessSofter(textColor);
}
::SetTextColor(hDC, textColor);
::DrawText(hDC, decodedLabel, lstrlen(decodedLabel), &rect, Flags);
::RestoreDC(hDC, nSavedDC);

View File

@ -258,7 +258,7 @@ protected:
static int _nbCtrl;
static HWND _hwndArray[nbCtrlMax];
void drawItem(DRAWITEMSTRUCT *pDrawItemStruct);
void drawItem(DRAWITEMSTRUCT *pDrawItemStruct, bool isDarkMode = false);
void draggingCursor(POINT screenPoint);
int getTabIndexAt(const POINT & p)

View File

@ -20,6 +20,8 @@
#include "Parameters.h"
#include "FindReplaceDlg_rc.h"
#include "NppDarkMode.h"
const int WS_TOOLBARSTYLE = WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | TBSTYLE_TOOLTIPS |TBSTYLE_FLAT | CCS_TOP | BTNS_AUTOSIZE | CCS_NOPARENTALIGN | CCS_NORESIZE | CCS_NODIVIDER;
void ToolBar::initTheme(TiXmlDocument *toolIconsDocRoot)
@ -253,11 +255,17 @@ void ToolBar::reset(bool create)
if (!_hSelf)
{
DWORD dwExtraStyle = 0;
if (NppDarkMode::isEnabled())
{
dwExtraStyle = TBSTYLE_CUSTOMERASE;
}
_hSelf = ::CreateWindowEx(
WS_EX_PALETTEWINDOW,
TOOLBARCLASSNAME,
TEXT(""),
WS_TOOLBARSTYLE,
WS_TOOLBARSTYLE | dwExtraStyle,
0, 0,
0, 0,
_hParent,
@ -408,17 +416,51 @@ void ToolBar::addToRebar(ReBar * rebar)
_rbBand.fMask = RBBIM_CHILD | RBBIM_CHILDSIZE | RBBIM_IDEALSIZE | RBBIM_SIZE;
}
constexpr UINT_PTR g_rebarSubclassID = 42;
LRESULT CALLBACK RebarSubclass(
HWND hWnd,
UINT uMsg,
WPARAM wParam,
LPARAM lParam,
UINT_PTR uIdSubclass,
DWORD_PTR dwRefData
)
{
UNREFERENCED_PARAMETER(dwRefData);
UNREFERENCED_PARAMETER(uIdSubclass);
switch (uMsg) {
case WM_ERASEBKGND:
if (NppDarkMode::isEnabled())
{
RECT rc;
GetClientRect(hWnd, &rc);
FillRect((HDC)wParam, &rc, NppDarkMode::getPureBackgroundBrush());
return 1;
}
else {
break;
}
case WM_NCDESTROY:
RemoveWindowSubclass(hWnd, RebarSubclass, g_rebarSubclassID);
break;
}
return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}
void ReBar::init(HINSTANCE hInst, HWND hPere)
{
Window::init(hInst, hPere);
_hSelf = CreateWindowEx(WS_EX_TOOLWINDOW,
REBARCLASSNAME,
NULL,
WS_CHILD|WS_VISIBLE|WS_CLIPSIBLINGS|WS_CLIPCHILDREN|RBS_VARHEIGHT|
RBS_BANDBORDERS | CCS_NODIVIDER | CCS_NOPARENTALIGN,
CCS_NODIVIDER | CCS_NOPARENTALIGN,
0,0,0,0, _hParent, NULL, _hInst, NULL);
SetWindowSubclass(_hSelf, RebarSubclass, g_rebarSubclassID, 0);
REBARINFO rbi;
ZeroMemory(&rbi, sizeof(REBARINFO));
rbi.cbSize = sizeof(REBARINFO);

View File

@ -875,6 +875,13 @@ void NativeLangSpeaker::changePrefereceDlgLang(PreferenceDlg & preference)
preference.renameDialogTitle(TEXT("Scintillas"), nameW);
}
changeDlgLang(preference._darkModeSubDlg.getHSelf(), "DarkMode", titre, titreMaxSize);
if (titre[0] != '\0')
{
const wchar_t* nameW = wmc.char2wchar(titre, _nativeLangEncoding);
preference.renameDialogTitle(TEXT("DarkMode"), nameW);
}
changeDlgLang(preference._marginsBorderEdgeSubDlg.getHSelf(), "MarginsBorderEdge", titre, titreMaxSize);
if (titre[0] != '\0')
{

View File

@ -446,6 +446,7 @@
#define NPPM_INTERNAL_FINDINPROJECTS (NOTEPADPLUS_USER_INTERNAL + 56)
#define NPPM_INTERNAL_SCINTILLAFINDERPURGE (NOTEPADPLUS_USER_INTERNAL + 57)
#define NPPM_INTERNAL_UPDATETEXTZONEPADDING (NOTEPADPLUS_USER_INTERNAL + 58)
#define NPPM_INTERNAL_REFRESHDARKMODE (NOTEPADPLUS_USER_INTERNAL + 59)
// See Notepad_plus_msgs.h
//#define NOTEPADPLUS_USER (WM_USER + 1000)

View File

@ -19,6 +19,7 @@
#include "Win32Exception.h" //Win32 exception
#include "MiniDumper.h" //Write dump files
#include "verifySignedfile.h"
#include "NppDarkMode.h"
typedef std::vector<generic_string> ParamVector;
@ -491,8 +492,11 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR pCmdLine, int)
}
nppParameters.load();
NppGUI & nppGui = nppParameters.getNppGUI();
NppDarkMode::initDarkMode();
bool doUpdateNpp = nppGui._autoUpdateOpt._doAutoUpdate;
bool doUpdatePluginList = nppGui._autoUpdateOpt._doAutoUpdate;

View File

@ -404,9 +404,11 @@ copy ..\src\contextMenu.xml ..\binarm64\contextMenu.xml
</PostBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\src\DarkMode\DarkMode.cpp" />
<ClCompile Include="..\src\MISC\Common\verifySignedfile.cpp" />
<ClCompile Include="..\src\MISC\md5\md5Dlgs.cpp" />
<ClCompile Include="..\src\MISC\sha2\sha-256.cpp" />
<ClCompile Include="..\src\NppDarkMode.cpp" />
<ClCompile Include="..\src\WinControls\AboutDlg\AboutDlg.cpp" />
<ClCompile Include="..\src\WinControls\AnsiCharPanel\ansiCharPanel.cpp" />
<ClCompile Include="..\src\ScintillaComponent\AutoCompletion.cpp" />
@ -669,6 +671,9 @@ copy ..\src\contextMenu.xml ..\binarm64\contextMenu.xml
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\src\clipboardFormats.h" />
<ClInclude Include="..\src\DarkMode\DarkMode.h" />
<ClInclude Include="..\src\DarkMode\IatHook.h" />
<ClInclude Include="..\src\DarkMode\UAHMenuBar.h" />
<ClInclude Include="..\src\dpiManager.h" />
<ClInclude Include="..\src\keys.h" />
<ClInclude Include="..\src\localizationString.h" />
@ -678,6 +683,7 @@ copy ..\src\contextMenu.xml ..\binarm64\contextMenu.xml
<ClInclude Include="..\src\MISC\md5\md5Dlgs.h" />
<ClInclude Include="..\src\MISC\md5\md5Dlgs_rc.h" />
<ClInclude Include="..\src\MISC\sha2\sha-256.h" />
<ClInclude Include="..\src\NppDarkMode.h" />
<ClInclude Include="..\src\ScintillaComponent\columnEditor_rc.h" />
<ClInclude Include="..\src\ScintillaComponent\FindReplaceDlg_rc.h" />
<ClInclude Include="..\src\ScintillaComponent\ScintillaRef.h" />