From 368043a1664b6170dfe5f8fda47cc5524113a260 Mon Sep 17 00:00:00 2001 From: hatelamers <97636078+hatelamers@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:18:30 +0100 Subject: [PATCH] #6 added support for multi-monitor taskbar --- .gitignore | 1 + buildenv/Product.cmake | 2 +- buildenv/buildnumber.txt | 2 +- src/app/AboutDlg.h | 1 + src/app/MainFrm.cpp | 58 +++++++++++++-------- src/app/WinPinMenu.rc | 9 ++-- src/app/productmeta.h | 14 ++--- src/app/resource.h | 3 +- src/app/taskbarautomation.h | 100 ++++++++++++++++++++++-------------- 9 files changed, 116 insertions(+), 74 deletions(-) diff --git a/.gitignore b/.gitignore index 85cfabd..7a27b50 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ /build **/LocalBuild.* +/test/data # User-specific files *.rsuser diff --git a/buildenv/Product.cmake b/buildenv/Product.cmake index 4e46e9b..3e19104 100644 --- a/buildenv/Product.cmake +++ b/buildenv/Product.cmake @@ -1,6 +1,6 @@ set(PRODUCT_VERSION_MAJOR 0) set(PRODUCT_VERSION_MINOR 1) -set(PRODUCT_VERSION_PATCH 0) +set(PRODUCT_VERSION_PATCH 1) set(PRODUCT_NAME WinPinMenu) set(PRODUCT_VERSION ${PRODUCT_VERSION_MAJOR}.${PRODUCT_VERSION_MINOR}.${PRODUCT_VERSION_PATCH}.${BUILD_NUMBER}) set(PRODUCT_VENDOR "diVISION") diff --git a/buildenv/buildnumber.txt b/buildenv/buildnumber.txt index cabf43b..978b4e8 100644 --- a/buildenv/buildnumber.txt +++ b/buildenv/buildnumber.txt @@ -1 +1 @@ -24 \ No newline at end of file +26 \ No newline at end of file diff --git a/src/app/AboutDlg.h b/src/app/AboutDlg.h index ebd2d29..2223695 100644 --- a/src/app/AboutDlg.h +++ b/src/app/AboutDlg.h @@ -15,6 +15,7 @@ class CAboutDlg BEGIN_MSG_MAP(CAboutDlg) MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog) COMMAND_ID_HANDLER(IDOK, OnCloseCmd) + COMMAND_ID_HANDLER(IDRETRY, OnCloseCmd) COMMAND_ID_HANDLER(IDCANCEL, OnCloseCmd) END_MSG_MAP() diff --git a/src/app/MainFrm.cpp b/src/app/MainFrm.cpp index 5322a3a..4f98e8d 100644 --- a/src/app/MainFrm.cpp +++ b/src/app/MainFrm.cpp @@ -128,8 +128,14 @@ LRESULT CMainFrame::OnFileExit(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCt LRESULT CMainFrame::OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/) { CAboutDlg dlg; - dlg.DoModal(); - TriggerActionPopup(); + if (IDRETRY == dlg.DoModal()) + { + TriggerActionPopup(); + } + else + { + PostMessage(WM_CLOSE); + } return 0; } @@ -228,51 +234,59 @@ INT CMainFrame::TriggerActionPopup() POINT CMainFrame::CalculatePopupPosision() { - POINT result{ 0,0 }; + ATLTRACE(__FUNCTION__ _T(" START\n")); + POINT result{ -1,-1 }; + POINT cursorPos{ 0,0 }; + if (::GetCursorPos(&cursorPos)) + { + result.x = cursorPos.x; + result.y = cursorPos.y; + } + CTaskbarAutomation tba; - tba.ForEveryButton([this,&result](const CComPtr& button, int) + auto hrButtons = tba.ForEveryButton([this,&result,&cursorPos](const CComPtr& button, int /*tbIndex*/, int /*btnIndex*/) { - CComBSTR bstrName; - auto hr = button->get_CurrentName(&bstrName); - //ATLTRACE(_T("Button[%d].Name=%s\n"), index, (LPCOLESTR)bstrName); - if (SUCCEEDED(hr)) + RECT rc{ 0,0,0,0 }; + if (SUCCEEDED(button->get_CachedBoundingRectangle(&rc))) { - CString strName(bstrName); - CString strTitle; - GetWindowText(strTitle); - if (0 == strName.Find(strTitle)) + auto hasFocus = FALSE; + button->get_CurrentHasKeyboardFocus(&hasFocus); + if (!hasFocus) { - RECT rc{ 0,0,0,0 }; - if (SUCCEEDED(button->get_CurrentBoundingRectangle(&rc))) - { - result.x = rc.left; - result.y = rc.top; - } + hasFocus = (cursorPos.x >= rc.left && cursorPos.x <= rc.right && cursorPos.y >= rc.top && cursorPos.y <= rc.bottom); + } + ATLTRACE(__FUNCTION__ _T(" taskbar button at x=%d y=%d, hasFocus=%d\n"), rc.left, rc.top, hasFocus); + if (hasFocus) + { + result.x = 10 > rc.left ? rc.right : rc.left; + result.y = 10 > rc.top ? rc.bottom + 2 : rc.top - 2; return false; } } return true; }); - if (0 == result.x && 0 == result.y) + if (S_FALSE != hrButtons) { - if (!::GetCursorPos(&result)) + if (-1 == result.x) { result.x = 100; result.y = -1; } + auto hTaskBar = ::FindWindow(_T("Shell_TrayWnd"), NULL); if (hTaskBar) { RECT rc{ 0,0,0,0 }; if (::GetWindowRect(hTaskBar, &rc) && (0 > result.y - || (result.x >= rc.left && result.x <= rc.right && result.y >= rc.top && result.y <= rc.bottom))) + || (cursorPos.x >= rc.left && cursorPos.x <= rc.right && cursorPos.y >= rc.top && cursorPos.y <= rc.bottom))) { - result.y = rc.top; + result.y = 10 > rc.top ? rc.bottom + 2 : rc.top - 2; } } } + ATLTRACE(__FUNCTION__ _T(" END\n")); return result; } diff --git a/src/app/WinPinMenu.rc b/src/app/WinPinMenu.rc index 46d110d..e251f7f 100644 --- a/src/app/WinPinMenu.rc +++ b/src/app/WinPinMenu.rc @@ -88,14 +88,15 @@ END // Dialog // -IDD_ABOUTBOX DIALOGEX 0, 0, 242, 121 +IDD_ABOUTBOX DIALOGEX 0, 0, 242, 134 STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTERMOUSE | WS_POPUP | WS_CAPTION | WS_SYSMENU CAPTION "About" FONT 9, "Segoe UI", 0, 0, 0x0 BEGIN - DEFPUSHBUTTON "OK",IDOK,185,100,50,14 + DEFPUSHBUTTON "O&K",IDOK,185,113,50,14 + PUSHBUTTON "&Back To Menu",IDRETRY,131,113,50,14 ICON IDR_MAINFRAME,IDC_STATIC,12,15,20,20 - GROUPBOX "",IDC_STATIC,7,7,228,107 + GROUPBOX "",IDC_STATIC,7,7,228,120 LTEXT "Product Name",IDC_TXT_PRODUCTNAME,41,15,125,18 RTEXT "License",IDC_LNK_LICENSE,172,15,57,8 RTEXT "Company Name",IDC_TXT_COMPANYNAME,172,26,57,8 @@ -129,7 +130,7 @@ BEGIN VERTGUIDE, 172 VERTGUIDE, 229 TOPMARGIN, 7 - BOTTOMMARGIN, 114 + BOTTOMMARGIN, 127 HORZGUIDE, 15 HORZGUIDE, 39 HORZGUIDE, 68 diff --git a/src/app/productmeta.h b/src/app/productmeta.h index 16c1885..5a53b26 100644 --- a/src/app/productmeta.h +++ b/src/app/productmeta.h @@ -3,21 +3,21 @@ #define PRODUCT_VERSION_MAJOR 0 #define PRODUCT_VERSION_MINOR 1 -#define PRODUCT_VERSION_PATCH 0 -#define PRODUCT_VERSION_TWEAK 24 +#define PRODUCT_VERSION_PATCH 1 +#define PRODUCT_VERSION_TWEAK 26 #define PRODUCT_NAME _T("WinPinMenu\0") #define PRODUCT_DESCRIPTION _T("diVISION Pinnable Taskbar Menu For Windows\0") -#define PRODUCT_HOMEPAGE_URL _T("https://winpinmenu.sourceforge.net\0") +#define PRODUCT_HOMEPAGE_URL _T("https://github.com/hatelamers/WinPinMenu\0") #define PRODUCT_VENDOR _T("diVISION\0") #define PRODUCT_LICENSE _T("GNU/GPL\0") #define PRODUCT_LICENSE_URL _T("https://www.gnu.org/licenses/gpl-3.0.en.html\0") #define PRODUCT_COPYRIGHT _T("© 2024, some rights reserved\0") -#define PRODUCT_VERSION 0,1,0 -#define PRODUCT_VERSION_S _T("0.1.0\0") +#define PRODUCT_VERSION 0,1,1 +#define PRODUCT_VERSION_S _T("0.1.1\0") #define FILE_NAME _T("WinPinMenu\0") #define FILE_DESCRIPTION _T("diVISION Pinnable Taskbar Menu For Windows\0") -#define FILE_VERSION 0,1,0,24 -#define FILE_VERSION_S _T("0.1.0.24\0") +#define FILE_VERSION 0,1,1,26 +#define FILE_VERSION_S _T("0.1.1.26\0") diff --git a/src/app/resource.h b/src/app/resource.h index f4b4747..ad8c9a6 100644 --- a/src/app/resource.h +++ b/src/app/resource.h @@ -15,6 +15,7 @@ #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 @@ -25,7 +26,7 @@ #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 203 #define _APS_NEXT_COMMAND_VALUE 32776 -#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_CONTROL_VALUE 1009 #define _APS_NEXT_SYMED_VALUE 102 #endif #endif diff --git a/src/app/taskbarautomation.h b/src/app/taskbarautomation.h index 0751dd9..f902f2e 100644 --- a/src/app/taskbarautomation.h +++ b/src/app/taskbarautomation.h @@ -16,7 +16,7 @@ class CTaskbarAutomation bool IsReady() const noexcept { - return m_pFirstElement && m_pSearchConditionButton; + return m_pTaskbars && m_pSearchConditionButton; } template @@ -27,37 +27,51 @@ class CTaskbarAutomation ATLTRACE2(_T("CTaskbarAutomation hasn't been initialed\n")); return E_FAIL; } - CComPtr pTaskbarButtonElements; - auto hr = m_pFirstElement->FindAll(TreeScope_Descendants, m_pSearchConditionButton, &pTaskbarButtonElements); - if (SUCCEEDED(hr)) + + auto tbCount = 0; + auto hr = m_pTaskbars->get_Length(&tbCount); + ATLTRACE(__FUNCTION__ _T(" %d taskbars\n"), tbCount); + for (auto i = 0; SUCCEEDED(hr) && i < tbCount; i++) { - auto count = 0; - hr = pTaskbarButtonElements->get_Length(&count); - for (auto i = 0; SUCCEEDED(hr) && i < count; i++) + CComPtr pTaskbar; + hr = m_pTaskbars->GetElement(i, &pTaskbar); + if (SUCCEEDED(hr)) { - CComPtr pButton; - hr = pTaskbarButtonElements->GetElement(i, &pButton); - if (SUCCEEDED(hr) && !itemReceiver(pButton, i)) + CComPtr pTaskbarButtonElements; + hr = pTaskbar->FindAllBuildCache(TreeScope_Descendants, m_pSearchConditionButton, m_pCacheRequestButton, &pTaskbarButtonElements); + if (SUCCEEDED(hr)) { - hr = S_FALSE; - break; + auto btnCount = 0; + hr = pTaskbarButtonElements->get_Length(&btnCount); + for (auto j = 0; SUCCEEDED(hr) && j < btnCount; j++) + { + CComPtr pButton; + hr = pTaskbarButtonElements->GetElement(j, &pButton); + if (SUCCEEDED(hr) && !itemReceiver(pButton, i, j)) + { + hr = S_FALSE; + break; + } + } } + } } + return hr; } protected: bool Initialize() { - if (m_pFirstElement && m_pSearchConditionButton) + ATLTRACE(__FUNCTION__ _T(" START\n")); + if (m_pTaskbars && m_pSearchConditionButton) return true; auto hr = S_OK; if (!m_pAutomation) { hr = m_pAutomation.CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER); - //HRESULT hr = CoCreateInstance(__uuidof(CUIAutomation), NULL, CLSCTX_INPROC_SERVER, __uuidof(IUIAutomation), (void**)&m_pAutomation); if (FAILED(hr)) return false; } @@ -65,35 +79,44 @@ class CTaskbarAutomation hr = m_pAutomation->GetRootElement(&pRootElement); if (FAILED(hr)) return false; + CComPtr pPrimTaskbarCondition; + hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"Shell_TrayWnd"), &pPrimTaskbarCondition); + if (FAILED(hr)) return false; + + CComPtr pSecondTaskbarCondition; + hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"Shell_SecondaryTrayWnd"), &pSecondTaskbarCondition); + if (FAILED(hr)) return false; + CComPtr pTaskbarCondition; - hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"MSTaskListWClass"), &pTaskbarCondition); - if (SUCCEEDED(hr)) - { - hr = pRootElement->FindFirst(TreeScope_Descendants, pTaskbarCondition, &m_pFirstElement); - if (SUCCEEDED(hr) && m_pFirstElement) - { - hr = m_pAutomation->CreatePropertyCondition(UIA_ControlTypePropertyId, CComVariant(UIA_ButtonControlTypeId), &m_pSearchConditionButton); - if (FAILED(hr)) return false; - } - } + hr = m_pAutomation->CreateOrCondition(pPrimTaskbarCondition, pSecondTaskbarCondition, &pTaskbarCondition); + if (FAILED(hr)) return false; - if (!m_pFirstElement) - { - if (pTaskbarCondition) - { - pTaskbarCondition.Release(); - } - hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"Shell_TrayWnd"), &pTaskbarCondition); - if (FAILED(hr)) return false; + hr = pRootElement->FindAll(TreeScope_Children, pTaskbarCondition, &m_pTaskbars); + if (FAILED(hr) || !m_pTaskbars) return false; - hr = pRootElement->FindFirst(TreeScope_Children, pTaskbarCondition, &m_pFirstElement); - if (FAILED(hr) || !m_pFirstElement) return false; + //CComPtr pTaskListCondition; + //hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"MSTaskListWClass"), &pTaskListCondition); + //if (FAILED(hr)) return false; - hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"Taskbar.TaskListButtonAutomationPeer"), &m_pSearchConditionButton); - if (FAILED(hr)) return false; - } + CComPtr pSearchConditionUXButton; + hr = m_pAutomation->CreatePropertyCondition(UIA_ClassNamePropertyId, CComVariant(L"Taskbar.TaskListButtonAutomationPeer"), &pSearchConditionUXButton); + if (FAILED(hr)) return false; + + CComPtr pSearchConditionButton; + hr = m_pAutomation->CreatePropertyCondition(UIA_ControlTypePropertyId, CComVariant(UIA_ButtonControlTypeId), &pSearchConditionButton); + if (FAILED(hr)) return false; + + hr = m_pAutomation->CreateOrCondition(pSearchConditionButton, pSearchConditionUXButton, &m_pSearchConditionButton); + if (FAILED(hr)) return false; + + hr = m_pAutomation->CreateCacheRequest(&m_pCacheRequestButton); + if (FAILED(hr)) return false; + + hr = m_pCacheRequestButton->AddProperty(UIA_BoundingRectanglePropertyId); + if (FAILED(hr)) return false; + ATLTRACE(__FUNCTION__ _T(" END\n")); return true; } @@ -101,7 +124,8 @@ class CTaskbarAutomation protected: CComPtr m_pAutomation; - CComPtr m_pFirstElement; + CComPtr m_pTaskbars; CComPtr m_pSearchConditionButton; + CComPtr m_pCacheRequestButton; };