From a4058d416c3f75d3d676692401c0463f10db5c83 Mon Sep 17 00:00:00 2001 From: hatelamers <97636078+hatelamers@users.noreply.github.com> Date: Sat, 16 Nov 2024 17:44:38 +0100 Subject: [PATCH] #3 implemented HiDPI support --- buildenv/CMakeLists.txt | 3 +- buildenv/buildnumber.txt | 2 +- src/app/AboutDlg.cpp | 8 +++ src/app/AboutDlg.h | 2 + src/app/MainFrm.cpp | 3 +- src/app/MainFrm.h | 6 +- src/app/WinPinMenu.cpp | 22 ++++++-- src/app/WinPinMenu.rc | 13 ++++- src/app/WinPinMenu.vcxproj | 9 +++ src/app/WinPinMenu.vcxproj.filters | 5 ++ src/app/app.manifest.in | 35 ++++++++++++ src/app/productmeta.h | 6 +- src/app/res/app.manifest | 35 ++++++++++++ src/app/resource.h | 4 +- src/app/stdafx.h | 23 +++++--- src/app/targetver.h | 6 +- src/app/uxthemehelper.h | 91 ++++++++++++++++++++++++++++-- 17 files changed, 237 insertions(+), 36 deletions(-) create mode 100644 src/app/app.manifest.in create mode 100644 src/app/res/app.manifest diff --git a/buildenv/CMakeLists.txt b/buildenv/CMakeLists.txt index aad15ad..7b744ad 100644 --- a/buildenv/CMakeLists.txt +++ b/buildenv/CMakeLists.txt @@ -13,8 +13,8 @@ endif() include("${CMAKE_CURRENT_LIST_DIR}/LocalBuild.cmake" OPTIONAL) -include("${CMAKE_CURRENT_LIST_DIR}/Product.cmake") file(READ "${CMAKE_CURRENT_LIST_DIR}/buildnumber.txt" BUILD_NUMBER) +include("${CMAKE_CURRENT_LIST_DIR}/Product.cmake") if(NOT ${BUILD_NUMBER}) set(BUILD_NUMBER 1) endif() @@ -43,6 +43,7 @@ set(MAIN_TARGET_SOURCE_DIR "${WORKSPACE_ROOT}/src/app") set(MAIN_TARGET_RESOURCE_DIR "${MAIN_TARGET_SOURCE_DIR}/res") configure_file("${MAIN_TARGET_SOURCE_DIR}/productmeta.h.in" "${MAIN_TARGET_SOURCE_DIR}/productmeta.h") +configure_file("${MAIN_TARGET_SOURCE_DIR}/app.manifest.in" "${MAIN_TARGET_RESOURCE_DIR}/app.manifest") add_custom_target(${MAIN_TARGET_NAME} ALL) diff --git a/buildenv/buildnumber.txt b/buildenv/buildnumber.txt index 978b4e8..368f89c 100644 --- a/buildenv/buildnumber.txt +++ b/buildenv/buildnumber.txt @@ -1 +1 @@ -26 \ No newline at end of file +28 \ No newline at end of file diff --git a/src/app/AboutDlg.cpp b/src/app/AboutDlg.cpp index bc3afde..64e6a99 100644 --- a/src/app/AboutDlg.cpp +++ b/src/app/AboutDlg.cpp @@ -10,6 +10,14 @@ LRESULT CAboutDlg::OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/) { + uxTheme.AllowDarkModeForWindow(m_hWnd, true); + if (uxTheme.ShouldAppsUseDarkMode()) + { + uxTheme.SwitchWindowDarkMode(m_hWnd, true); + } + SetThemeExtendedStyle(THEME_EX_THEMECLIENTEDGE); + //EnableThemeDialogTexture(ETDT_ENABLETAB); + DoDataExchange(FALSE); //m_lnkLicense.SetHyperLinkExtendedStyle(HLINK_COMMANDBUTTON, HLINK_COMMANDBUTTON); if (m_fvi.Open()) diff --git a/src/app/AboutDlg.h b/src/app/AboutDlg.h index 2223695..34ce943 100644 --- a/src/app/AboutDlg.h +++ b/src/app/AboutDlg.h @@ -7,6 +7,7 @@ class CAboutDlg : public CDialogImpl + , public CThemeImpl , public CWinDataExchange { public: @@ -17,6 +18,7 @@ class CAboutDlg COMMAND_ID_HANDLER(IDOK, OnCloseCmd) COMMAND_ID_HANDLER(IDRETRY, OnCloseCmd) COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) + CHAIN_MSG_MAP(CThemeImpl) END_MSG_MAP() BEGIN_DDX_MAP(CAboutDlg) diff --git a/src/app/MainFrm.cpp b/src/app/MainFrm.cpp index 4f98e8d..b34bcd8 100644 --- a/src/app/MainFrm.cpp +++ b/src/app/MainFrm.cpp @@ -51,8 +51,6 @@ LRESULT CMainFrame::OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/ LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& bHandled) { - m_mnuMain.Detach(); - // unregister message filtering and idle updates CMessageLoop* pLoop = _Module.GetMessageLoop(); ATLASSERT(pLoop != NULL); @@ -60,6 +58,7 @@ LRESULT CMainFrame::OnDestroy(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam* pLoop->RemoveIdleHandler(this); bHandled = FALSE; + PostQuitMessage(0); return 1; } diff --git a/src/app/MainFrm.h b/src/app/MainFrm.h index 7cca4c4..8dc29e0 100644 --- a/src/app/MainFrm.h +++ b/src/app/MainFrm.h @@ -11,7 +11,8 @@ #define MNUPOS_APP_EXIT 0 class CMainFrame : - public CFrameWindowImpl, + public CFrameWindowImpl, + public CThemeImpl, public CUpdateUI, public CMessageFilter, public CIdleHandler, public CShellBrowseMenu::ShellMenuController { @@ -49,6 +50,7 @@ class CMainFrame : MESSAGE_HANDLER(GetDisplayPopupMessage(), OnDisplayPopup) COMMAND_ID_HANDLER(ID_APP_EXIT, OnFileExit) COMMAND_ID_HANDLER(ID_APP_ABOUT, OnAppAbout) + CHAIN_MSG_MAP(CThemeImpl) CHAIN_MSG_MAP_MEMBER(m_shellMenu) MESSAGE_HANDLER(WM_MENUSELECT, OnMenuSelect) CHAIN_MSG_MAP(CUpdateUI) @@ -80,7 +82,7 @@ class CMainFrame : public: bool m_hasChildren{ false }; private: - CMenu m_mnuMain; + CMenuHandle m_mnuMain; CShellBrowseMenu m_shellMenu; CString m_strActionSource; CString m_strSourceTitle; diff --git a/src/app/WinPinMenu.cpp b/src/app/WinPinMenu.cpp index 787dfa5..274a4ba 100644 --- a/src/app/WinPinMenu.cpp +++ b/src/app/WinPinMenu.cpp @@ -9,6 +9,7 @@ #include "MainFrm.h" CAppModule _Module; +CUxTheme uxTheme; class CWinPinMenuThreadManager { @@ -36,13 +37,24 @@ class CWinPinMenuThreadManager _RunData* pData = (_RunData*)lpData; CMainFrame wndFrame; - RECT rc{ -1000, -1000, -950, -950 }; - if(wndFrame.CreateEx(NULL, &rc, WS_ICONIC) == NULL) + RECT rc{ -1000, -1000, 0, 0 }; + POINT pt{ 0,0 }; + if (::GetCursorPos(&pt)) + { + rc.left = pt.x; + rc.top = pt.y; + } + if(wndFrame.CreateEx(NULL, &rc, WS_POPUP) == NULL) { ATLTRACE(_T("Frame window creation failed!\n")); return 0; } + uxTheme.AllowDarkModeForWindow(wndFrame, true); + if (uxTheme.ShouldAppsUseDarkMode()) + { + uxTheme.SwitchWindowDarkMode(wndFrame, true); + } wndFrame.ShowWindow(pData->nCmdShow); delete pData; @@ -135,13 +147,13 @@ class CWinPinMenuThreadManager int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow) { - CUxTheme uxTheme; - uxTheme.SetPreferredAppMode(PreferredAppMode::AllowDark); + // TODO: intrusive dark mode doesn't support owner-drawn menus, we need to paint all ourselves + //uxTheme.SetPreferredAppMode(PreferredAppMode::AllowDark); HRESULT hRes = ::CoInitialize(NULL); ATLASSERT(SUCCEEDED(hRes)); - AtlInitCommonControls(ICC_BAR_CLASSES); // add flags to support other controls + AtlInitCommonControls(ICC_STANDARD_CLASSES | ICC_BAR_CLASSES); // add flags to support other controls hRes = _Module.Init(NULL, hInstance); ATLASSERT(SUCCEEDED(hRes)); diff --git a/src/app/WinPinMenu.rc b/src/app/WinPinMenu.rc index e251f7f..ab10cb8 100644 --- a/src/app/WinPinMenu.rc +++ b/src/app/WinPinMenu.rc @@ -30,6 +30,14 @@ IDR_MAINFRAME ICON "res\\app.ico" IDI_APP_RUNNING ICON "res\\app_running.ico" + +///////////////////////////////////////////////////////////////////////////// +// +// RT_MANIFEST +// + +1 RT_MANIFEST "res\\app.manifest" + #endif // Neutral (Default) resources ///////////////////////////////////////////////////////////////////////////// @@ -93,8 +101,8 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTERMOUSE | WS_POPUP | WS_CAPTION | WS_S CAPTION "About" FONT 9, "Segoe UI", 0, 0, 0x0 BEGIN - DEFPUSHBUTTON "O&K",IDOK,185,113,50,14 - PUSHBUTTON "&Back To Menu",IDRETRY,131,113,50,14 + DEFPUSHBUTTON "O&K",IDOK,179,106,50,14 + PUSHBUTTON "&Back To Menu",IDRETRY,122,106,50,14 ICON IDR_MAINFRAME,IDC_STATIC,12,15,20,20 GROUPBOX "",IDC_STATIC,7,7,228,120 LTEXT "Product Name",IDC_TXT_PRODUCTNAME,41,15,125,18 @@ -134,6 +142,7 @@ BEGIN HORZGUIDE, 15 HORZGUIDE, 39 HORZGUIDE, 68 + HORZGUIDE, 120 END END #endif // APSTUDIO_INVOKED diff --git a/src/app/WinPinMenu.vcxproj b/src/app/WinPinMenu.vcxproj index 3e32b0a..6aa20ec 100644 --- a/src/app/WinPinMenu.vcxproj +++ b/src/app/WinPinMenu.vcxproj @@ -98,31 +98,37 @@ true ..\..\build\$(Configuration)-$(PlatformShortName)\ ..\..\build\$(Configuration)-$(PlatformShortName)\ + false true ..\..\build\$(Configuration)-$(PlatformShortName)\ ..\..\build\$(Configuration)-$(PlatformShortName)\ + false true ..\..\build\$(Configuration)-$(PlatformShortName)\ ..\..\build\$(Configuration)-$(PlatformShortName)\ + false false ..\..\build\$(Configuration)-$(PlatformShortName)\ ..\..\build\$(Configuration)-$(PlatformShortName)\ + false false ..\..\build\$(Configuration)-$(PlatformShortName)\ ..\..\build\$(Configuration)-$(PlatformShortName)\ + false false ..\..\build\$(Configuration)-$(PlatformShortName)\ ..\..\build\$(Configuration)-$(PlatformShortName)\ + false @@ -345,6 +351,9 @@ + + + diff --git a/src/app/WinPinMenu.vcxproj.filters b/src/app/WinPinMenu.vcxproj.filters index f66c35a..fc7335f 100644 --- a/src/app/WinPinMenu.vcxproj.filters +++ b/src/app/WinPinMenu.vcxproj.filters @@ -94,4 +94,9 @@ Resource Files + + + Resource Files + + \ No newline at end of file diff --git a/src/app/app.manifest.in b/src/app/app.manifest.in new file mode 100644 index 0000000..dbead88 --- /dev/null +++ b/src/app/app.manifest.in @@ -0,0 +1,35 @@ + + + + ${PRODUCT_DESCRIPTON} + + + + + + + + + + + + + + + PerMonitorV2, system + true + + + + + + + + + + + + + + + diff --git a/src/app/productmeta.h b/src/app/productmeta.h index 5a53b26..3f72ec5 100644 --- a/src/app/productmeta.h +++ b/src/app/productmeta.h @@ -4,7 +4,7 @@ #define PRODUCT_VERSION_MAJOR 0 #define PRODUCT_VERSION_MINOR 1 #define PRODUCT_VERSION_PATCH 1 -#define PRODUCT_VERSION_TWEAK 26 +#define PRODUCT_VERSION_TWEAK 28 #define PRODUCT_NAME _T("WinPinMenu\0") #define PRODUCT_DESCRIPTION _T("diVISION Pinnable Taskbar Menu For Windows\0") #define PRODUCT_HOMEPAGE_URL _T("https://github.com/hatelamers/WinPinMenu\0") @@ -19,5 +19,5 @@ #define FILE_NAME _T("WinPinMenu\0") #define FILE_DESCRIPTION _T("diVISION Pinnable Taskbar Menu For Windows\0") -#define FILE_VERSION 0,1,1,26 -#define FILE_VERSION_S _T("0.1.1.26\0") +#define FILE_VERSION 0,1,1,28 +#define FILE_VERSION_S _T("0.1.1.28\0") diff --git a/src/app/res/app.manifest b/src/app/res/app.manifest new file mode 100644 index 0000000..7651d54 --- /dev/null +++ b/src/app/res/app.manifest @@ -0,0 +1,35 @@ + + + + diVISION Pinnable Taskbar Menu For Windows + + + + + + + + + + + + + + + PerMonitorV2, system + true + + + + + + + + + + + + + + + diff --git a/src/app/resource.h b/src/app/resource.h index ad8c9a6..907f884 100644 --- a/src/app/resource.h +++ b/src/app/resource.h @@ -7,6 +7,7 @@ #define IDR_MAINFRAME 128 #define IDI_ICON1 202 #define IDI_APP_RUNNING 202 +#define IDR_RT_MANIFEST1 203 #define IDC_TXT_PRODUCTNAME 1000 #define IDC_LNK_LICENSE 1001 #define IDC_TXT_COMPANYNAME 1002 @@ -15,7 +16,6 @@ #define IDC_TXT_PRODUCTVERSION 1005 #define IDC_TXT_FILEVERSION 1006 #define IDC_TXT_LEGALCOPYRIGHT 1007 -#define IDC_BUTTON1 1008 #define IDS_EMPTY_FOLDER 32772 #define IDS_INVALID_SOURCE 32773 #define ID_FILE_NOACTIONSOURCE 32775 @@ -24,7 +24,7 @@ // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 203 +#define _APS_NEXT_RESOURCE_VALUE 204 #define _APS_NEXT_COMMAND_VALUE 32776 #define _APS_NEXT_CONTROL_VALUE 1009 #define _APS_NEXT_SYMED_VALUE 102 diff --git a/src/app/stdafx.h b/src/app/stdafx.h index 43da8f6..37a0e0b 100644 --- a/src/app/stdafx.h +++ b/src/app/stdafx.h @@ -10,6 +10,8 @@ #include +#include + #include #if (_ATL_VER >= 0x0700) #include @@ -31,18 +33,21 @@ extern CAppModule _Module; #include #include #include +#include #include -#if defined _M_IX86 - #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") -#elif defined _M_IA64 - #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") -#elif defined _M_X64 - #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") -#else - #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") -#endif +//#if defined _M_IX86 +// #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='x86' publicKeyToken='6595b64144ccf1df' language='*'\"") +//#elif defined _M_IA64 +// #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='ia64' publicKeyToken='6595b64144ccf1df' language='*'\"") +//#elif defined _M_X64 +// #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='amd64' publicKeyToken='6595b64144ccf1df' language='*'\"") +//#else +// #pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +//#endif #include "taskbarautomation.h" #include "uxthemehelper.h" + +extern CUxTheme uxTheme; \ No newline at end of file diff --git a/src/app/targetver.h b/src/app/targetver.h index 3b6eecc..7390d7e 100644 --- a/src/app/targetver.h +++ b/src/app/targetver.h @@ -1,6 +1,6 @@ #pragma once // Change these values to use different versions -#define WINVER 0x0601 -#define _WIN32_WINNT 0x0601 -#define _WIN32_IE 0x0700 +#define WINVER 0x0A00 +#define _WIN32_WINNT 0x0A00 +#define _WIN32_IE 0x0A00 #define _RICHEDIT_VER 0x0500 diff --git a/src/app/uxthemehelper.h b/src/app/uxthemehelper.h index 04927c7..12aab7d 100644 --- a/src/app/uxthemehelper.h +++ b/src/app/uxthemehelper.h @@ -1,5 +1,7 @@ #pragma once +#pragma comment(lib, "Dwmapi.lib") + enum class PreferredAppMode { Default, @@ -9,9 +11,50 @@ enum class PreferredAppMode 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 FnShouldAppsUseDarkMode = bool (WINAPI*)(); // ordinal 132 using FnAllowDarkModeForWindow = bool (WINAPI*)(HWND hWnd, bool allow); // ordinal 133 using FnSetPreferredAppMode = PreferredAppMode(WINAPI*)(PreferredAppMode appMode); // ordinal 135, in 1903 +using FnSetWindowCompositionAttribute = BOOL(WINAPI*)(HWND hWnd, WINDOWCOMPOSITIONATTRIBDATA*); class CUxTheme { @@ -30,6 +73,11 @@ class CUxTheme m_pfnSetPreferredAppMode = (FnSetPreferredAppMode)::GetProcAddress(m_hUxtheme, MAKEINTRESOURCEA(135)); ATLASSERT(m_pfnSetPreferredAppMode); } + auto hModule = ::GetModuleHandle(_T("user32.dll")); + if (hModule) + { + m_pfnSetWindowCompositionAttribute = reinterpret_cast(::GetProcAddress(hModule, "SetWindowCompositionAttribute")); + } } ~CUxTheme() @@ -45,18 +93,48 @@ class CUxTheme return false; } + PreferredAppMode SetPreferredAppMode(PreferredAppMode appMode) const + { + if (m_pfnSetPreferredAppMode) + return m_pfnSetPreferredAppMode(appMode); + return PreferredAppMode::Default; + } + bool AllowDarkModeForWindow(HWND hWnd, bool allow) const { + DWMNCRENDERINGPOLICY ncrp = allow ? DWMNCRP_ENABLED : DWMNCRP_DISABLED; + ::DwmSetWindowAttribute(hWnd, DWMWA_NCRENDERING_POLICY, &ncrp, sizeof(ncrp)); + + auto result = false; if (m_pfnAllowDarkModeForWindow) - return m_pfnAllowDarkModeForWindow(hWnd, allow); - return false; + result = m_pfnAllowDarkModeForWindow(hWnd, allow); + + return result; } - PreferredAppMode SetPreferredAppMode(PreferredAppMode appMode) const + BOOL SetWindowCompositionAttribute(HWND hWnd, WINDOWCOMPOSITIONATTRIB attr, BOOL value) const { - if (m_pfnSetPreferredAppMode) - return m_pfnSetPreferredAppMode(appMode); - return PreferredAppMode::Default; + if (m_pfnSetWindowCompositionAttribute) + { + WINDOWCOMPOSITIONATTRIBDATA data = { attr, &value, sizeof(value) }; + return m_pfnSetWindowCompositionAttribute(hWnd, &data); + } + return FALSE; + } + + bool SwitchWindowDarkMode(HWND hWnd, bool setDark, bool immersive = false) const + { + auto result = false; + if (immersive) + { + BOOL value = setDark ? TRUE : FALSE; + result = SUCCEEDED(::DwmSetWindowAttribute(hWnd, DWMWA_USE_IMMERSIVE_DARK_MODE, &value, sizeof(value))); + } + else + { + result = SetWindowCompositionAttribute(hWnd, WINDOWCOMPOSITIONATTRIB::WCA_USEDARKMODECOLORS, setDark ? TRUE : FALSE); + } + return result; } private: @@ -65,5 +143,6 @@ class CUxTheme FnAllowDarkModeForWindow m_pfnAllowDarkModeForWindow; FnShouldAppsUseDarkMode m_pfnShouldAppsUseDarkMode; FnSetPreferredAppMode m_pfnSetPreferredAppMode; + FnSetWindowCompositionAttribute m_pfnSetWindowCompositionAttribute; };