diff --git a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp index 3f0a2c9f76..d5a89db5ba 100644 --- a/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/ui/EditKeyboardWindow.cpp @@ -2,6 +2,7 @@ #include "EditKeyboardWindow.h" #include "SingleKeyRemapControl.h" #include "KeyDropDownControl.h" +#include "XamlBridge.h" LRESULT CALLBACK EditKeyboardWindowProc(HWND, UINT, WPARAM, LPARAM); @@ -12,6 +13,8 @@ bool isEditKeyboardWindowRegistrationCompleted = false; // Holds the native window handle of EditKeyboard Window. HWND hwndEditKeyboardNativeWindow = nullptr; std::mutex editKeyboardWindowMutex; +// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure +static XamlBridge* xamlBridgePtr = nullptr; // Function to create the Edit Keyboard Window void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) @@ -60,18 +63,15 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan hwndEditKeyboardNativeWindow = _hWndEditKeyboardWindow; hwndLock.unlock(); - // This DesktopWindowXamlSource is the object that enables a non-UWP desktop application - // to host UWP controls in any UI element that is associated with a window handle (HWND). - DesktopWindowXamlSource desktopSource; - // Get handle to corewindow - auto interop = desktopSource.as(); - // Parent the DesktopWindowXamlSource object to current window - check_hresult(interop->AttachToWindow(_hWndEditKeyboardWindow)); + // Create the xaml bridge object + XamlBridge xamlBridge(_hWndEditKeyboardWindow); + // DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource; + // Create the desktop window xaml source object and set its content + hWndXamlIslandEditKeyboardWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource); - // Get the new child window's hwnd - interop->get_WindowHandle(&hWndXamlIslandEditKeyboardWindow); - // Update the xaml island window size becuase initially is 0,0 - SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, 0, 0, 400, 400, SWP_SHOWWINDOW); + // Set the pointer to the xaml bridge object + xamlBridgePtr = &xamlBridge; // Header for the window Windows::UI::Xaml::Controls::RelativePanel header; @@ -310,10 +310,9 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan xamlContainer.SetAlignRightWithPanel(scrollViewer, true); xamlContainer.Children().Append(header); xamlContainer.Children().Append(scrollViewer); - xamlContainer.UpdateLayout(); - desktopSource.Content(xamlContainer); + desktopSource.Content(xamlContainer); ////End XAML Island section if (_hWndEditKeyboardWindow) { @@ -322,17 +321,16 @@ void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMan } // Message loop: - MSG msg = {}; - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - desktopSource.Close(); + xamlBridge.MessageLoop(); + // Reset pointers to nullptr + xamlBridgePtr = nullptr; hWndXamlIslandEditKeyboardWindow = nullptr; hwndLock.lock(); hwndEditKeyboardNativeWindow = nullptr; + + // Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit + xamlBridge.ClearXamlIslands(); } LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam) @@ -346,10 +344,17 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar GetClientRect(hWnd, &rcClient); SetWindowPos(hWndXamlIslandEditKeyboardWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW); break; - case WM_DESTROY: - PostQuitMessage(0); - break; default: + // If the Xaml Bridge object exists, then use it's message handler to handle keyboard focus operations + if (xamlBridgePtr != nullptr) + { + return xamlBridgePtr->MessageHandler(messageCode, wParam, lParam); + } + else if (messageCode == WM_DESTROY) + { + PostQuitMessage(0); + break; + } return DefWindowProc(hWnd, messageCode, wParam, lParam); break; } @@ -357,6 +362,7 @@ LRESULT CALLBACK EditKeyboardWindowProc(HWND hWnd, UINT messageCode, WPARAM wPar return 0; } +// Function to check if there is already a window active if yes bring to foreground bool CheckEditKeyboardWindowActive() { bool result = false; diff --git a/src/modules/keyboardmanager/ui/EditKeyboardWindow.h b/src/modules/keyboardmanager/ui/EditKeyboardWindow.h index c516885196..090c6c49b8 100644 --- a/src/modules/keyboardmanager/ui/EditKeyboardWindow.h +++ b/src/modules/keyboardmanager/ui/EditKeyboardWindow.h @@ -4,5 +4,5 @@ // Function to create the Edit Keyboard Window void createEditKeyboardWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); -// Function to check if there is already a window active if yes bring to foreground. +// Function to check if there is already a window active if yes bring to foreground bool CheckEditKeyboardWindowActive(); \ No newline at end of file diff --git a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp index 69ba8fd636..bf91f6565b 100644 --- a/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/ui/EditShortcutsWindow.cpp @@ -2,6 +2,7 @@ #include "EditShortcutsWindow.h" #include "ShortcutControl.h" #include "KeyDropDownControl.h" +#include "XamlBridge.h" LRESULT CALLBACK EditShortcutsWindowProc(HWND, UINT, WPARAM, LPARAM); @@ -12,6 +13,8 @@ bool isEditShortcutsWindowRegistrationCompleted = false; // Holds the native window handle of EditShortcuts Window. HWND hwndEditShortcutsNativeWindow = nullptr; std::mutex editShortcutsWindowMutex; +// Stores a pointer to the Xaml Bridge object so that it can be accessed from the window procedure +static XamlBridge* xamlBridgePtr = nullptr; // Function to create the Edit Shortcuts Window void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState) @@ -61,18 +64,15 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa hwndEditShortcutsNativeWindow = _hWndEditShortcutsWindow; hwndLock.unlock(); - // This DesktopWindowXamlSource is the object that enables a non-UWP desktop application - // to host UWP controls in any UI element that is associated with a window handle (HWND). - DesktopWindowXamlSource desktopSource; - // Get handle to corewindow - auto interop = desktopSource.as(); - // Parent the DesktopWindowXamlSource object to current window - check_hresult(interop->AttachToWindow(_hWndEditShortcutsWindow)); + // Create the xaml bridge object + XamlBridge xamlBridge(_hWndEditShortcutsWindow); + // DesktopSource needs to be declared before the RelativePanel xamlContainer object to avoid errors + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource; + // Create the desktop window xaml source object and set its content + hWndXamlIslandEditShortcutsWindow = xamlBridge.InitDesktopWindowsXamlSource(desktopSource); - // Get the new child window's hwnd - interop->get_WindowHandle(&hWndXamlIslandEditShortcutsWindow); - // Update the xaml island window size becuase initially is 0,0 - SetWindowPos(hWndXamlIslandEditShortcutsWindow, 0, 0, 0, 400, 400, SWP_SHOWWINDOW); + // Set the pointer to the xaml bridge object + xamlBridgePtr = &xamlBridge; // Header for the window Windows::UI::Xaml::Controls::RelativePanel header; @@ -233,8 +233,8 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa xamlContainer.SetAlignRightWithPanel(scrollViewer, true); xamlContainer.Children().Append(header); xamlContainer.Children().Append(scrollViewer); - xamlContainer.UpdateLayout(); + desktopSource.Content(xamlContainer); ////End XAML Island section @@ -245,17 +245,16 @@ void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardMa } // Message loop: - MSG msg = {}; - while (GetMessage(&msg, NULL, 0, 0)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - desktopSource.Close(); + xamlBridge.MessageLoop(); + // Reset pointers to nullptr + xamlBridgePtr = nullptr; hWndXamlIslandEditShortcutsWindow = nullptr; hwndLock.lock(); hwndEditShortcutsNativeWindow = nullptr; + + // Cannot be done in WM_DESTROY because that causes crashes due to fatal app exit + xamlBridge.ClearXamlIslands(); } LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam) @@ -269,10 +268,17 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa GetClientRect(hWnd, &rcClient); SetWindowPos(hWndXamlIslandEditShortcutsWindow, 0, rcClient.left, rcClient.top, rcClient.right, rcClient.bottom, SWP_SHOWWINDOW); break; - case WM_DESTROY: - PostQuitMessage(0); - break; default: + // If the Xaml Bridge object exists, then use it's message handler to handle keyboard focus operations + if (xamlBridgePtr != nullptr) + { + return xamlBridgePtr->MessageHandler(messageCode, wParam, lParam); + } + else if (messageCode == WM_DESTROY) + { + PostQuitMessage(0); + break; + } return DefWindowProc(hWnd, messageCode, wParam, lParam); break; } @@ -280,6 +286,7 @@ LRESULT CALLBACK EditShortcutsWindowProc(HWND hWnd, UINT messageCode, WPARAM wPa return 0; } +// Function to check if there is already a window active if yes bring to foreground bool CheckEditShortcutsWindowActive() { bool result = false; diff --git a/src/modules/keyboardmanager/ui/EditShortcutsWindow.h b/src/modules/keyboardmanager/ui/EditShortcutsWindow.h index db6168383a..3520b3fc1f 100644 --- a/src/modules/keyboardmanager/ui/EditShortcutsWindow.h +++ b/src/modules/keyboardmanager/ui/EditShortcutsWindow.h @@ -6,5 +6,5 @@ // Function to create the Edit Shortcuts Window void createEditShortcutsWindow(HINSTANCE hInst, KeyboardManagerState& keyboardManagerState); -// Function to check if there is already a window active if yes bring to foreground. +// Function to check if there is already a window active if yes bring to foreground bool CheckEditShortcutsWindowActive(); \ No newline at end of file diff --git a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj index 06814c6c82..91debc97e8 100644 --- a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj +++ b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj @@ -107,6 +107,7 @@ + @@ -115,6 +116,7 @@ + diff --git a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters index 9ca0c99698..b42dad8681 100644 --- a/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters +++ b/src/modules/keyboardmanager/ui/KeyboardManagerUI.vcxproj.filters @@ -7,6 +7,7 @@ + @@ -15,6 +16,7 @@ + diff --git a/src/modules/keyboardmanager/ui/XamlBridge.cpp b/src/modules/keyboardmanager/ui/XamlBridge.cpp new file mode 100644 index 0000000000..9350e2cc6a --- /dev/null +++ b/src/modules/keyboardmanager/ui/XamlBridge.cpp @@ -0,0 +1,297 @@ +#include "pch.h" + +#include "XamlBridge.h" + +bool XamlBridge::FilterMessage(const MSG* msg) +{ + // When multiple child windows are present it is needed to pre dispatch messages to all + // DesktopWindowXamlSource instances so keyboard accelerators and + // keyboard focus work correctly. + BOOL xamlSourceProcessedMessage = FALSE; + { + for (auto xamlSource : m_xamlSources) + { + auto xamlSourceNative2 = xamlSource.as(); + const auto hr = xamlSourceNative2->PreTranslateMessage(msg, &xamlSourceProcessedMessage); + winrt::check_hresult(hr); + if (xamlSourceProcessedMessage) + { + break; + } + } + } + + return !!xamlSourceProcessedMessage; +} + +const auto static invalidReason = static_cast(-1); + +winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason GetReasonFromKey(WPARAM key) +{ + auto reason = invalidReason; + if (key == VK_TAB) + { + byte keyboardState[256] = {}; + WINRT_VERIFY(::GetKeyboardState(keyboardState)); + reason = (keyboardState[VK_SHIFT] & 0x80) ? + winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Last : + winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First; + } + else if (key == VK_LEFT) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Left; + } + else if (key == VK_RIGHT) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right; + } + else if (key == VK_UP) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Up; + } + else if (key == VK_DOWN) + { + reason = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down; + } + return reason; +} + +// Function to return the next xaml island in focus +winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource XamlBridge::GetNextFocusedIsland(MSG* msg) +{ + if (msg->message == WM_KEYDOWN) + { + const auto key = msg->wParam; + auto reason = GetReasonFromKey(key); + if (reason != invalidReason) + { + const BOOL previous = + (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right) ? + false : + true; + + const auto currentFocusedWindow = ::GetFocus(); + const auto nextElement = ::GetNextDlgTabItem(parentWindow, currentFocusedWindow, previous); + for (auto xamlSource : m_xamlSources) + { + const auto nativeIsland = xamlSource.as(); + HWND islandWnd = nullptr; + winrt::check_hresult(nativeIsland->get_WindowHandle(&islandWnd)); + if (nextElement == islandWnd) + { + return xamlSource; + } + } + } + } + + return nullptr; +} + +// Function to return the xaml island currently in focus +winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource XamlBridge::GetFocusedIsland() +{ + for (auto xamlSource : m_xamlSources) + { + if (xamlSource.HasFocus()) + { + return xamlSource; + } + } + return nullptr; +} + +// Function to handle focus navigation +bool XamlBridge::NavigateFocus(MSG* msg) +{ + if (const auto nextFocusedIsland = GetNextFocusedIsland(msg)) + { + const auto previousFocusedWindow = ::GetFocus(); + RECT rect = {}; + WINRT_VERIFY(::GetWindowRect(previousFocusedWindow, &rect)); + const auto nativeIsland = nextFocusedIsland.as(); + HWND islandWnd = nullptr; + winrt::check_hresult(nativeIsland->get_WindowHandle(&islandWnd)); + POINT pt = { rect.left, rect.top }; + SIZE size = { rect.right - rect.left, rect.bottom - rect.top }; + ::ScreenToClient(islandWnd, &pt); + const auto hintRect = winrt::Windows::Foundation::Rect({ static_cast(pt.x), static_cast(pt.y), static_cast(size.cx), static_cast(size.cy) }); + const auto reason = GetReasonFromKey(msg->wParam); + const auto request = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationRequest(reason, hintRect); + lastFocusRequestId = request.CorrelationId(); + const auto result = nextFocusedIsland.NavigateFocus(request); + return result.WasFocusMoved(); + } + else + { + const bool islandIsFocused = GetFocusedIsland() != nullptr; + byte keyboardState[256] = {}; + WINRT_VERIFY(::GetKeyboardState(keyboardState)); + const bool isMenuModifier = keyboardState[VK_MENU] & 0x80; + if (islandIsFocused && !isMenuModifier) + { + return false; + } + const bool isDialogMessage = !!IsDialogMessage(parentWindow, msg); + return isDialogMessage; + } +} + +// Function to run the message loop for the xaml island window +int XamlBridge::MessageLoop() +{ + MSG msg = {}; + HRESULT hr = S_OK; + while (GetMessage(&msg, nullptr, 0, 0)) + { + const bool xamlSourceProcessedMessage = FilterMessage(&msg); + if (!xamlSourceProcessedMessage) + { + if (!NavigateFocus(&msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + } + + return (int)msg.wParam; +} + +static const WPARAM invalidKey = (WPARAM)-1; + +WPARAM GetKeyFromReason(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason reason) +{ + auto key = invalidKey; + if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Last || reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First) + { + key = VK_TAB; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Left) + { + key = VK_LEFT; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right) + { + key = VK_RIGHT; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Up) + { + key = VK_UP; + } + else if (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down) + { + key = VK_DOWN; + } + return key; +} + +// Event triggered when focus is requested +void XamlBridge::OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource const& sender, winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSourceTakeFocusRequestedEventArgs const& args) +{ + if (args.Request().CorrelationId() != lastFocusRequestId) + { + const auto reason = args.Request().Reason(); + const BOOL previous = + (reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::First || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Down || + reason == winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Right) ? + false : + true; + + const auto nativeXamlSource = sender.as(); + HWND senderHwnd = nullptr; + winrt::check_hresult(nativeXamlSource->get_WindowHandle(&senderHwnd)); + + MSG msg = {}; + msg.hwnd = senderHwnd; + msg.message = WM_KEYDOWN; + msg.wParam = GetKeyFromReason(reason); + if (!NavigateFocus(&msg)) + { + const auto nextElement = ::GetNextDlgTabItem(parentWindow, senderHwnd, previous); + ::SetFocus(nextElement); + } + } + else + { + const auto request = winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationRequest(winrt::Windows::UI::Xaml::Hosting::XamlSourceFocusNavigationReason::Restore); + lastFocusRequestId = request.CorrelationId(); + sender.NavigateFocus(request); + } +} + +// Function to initialise the xaml source object +HWND XamlBridge::InitDesktopWindowsXamlSource(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource desktopSource) +{ + HRESULT hr = S_OK; + + auto interop = desktopSource.as(); + // Parent the DesktopWindowXamlSource object to current window + hr = interop->AttachToWindow(parentWindow); + winrt::check_hresult(hr); + + // Get the new child window's hwnd + HWND hWndXamlIsland = nullptr; + hr = interop->get_WindowHandle(&hWndXamlIsland); + winrt::check_hresult(hr); + + m_takeFocusEventRevokers.push_back(desktopSource.TakeFocusRequested(winrt::auto_revoke, { this, &XamlBridge::OnTakeFocusRequested })); + m_xamlSources.push_back(desktopSource); + + return hWndXamlIsland; +} + +// Function to close and delete all the xaml source objects +void XamlBridge::ClearXamlIslands() +{ + for (auto& takeFocusRevoker : m_takeFocusEventRevokers) + { + takeFocusRevoker.revoke(); + } + m_takeFocusEventRevokers.clear(); + + for (auto xamlSource : m_xamlSources) + { + xamlSource.Close(); + } + m_xamlSources.clear(); +} + +// Function invoked when the window is destroyed +void XamlBridge::OnDestroy(HWND) +{ + PostQuitMessage(0); +} + +// Function invoked when the window is activated +void XamlBridge::OnActivate(HWND, UINT state, HWND hwndActDeact, BOOL fMinimized) +{ + if (state == WA_INACTIVE) + { + m_hwndLastFocus = GetFocus(); + } +} + +// Function invoked when the window is set to focus +void XamlBridge::OnSetFocus(HWND, HWND hwndOldFocus) +{ + if (m_hwndLastFocus) + { + SetFocus(m_hwndLastFocus); + } +} + +// Message Handler function for Xaml Island windows +LRESULT XamlBridge::MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept +{ + switch (message) + { + HANDLE_MSG(parentWindow, WM_DESTROY, OnDestroy); + HANDLE_MSG(parentWindow, WM_ACTIVATE, OnActivate); + HANDLE_MSG(parentWindow, WM_SETFOCUS, OnSetFocus); + } + return DefWindowProc(parentWindow, message, wParam, lParam); +} diff --git a/src/modules/keyboardmanager/ui/XamlBridge.h b/src/modules/keyboardmanager/ui/XamlBridge.h new file mode 100644 index 0000000000..1c13f19cb1 --- /dev/null +++ b/src/modules/keyboardmanager/ui/XamlBridge.h @@ -0,0 +1,72 @@ +#pragma once +#include // To enable support for non-WinRT interfaces, unknwn.h must be included before any C++/WinRT headers. +#include +#include +#include +#include +#include +#include +#include + +// This class is used for handling XAML Island operations +class XamlBridge +{ +public: + // Function to run the message loop for the xaml island window + int MessageLoop(); + + // Constructor + XamlBridge(HWND parent) : + parentWindow(parent), lastFocusRequestId(winrt::guid()) + { + } + + // Function to initialise the xaml source object + HWND InitDesktopWindowsXamlSource(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource); + + // Function to close and delete all the xaml source objects + void ClearXamlIslands(); + + // Message Handler function for Xaml Island windows + LRESULT MessageHandler(UINT const message, WPARAM const wParam, LPARAM const lParam) noexcept; + +private: + // Stores the last window handle in focus + HWND m_hwndLastFocus = nullptr; + + // Stores the handle of the parent native window + HWND parentWindow = nullptr; + + // Stores the GUID of the last focus request + winrt::guid lastFocusRequestId; + + // Function to return the xaml island currently in focus + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource GetFocusedIsland(); + + // Function to pre process the message on the xaml source object + bool FilterMessage(const MSG* msg); + + // Event triggered when focus is requested + void OnTakeFocusRequested(winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource const& sender, winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSourceTakeFocusRequestedEventArgs const& args); + + // Function to return the next xaml island in focus + winrt::Windows::UI::Xaml::Hosting::DesktopWindowXamlSource GetNextFocusedIsland(MSG* msg); + + // Function to handle focus navigation + bool NavigateFocus(MSG* msg); + + // Stores the focus event objects + std::vector m_takeFocusEventRevokers; + + // Stores the xaml source objects + std::vector m_xamlSources; + + // Function invoked when the window is destroyed + void OnDestroy(HWND); + + // Function invoked when the window is activated + void OnActivate(HWND, UINT state, HWND hwndActDeact, BOOL fMinimized); + + // Function invoked when the window is set to focus + void OnSetFocus(HWND, HWND hwndOldFocus); +};