From f5f7d893766fa0b217d116ff29f4a2e315ee0623 Mon Sep 17 00:00:00 2001 From: antmor <43587397+antmor@users.noreply.github.com> Date: Tue, 20 Sep 2022 20:17:49 -0700 Subject: [PATCH 1/4] change items on focus (based on titles, todo otherwise) New item doesn't work yet. --- .../MainWindow.xaml.cpp | 79 ---------- .../MainWindow.xaml.cpp | 75 ---------- .../MainWindow.xaml.cpp | 75 ---------- vertical_tasks/App.xaml.cpp | 73 ++++----- vertical_tasks/MainWindow.xaml.cpp | 138 ++++++++++++++++-- vertical_tasks/MainWindow.xaml.h | 3 + vertical_tasks/ShellHookMessages.h | 103 +++++++++++++ vertical_tasks/pch.h | 5 +- vertical_tasks/vertical_tasks.vcxproj | 1 + vertical_tasks/vertical_tasks.vcxproj.filters | 1 + 10 files changed, 276 insertions(+), 277 deletions(-) delete mode 100644 enc_temp_folder/1833592448cd2768ab9e3cba28aa33/MainWindow.xaml.cpp delete mode 100644 enc_temp_folder/afd2ce2136732e5a1fb517b019afd650/MainWindow.xaml.cpp delete mode 100644 enc_temp_folder/c3545e6cd3c76d8eb39ef886a3f8ca/MainWindow.xaml.cpp create mode 100644 vertical_tasks/ShellHookMessages.h diff --git a/enc_temp_folder/1833592448cd2768ab9e3cba28aa33/MainWindow.xaml.cpp b/enc_temp_folder/1833592448cd2768ab9e3cba28aa33/MainWindow.xaml.cpp deleted file mode 100644 index 9c7206b..0000000 --- a/enc_temp_folder/1833592448cd2768ab9e3cba28aa33/MainWindow.xaml.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "pch.h" -#include "MainWindow.xaml.h" -#if __has_include("MainWindow.g.cpp") -#include "MainWindow.g.cpp" -#endif - -using namespace winrt; -using namespace Microsoft::UI::Xaml; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace winrt::vertical_tasks::implementation -{ - MainWindow::MainWindow() - { - InitializeComponent(); - } - - int32_t MainWindow::MyProperty() - { - throw hresult_not_implemented(); - } - - void MainWindow::MyProperty(int32_t /* value */) - { - throw hresult_not_implemented(); - } - struct WinData - { - HWND hwnd; - std::wstring title; - - }; - - std::vector g_windows; - - BOOL CALLBACK WindowEnumerationCallBack(HWND hwnd, LPARAM /*lParam*/) - { - if (IsWindow(hwnd) && IsWindowVisible(hwnd) && (0 == GetWindow(hwnd, GW_OWNER))) - { - std::wstring title; - const auto size = GetWindowTextLength(hwnd); - if (size > 0) - { - - std::vector buffer(size + 1); - GetWindowText(hwnd, buffer.data(), size + 1); - - g_windows.emplace_back(WinData{ hwnd, std::wstring(std::begin(buffer), std::end(buffer)) }); - } - } - // keep on looping - return true; - } - - void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&) - { - myButton().Content(box_value(L"Clicked")); - EnumWindows(&WindowEnumerationCallBack, 0); - std::vector newTitles; - newTitles.reserve(g_windows.size()); - - for (auto&& winData : g_windows) - { - if (!winData.title.empty()) - { - newTitles.emplace_back(winrt::box_value(winData.title)); - } - } - m_windowTitles.ReplaceAll(newTitles); - } - - Windows::Foundation::Collections::IObservableVector MainWindow::WindowTitles() - { - return m_windowTitles; - } - -} diff --git a/enc_temp_folder/afd2ce2136732e5a1fb517b019afd650/MainWindow.xaml.cpp b/enc_temp_folder/afd2ce2136732e5a1fb517b019afd650/MainWindow.xaml.cpp deleted file mode 100644 index 31e5eb9..0000000 --- a/enc_temp_folder/afd2ce2136732e5a1fb517b019afd650/MainWindow.xaml.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "pch.h" -#include "MainWindow.xaml.h" -#if __has_include("MainWindow.g.cpp") -#include "MainWindow.g.cpp" -#endif - -using namespace winrt; -using namespace Microsoft::UI::Xaml; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace winrt::vertical_tasks::implementation -{ - MainWindow::MainWindow() - { - InitializeComponent(); - } - - int32_t MainWindow::MyProperty() - { - throw hresult_not_implemented(); - } - - void MainWindow::MyProperty(int32_t /* value */) - { - throw hresult_not_implemented(); - } - struct WinData - { - HWND hwnd; - std::wstring title; - - }; - - std::vector g_windows; - - BOOL CALLBACK WindowEnumerationCallBack(HWND hwnd, LPARAM /*lParam*/) - { - std::wstring title; - const auto size = GetWindowTextLength(hwnd); - if (size > 0) - { - std::vector buffer(size+1); - GetWindowText(hwnd, buffer.data(), size+1); - - g_windows.emplace_back(WinData{ hwnd, std::wstring(std::begin(buffer), std::end(buffer)) }); - } - // keep on looping - return true; - } - - void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&) - { - myButton().Content(box_value(L"Clicked")); - EnumWindows(&WindowEnumerationCallBack, 0); - std::vector newTitles; - newTitles.reserve(g_windows.size()); - - for (auto&& winData : g_windows) - { - if (!winData.title.empty()) - { - newTitles.emplace_back(winrt::box_value(winData.title)); - } - } - m_windowTitles.ReplaceAll(newTitles); - } - - Windows::Foundation::Collections::IObservableVector MainWindow::WindowTitles() - { - return m_windowTitles; - } - -} diff --git a/enc_temp_folder/c3545e6cd3c76d8eb39ef886a3f8ca/MainWindow.xaml.cpp b/enc_temp_folder/c3545e6cd3c76d8eb39ef886a3f8ca/MainWindow.xaml.cpp deleted file mode 100644 index e227cd7..0000000 --- a/enc_temp_folder/c3545e6cd3c76d8eb39ef886a3f8ca/MainWindow.xaml.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "pch.h" -#include "MainWindow.xaml.h" -#if __has_include("MainWindow.g.cpp") -#include "MainWindow.g.cpp" -#endif - -using namespace winrt; -using namespace Microsoft::UI::Xaml; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. - -namespace winrt::vertical_tasks::implementation -{ - MainWindow::MainWindow() - { - InitializeComponent(); - } - - int32_t MainWindow::MyProperty() - { - throw hresult_not_implemented(); - } - - void MainWindow::MyProperty(int32_t /* value */) - { - throw hresult_not_implemented(); - } - struct WinData - { - HWND hwnd; - std::wstring title; - - }; - - std::vector g_windows; - - BOOL CALLBACK WindowEnumerationCallBack(HWND hwnd, LPARAM /*lParam*/) - { - std::wstring title; - const auto size = GetWindowTextLength(hwnd); - if (size > 0) - { - std::vector buffer(size+1); - GetWindowText(hwnd, buffer.data(), size); - - g_windows.emplace_back(WinData{ hwnd, std::wstring(std::begin(buffer), std::end(buffer)) }); - } - // keep on looping - return true; - } - - void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&) - { - myButton().Content(box_value(L"Clicked")); - EnumWindows(&WindowEnumerationCallBack, 0); - std::vector newTitles; - newTitles.reserve(g_windows.size()); - - for (auto&& winData : g_windows) - { - if (!winData.title.empty()) - { - newTitles.emplace_back(winrt::box_value(winData.title)); - } - } - m_windowTitles.ReplaceAll(newTitles); - } - - Windows::Foundation::Collections::IObservableVector MainWindow::WindowTitles() - { - return m_windowTitles; - } - -} diff --git a/vertical_tasks/App.xaml.cpp b/vertical_tasks/App.xaml.cpp index 12181f9..83101a3 100644 --- a/vertical_tasks/App.xaml.cpp +++ b/vertical_tasks/App.xaml.cpp @@ -2,45 +2,50 @@ #include "App.xaml.h" #include "MainWindow.xaml.h" +#include -using namespace winrt; -using namespace Windows::Foundation; -using namespace Microsoft::UI::Xaml; -using namespace Microsoft::UI::Xaml::Controls; -using namespace Microsoft::UI::Xaml::Navigation; -using namespace vertical_tasks; -using namespace vertical_tasks::implementation; - -// To learn more about WinUI, the WinUI project structure, -// and more about our project templates, see: http://aka.ms/winui-project-info. +namespace winrt +{ + using namespace Windows::Foundation; + using namespace Microsoft::UI::Xaml; + using namespace Microsoft::UI::Xaml::Controls; + using namespace Microsoft::UI::Xaml::Navigation; + using namespace vertical_tasks; +} -/// -/// Initializes the singleton application object. This is the first line of authored code -/// executed, and as such is the logical equivalent of main() or WinMain(). -/// -App::App() +namespace winrt::vertical_tasks::implementation { - InitializeComponent(); + // To learn more about WinUI, the WinUI project structure, + // and more about our project templates, see: http://aka.ms/winui-project-info. -#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION - UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e) + /// + /// Initializes the singleton application object. This is the first line of authored code + /// executed, and as such is the logical equivalent of main() or WinMain(). + /// + App::App() { - if (IsDebuggerPresent()) - { - auto errorMessage = e.Message(); - __debugbreak(); - } - }); + InitializeComponent(); + +#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION + UnhandledException([this](winrt::IInspectable const&, winrt::UnhandledExceptionEventArgs const& e) + { + if (IsDebuggerPresent()) + { + auto errorMessage = e.Message(); + __debugbreak(); + } + }); #endif -} + } -/// -/// Invoked when the application is launched normally by the end user. Other entry points -/// will be used such as when the application is launched to open a specific file. -/// -/// Details about the launch request and process. -void App::OnLaunched(LaunchActivatedEventArgs const&) -{ - window = make(); - window.Activate(); + /// + /// Invoked when the application is launched normally by the end user. Other entry points + /// will be used such as when the application is launched to open a specific file. + /// + /// Details about the launch request and process. + void App::OnLaunched(winrt::LaunchActivatedEventArgs const&) + { + window = winrt::make(); + window.Activate(); + } } \ No newline at end of file diff --git a/vertical_tasks/MainWindow.xaml.cpp b/vertical_tasks/MainWindow.xaml.cpp index c0bafc2..325ad5f 100644 --- a/vertical_tasks/MainWindow.xaml.cpp +++ b/vertical_tasks/MainWindow.xaml.cpp @@ -4,9 +4,20 @@ #include "MainWindow.g.cpp" #endif -using namespace winrt; -using namespace Microsoft::UI::Xaml; +#include +#include +#include +#include + +#include "ShellHookMessages.h" + +#include + +namespace winrt +{ + using namespace Microsoft::UI::Xaml; +} // To learn more about WinUI, the WinUI project structure, // and more about our project templates, see: http://aka.ms/winui-project-info. @@ -34,6 +45,8 @@ namespace winrt::vertical_tasks::implementation }; std::vector g_windows; + UINT g_shellHookMsgId{ UINT_MAX }; + bool g_initialized{ false }; BOOL CALLBACK WindowEnumerationCallBack(HWND hwnd, LPARAM /*lParam*/) { @@ -43,11 +56,24 @@ namespace winrt::vertical_tasks::implementation const auto size = GetWindowTextLength(hwnd); if (size > 0) { - std::vector buffer(size + 1); GetWindowText(hwnd, buffer.data(), size + 1); - g_windows.emplace_back(WinData{ hwnd, std::wstring(std::begin(buffer), std::end(buffer)) }); + bool found{ false }; + for (auto&& winData : g_windows) + { + if (winData.hwnd == hwnd) + { + auto oldTitle = std::move(winData.title); + winData.title = std::wstring(std::begin(buffer), std::end(buffer)); + found = true; + break; + } + } + if (!found) + { + g_windows.emplace_back(WinData{ hwnd, std::wstring(std::begin(buffer), std::end(buffer)) }); + } } } // keep on looping @@ -56,20 +82,108 @@ namespace winrt::vertical_tasks::implementation void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&) { - myButton().Content(box_value(L"Clicked")); - g_windows.clear(); - EnumWindows(&WindowEnumerationCallBack, 0); - std::vector newTitles; - newTitles.reserve(g_windows.size()); + if (!g_initialized) + { + myButton().Content(box_value(L"Clicked")); + g_windows.clear(); + EnumWindows(&WindowEnumerationCallBack, 0); + std::vector newTitles; + newTitles.reserve(g_windows.size()); + + for (auto&& winData : g_windows) + { + if (!winData.title.empty()) + { + newTitles.emplace_back(winrt::box_value(winData.title)); + } + } + m_windowTitles.ReplaceAll(newTitles); + static ShellHookMessages s_myMessages; + s_myMessages.Register([weak_this = get_weak()](WPARAM wParam, LPARAM lParam) + { + auto strong_this = weak_this.get(); + { + strong_this->OnShellMessage(wParam, lParam); + } + }); + g_initialized = true; + } + } + + void MainWindow::SelectItem(HWND hwnd) + { + std::wstring_view titleToSelect; + for (auto&& winData : g_windows) + { + if (winData.hwnd == hwnd) + { + titleToSelect = winData.title; + break; + } + } + if (!titleToSelect.empty()) + { + for (auto&& item : m_windowTitles) + { + auto&& title = winrt::unbox_value(item); + if (title == titleToSelect) + { + myList().SelectedItem(item); + return; + } + } + } + } + + void MainWindow::DeleteItem(HWND hwnd) + { + std::wstring_view titleToDelete; for (auto&& winData : g_windows) { - if (!winData.title.empty()) + if (winData.hwnd == hwnd) + { + titleToDelete = winData.title; + break; + } + } + + if (!titleToDelete.empty()) + { + for (uint32_t i = 0; i < m_windowTitles.Size(); i++) { - newTitles.emplace_back(winrt::box_value(winData.title)); + auto&& title = winrt::unbox_value(m_windowTitles.GetAt(i)); + if (title == titleToDelete) + { + m_windowTitles.RemoveAt(i); + break; + } } } - m_windowTitles.ReplaceAll(newTitles); + } + + winrt::fire_and_forget MainWindow::OnShellMessage(WPARAM wParam, LPARAM lParam) + { + co_await wil::resume_foreground(DispatcherQueue()); + OutputDebugString(L"shell message"); + std::wstringstream myString; + myString << std::hex << wParam << L", " << lParam << std::endl; + OutputDebugString(myString.str().c_str()); + switch (wParam) + { + case HSHELL_WINDOWACTIVATED: + case HSHELL_RUDEAPPACTIVATED: + SelectItem(reinterpret_cast(lParam)); + break; + case HSHELL_WINDOWCREATED: + // add the window + WindowEnumerationCallBack(reinterpret_cast(lParam), 0); + SelectItem(reinterpret_cast(lParam)); + break; + case HSHELL_WINDOWDESTROYED: + DeleteItem(reinterpret_cast(lParam)); + break; + } } Windows::Foundation::Collections::IObservableVector MainWindow::WindowTitles() diff --git a/vertical_tasks/MainWindow.xaml.h b/vertical_tasks/MainWindow.xaml.h index 3646332..93fd484 100644 --- a/vertical_tasks/MainWindow.xaml.h +++ b/vertical_tasks/MainWindow.xaml.h @@ -14,6 +14,9 @@ namespace winrt::vertical_tasks::implementation Windows::Foundation::Collections::IObservableVector WindowTitles(); private: + winrt::fire_and_forget OnShellMessage(WPARAM wParam, LPARAM lParam); + void SelectItem(HWND hwnd); + void DeleteItem(HWND hwnd); Windows::Foundation::Collections::IObservableVector m_windowTitles{winrt::single_threaded_observable_vector()}; }; } diff --git a/vertical_tasks/ShellHookMessages.h b/vertical_tasks/ShellHookMessages.h new file mode 100644 index 0000000..e7a64c8 --- /dev/null +++ b/vertical_tasks/ShellHookMessages.h @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include + +const wchar_t* c_wzShellHookMessage = L"SHELLHOOK"; + +// retrieve the HINSTANCE for the current DLL or EXE using this symbol that +// the linker provides for every module, avoids the need for a global HINSTANCE variable +// and provides access to this value for static libraries +EXTERN_C IMAGE_DOS_HEADER __ImageBase; +__inline HINSTANCE GetModuleHINSTANCE() { return (HINSTANCE)&__ImageBase; } + +struct ShellHookMessages : std::enable_shared_from_this +{ + wil::unique_hwnd m_hwnd{}; + DWORD threadId{}; + UINT shellHookMsgId{}; + std::function callback; + + static LRESULT WINAPI StaticWndProc(HWND hwnd, UINT msgId, WPARAM wParam, LPARAM lParam) + { + ShellHookMessages* thisRef{ nullptr }; + if (msgId == WM_NCCREATE) + { + auto lpcs = reinterpret_cast(lParam); + thisRef = static_cast(lpcs->lpCreateParams); + SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(thisRef)); + } + else + { + thisRef = reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); + } + if (thisRef) + { + return thisRef->WndProc(hwnd, msgId, wParam, lParam); + } + return DefWindowProc(hwnd, msgId, wParam, lParam); + } + + ShellHookMessages() + { + WNDCLASSEX wc = { 0 }; + wc.lpfnWndProc = ShellHookMessages::StaticWndProc; + wc.cbSize = sizeof(wc); + wc.style = CS_DBLCLKS | CS_NOCLOSE; + wc.cbWndExtra = sizeof(ShellHookMessages*); + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.lpszClassName = L"ShellHookMessages"; + wc.hInstance = GetModuleHINSTANCE(); + THROW_LAST_ERROR_IF(RegisterClassEx(&wc) == 0); + + // Create the window. + // Pass "this" param so we can use derived class implementation later. + m_hwnd.reset(CreateWindowEx( + 0, // dwExStyle + L"ShellHookMessages", // lpClassName + L"ShellHookMessages", // lpClassName + 0, // dwStyle + 0, 0, 0, 0, // x, y, width, height + HWND_MESSAGE, // parent + nullptr, // hMenu + GetModuleHINSTANCE(), //hInstance + this // lParam + )); + THROW_LAST_ERROR_IF_NULL(m_hwnd); + } + + LRESULT WndProc(HWND hwnd, UINT msgId, WPARAM wParam, LPARAM lParam) + { + switch (msgId) + { + case WM_CREATE: + { + shellHookMsgId = RegisterWindowMessage(c_wzShellHookMessage); + RegisterShellHookWindow(hwnd); + return 0; + break; + } + case WM_DESTROY: + { + DeregisterShellHookWindow(hwnd); + + return 0; + break; + } + default: + if (msgId == shellHookMsgId) + { + callback(wParam, lParam); + } + break; + } + + return -1; + } + + void Register(std::function&& toRegister) + { + callback = toRegister; + } +}; \ No newline at end of file diff --git a/vertical_tasks/pch.h b/vertical_tasks/pch.h index 87057b1..905d67a 100644 --- a/vertical_tasks/pch.h +++ b/vertical_tasks/pch.h @@ -6,8 +6,9 @@ #include #include #include - - +#include +#include +#include // Undefine GetCurrentTime macro to prevent // conflict with Storyboard::GetCurrentTime #undef GetCurrentTime diff --git a/vertical_tasks/vertical_tasks.vcxproj b/vertical_tasks/vertical_tasks.vcxproj index 08a254b..a3809e8 100644 --- a/vertical_tasks/vertical_tasks.vcxproj +++ b/vertical_tasks/vertical_tasks.vcxproj @@ -109,6 +109,7 @@ MainWindow.xaml + diff --git a/vertical_tasks/vertical_tasks.vcxproj.filters b/vertical_tasks/vertical_tasks.vcxproj.filters index c60a1c5..7482b55 100644 --- a/vertical_tasks/vertical_tasks.vcxproj.filters +++ b/vertical_tasks/vertical_tasks.vcxproj.filters @@ -16,6 +16,7 @@ + From ccd17d2399079eb445d0f77def1ed4cc1642d53e Mon Sep 17 00:00:00 2001 From: antmor <43587397+antmor@users.noreply.github.com> Date: Wed, 21 Sep 2022 16:01:10 -0700 Subject: [PATCH 2/4] notifications fully work yay. and async icon calcs --- vertical_tasks/MainWindow.xaml.cpp | 123 +++++++++++++++++++++-------- vertical_tasks/MainWindow.xaml.h | 1 + 2 files changed, 90 insertions(+), 34 deletions(-) diff --git a/vertical_tasks/MainWindow.xaml.cpp b/vertical_tasks/MainWindow.xaml.cpp index 325ad5f..811f98c 100644 --- a/vertical_tasks/MainWindow.xaml.cpp +++ b/vertical_tasks/MainWindow.xaml.cpp @@ -37,18 +37,20 @@ namespace winrt::vertical_tasks::implementation { throw hresult_not_implemented(); } + struct WinData { - HWND hwnd; std::wstring title; - + wil::unique_hicon icon; + bool inView{ false }; }; - std::vector g_windows; + std::unordered_map g_windows; UINT g_shellHookMsgId{ UINT_MAX }; bool g_initialized{ false }; - BOOL CALLBACK WindowEnumerationCallBack(HWND hwnd, LPARAM /*lParam*/) + // returns true if window already existed + decltype(g_windows)::iterator AddOrUpdateWindow(HWND hwnd) { if (IsWindow(hwnd) && IsWindowVisible(hwnd) && (0 == GetWindow(hwnd, GW_OWNER))) { @@ -59,27 +61,57 @@ namespace winrt::vertical_tasks::implementation std::vector buffer(size + 1); GetWindowText(hwnd, buffer.data(), size + 1); - bool found{ false }; - for (auto&& winData : g_windows) + auto found{ g_windows.find(hwnd) }; + + if (found != g_windows.end()) { - if (winData.hwnd == hwnd) - { - auto oldTitle = std::move(winData.title); - winData.title = std::wstring(std::begin(buffer), std::end(buffer)); - found = true; - break; - } + auto&& [_, winData] = *found; + auto oldTitle = std::move(winData.title); + winData.title = std::wstring(std::begin(buffer), std::end(buffer)); + winData.inView = true; + return found; } - if (!found) + else { - g_windows.emplace_back(WinData{ hwnd, std::wstring(std::begin(buffer), std::end(buffer)) }); + return g_windows.emplace(hwnd, WinData{ std::wstring(std::begin(buffer), std::end(buffer)) }).first; } } } + return g_windows.end(); + } + + BOOL CALLBACK WindowEnumerationCallBack(HWND hwnd, LPARAM /*lParam*/) + { + AddOrUpdateWindow(hwnd); // keep on looping return true; } + winrt::fire_and_forget MainWindow::FetchIcon(HWND hwnd) + { + co_await winrt::resume_background(); + + HICON icon; + SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL2, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, + 500/*ms*/, reinterpret_cast(&icon)); + if (!icon) + { + SendMessageTimeout(hwnd, WM_GETICON, ICON_SMALL, 0, SMTO_BLOCK | SMTO_ABORTIFHUNG, + 500/*ms*/, reinterpret_cast(&icon)); + } + wil::unique_hicon iconCopy(CopyIcon(icon)); + + co_await wil::resume_foreground(DispatcherQueue()); + auto found{ g_windows.find(hwnd) }; + + if (found != g_windows.end()) + { + found->second.icon = std::move(iconCopy); + } + // TODO update icon in ViewModel + } + + //int i = 0; void MainWindow::myButton_Click(IInspectable const&, RoutedEventArgs const&) { if (!g_initialized) @@ -89,8 +121,8 @@ namespace winrt::vertical_tasks::implementation EnumWindows(&WindowEnumerationCallBack, 0); std::vector newTitles; newTitles.reserve(g_windows.size()); - - for (auto&& winData : g_windows) + size_t index = 0; + for (auto&& [hwnd, winData] : g_windows) { if (!winData.title.empty()) { @@ -108,18 +140,21 @@ namespace winrt::vertical_tasks::implementation }); g_initialized = true; } + else + { + /*g_windows[i].ShowWindow(); + i++;*/ + } } void MainWindow::SelectItem(HWND hwnd) { std::wstring_view titleToSelect; - for (auto&& winData : g_windows) + auto& found{ g_windows.find(hwnd) }; + + if (found != g_windows.end()) { - if (winData.hwnd == hwnd) - { - titleToSelect = winData.title; - break; - } + titleToSelect = found->second.title; } if (!titleToSelect.empty()) @@ -139,13 +174,12 @@ namespace winrt::vertical_tasks::implementation void MainWindow::DeleteItem(HWND hwnd) { std::wstring_view titleToDelete; - for (auto&& winData : g_windows) + + auto& found{ g_windows.find(hwnd) }; + + if (found != g_windows.end()) { - if (winData.hwnd == hwnd) - { - titleToDelete = winData.title; - break; - } + titleToDelete = found->second.title; } if (!titleToDelete.empty()) @@ -165,25 +199,46 @@ namespace winrt::vertical_tasks::implementation winrt::fire_and_forget MainWindow::OnShellMessage(WPARAM wParam, LPARAM lParam) { co_await wil::resume_foreground(DispatcherQueue()); - OutputDebugString(L"shell message"); std::wstringstream myString; - myString << std::hex << wParam << L", " << lParam << std::endl; - OutputDebugString(myString.str().c_str()); + myString << L"shell message: " << std::hex << wParam << L", " << lParam; switch (wParam) { case HSHELL_WINDOWACTIVATED: case HSHELL_RUDEAPPACTIVATED: + { SelectItem(reinterpret_cast(lParam)); + } break; case HSHELL_WINDOWCREATED: + { // add the window - WindowEnumerationCallBack(reinterpret_cast(lParam), 0); - SelectItem(reinterpret_cast(lParam)); + auto&& added = AddOrUpdateWindow(reinterpret_cast(lParam)); + if (added != g_windows.end()) + { + auto&& [_, data] = *added; + if (!data.inView) + { + m_windowTitles.Append(winrt::box_value(data.title)); + } + SelectItem(reinterpret_cast(lParam)); + + } + } break; case HSHELL_WINDOWDESTROYED: + { DeleteItem(reinterpret_cast(lParam)); + } break; + default: + { + myString << L" ! UNKNOWN"; } + break; + }noti + myString << std::endl; + OutputDebugString(myString.str().c_str()); + } Windows::Foundation::Collections::IObservableVector MainWindow::WindowTitles() diff --git a/vertical_tasks/MainWindow.xaml.h b/vertical_tasks/MainWindow.xaml.h index 93fd484..0c9a838 100644 --- a/vertical_tasks/MainWindow.xaml.h +++ b/vertical_tasks/MainWindow.xaml.h @@ -15,6 +15,7 @@ namespace winrt::vertical_tasks::implementation private: winrt::fire_and_forget OnShellMessage(WPARAM wParam, LPARAM lParam); + winrt::fire_and_forget FetchIcon(HWND hwnd); void SelectItem(HWND hwnd); void DeleteItem(HWND hwnd); Windows::Foundation::Collections::IObservableVector m_windowTitles{winrt::single_threaded_observable_vector()}; From 04fe8996f5ad617aae6af0f5059161d9c8397742 Mon Sep 17 00:00:00 2001 From: antmor <43587397+antmor@users.noreply.github.com> Date: Wed, 21 Sep 2022 22:22:57 -0700 Subject: [PATCH 3/4] TaskVM, array shenanigans, and selection/deletion. TODO: get onclick or onselection changed to work. and rename of a window. --- vertical_tasks/MainWindow.idl | 15 +- vertical_tasks/MainWindow.xaml | 40 ++++- vertical_tasks/MainWindow.xaml.cpp | 152 +++++++----------- vertical_tasks/MainWindow.xaml.h | 58 ++++++- vertical_tasks/TaskVM.cpp | 17 ++ vertical_tasks/TaskVM.h | 69 ++++++++ vertical_tasks/vertical_tasks.vcxproj | 2 + vertical_tasks/vertical_tasks.vcxproj.filters | 2 + 8 files changed, 248 insertions(+), 107 deletions(-) create mode 100644 vertical_tasks/TaskVM.cpp create mode 100644 vertical_tasks/TaskVM.h diff --git a/vertical_tasks/MainWindow.idl b/vertical_tasks/MainWindow.idl index 7ce84ab..d384da3 100644 --- a/vertical_tasks/MainWindow.idl +++ b/vertical_tasks/MainWindow.idl @@ -1,10 +1,21 @@ namespace vertical_tasks { + [default_interface] + runtimeclass TaskVM : Windows.UI.Xaml.Data.INotifyPropertyChanged + { + TaskVM(UInt64 hwnd); + String Title{ get; }; + Microsoft.UI.Xaml.Controls.IconSource IconSource{ get; }; + + void Select(); + void Close(); + + } + [default_interface] runtimeclass MainWindow : Microsoft.UI.Xaml.Window { MainWindow(); - Int32 MyProperty; - Windows.Foundation.Collections.IObservableVector WindowTitles{ get; }; + Windows.Foundation.Collections.IObservableVector Tasks{ get; }; } } diff --git a/vertical_tasks/MainWindow.xaml b/vertical_tasks/MainWindow.xaml index 56a13a6..0afd2e7 100644 --- a/vertical_tasks/MainWindow.xaml +++ b/vertical_tasks/MainWindow.xaml @@ -9,6 +9,44 @@ - + + + + + + + + + + + + + + + - + @@ -36,12 +39,25 @@ TextTrimming="CharacterEllipsis" FontSize="12" Margin="6,0,0,0"/> - diff --git a/vertical_tasks/MainWindow.xaml.cpp b/vertical_tasks/MainWindow.xaml.cpp index 9e576e7..022e996 100644 --- a/vertical_tasks/MainWindow.xaml.cpp +++ b/vertical_tasks/MainWindow.xaml.cpp @@ -123,17 +123,54 @@ namespace winrt::vertical_tasks::implementation } } - winrt::fire_and_forget MainWindow::OnItemClick(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& args) + winrt::fire_and_forget MainWindow::OnItemClick(Windows::Foundation::IInspectable const& /*sender*/, Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& /*args*/) { - args.ClickedItem().as(); - - args.OriginalSource(); co_return; } + winrt::fire_and_forget MainWindow::OnSelectionChanged(Windows::Foundation::IInspectable const& /*sender*/, Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& /*args*/) + { + if (selectionFromShell) + { + // seleciton change from shell + co_return; + } + + std::vector windowsToShow; + { + auto scope = selectionFromClick.onInScope(); + + auto selection = myList().SelectedItems(); + for (auto&& item : selection) + { + auto& taskVM = item.as(); + // select window + windowsToShow.emplace_back(taskVM->Hwnd()); + } + } + co_await winrt::resume_background(); + for (auto&& hwnd : windowsToShow) + { + SetForegroundWindow(hwnd); + + if (!ShowWindow(hwnd, SW_RESTORE)) + { + // ShowWindow doesn't work if the process is running elevated: fallback to SendMessage + SendMessage(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0); + } + } + } + + void MainWindow::SelectItem(HWND hwnd) { + if (selectionFromClick) + { + // we caused the selection, so ignore it. + return; + } + auto scope = selectionFromShell.onInScope(); auto found = m_tasks->find(hwnd); if (found != m_tasks->end()) diff --git a/vertical_tasks/MainWindow.xaml.h b/vertical_tasks/MainWindow.xaml.h index 2a620fe..005f092 100644 --- a/vertical_tasks/MainWindow.xaml.h +++ b/vertical_tasks/MainWindow.xaml.h @@ -52,6 +52,7 @@ namespace winrt::vertical_tasks::implementation void myButton_Click(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::RoutedEventArgs const& args); winrt::fire_and_forget OnItemClick(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& args); + winrt::fire_and_forget OnSelectionChanged(Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Controls::SelectionChangedEventArgs const& args); IObservableVector Tasks() { return m_tasks.as< winrt::Windows::Foundation::Collections::IObservableVector>(); }; @@ -64,6 +65,26 @@ namespace winrt::vertical_tasks::implementation winrt::fire_and_forget FetchIcon(HWND hwnd); winrt::com_ptr m_tasks{ winrt::make_self() }; + + struct scope_toggle + { + operator bool() + { + return toggle; + } + auto onInScope() + { + toggle = true; + return wil::scope_exit([this]() { + toggle = false; }); + } + private: + bool toggle{ false }; + + }; + + scope_toggle selectionFromShell; + scope_toggle selectionFromClick; }; }