diff --git a/change/react-native-windows-085557aa-eea4-4220-9665-4c1cb7386536.json b/change/react-native-windows-085557aa-eea4-4220-9665-4c1cb7386536.json new file mode 100644 index 00000000000..8259cc6ef9a --- /dev/null +++ b/change/react-native-windows-085557aa-eea4-4220-9665-4c1cb7386536.json @@ -0,0 +1,7 @@ +{ + "type": "prerelease", + "comment": "[Fabric] Support hosting in ContentIsland", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/playground/windows/playground-composition/CustomComponent.cpp b/packages/playground/windows/playground-composition/CustomComponent.cpp new file mode 100644 index 00000000000..94265a0fb1a --- /dev/null +++ b/packages/playground/windows/playground-composition/CustomComponent.cpp @@ -0,0 +1,123 @@ +#include "pch.h" + +#include +#include +#include +#include + +/* + * Custom Properties can be passed from JS to this native component + * This struct will eventually be codegen'd from the JS spec file + */ +struct CustomProps : winrt::implements { + CustomProps(winrt::Microsoft::ReactNative::ViewProps props) : m_props(props) {} + + void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept { + if (propName == L"label") { + if (!value) { + label.clear(); + } else { + label = winrt::to_string(value.GetString()); + } + } + } + + std::string label; + winrt::Microsoft::ReactNative::ViewProps m_props; +}; + +struct CustomComponent : winrt::implements { + CustomComponent( + winrt::Microsoft::ReactNative::IReactContext reactContext, + winrt::Microsoft::ReactNative::Composition::ICompositionContext compContext) + : m_compContext(compContext) {} + + void UpdateProps(winrt::Microsoft::ReactNative::IComponentProps props) noexcept { + auto customProps = props.as(); + } + + void UpdateLayoutMetrics(winrt::Microsoft::ReactNative::Composition::LayoutMetrics metrics) noexcept { + m_visual.Size({metrics.Frame.Width, metrics.Frame.Height}); + } + + winrt::Microsoft::ReactNative::Composition::IVisual CreateVisual() noexcept { + m_visual = m_compContext.CreateSpriteVisual(); + m_visual.Brush(m_compContext.CreateColorBrush(winrt::Windows::UI::Colors::White())); + + auto compositor = + winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::InnerCompositor(m_compContext); + + m_spotlight = compositor.CreateSpotLight(); + m_spotlight.InnerConeAngleInDegrees(50.0f); + m_spotlight.InnerConeColor(winrt::Windows::UI::Colors::FloralWhite()); + m_spotlight.InnerConeIntensity(5.0f); + m_spotlight.OuterConeAngleInDegrees(0.0f); + m_spotlight.ConstantAttenuation(1.0f); + m_spotlight.LinearAttenuation(0.253f); + m_spotlight.QuadraticAttenuation(0.58f); + m_spotlight.CoordinateSpace( + winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::InnerVisual(m_visual)); + m_spotlight.Targets().Add( + winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::InnerVisual(m_visual)); + + auto animation = compositor.CreateVector3KeyFrameAnimation(); + auto easeIn = compositor.CreateCubicBezierEasingFunction({0.5f, 0.0f}, {1.0f, 1.0f}); + animation.InsertKeyFrame(0.00f, {100.0f, 100.0f, 35.0f}); + animation.InsertKeyFrame(0.25f, {300.0f, 200.0f, 75.0f}, easeIn); + animation.InsertKeyFrame(0.50f, {050.0f, 300.0f, 15.0f}, easeIn); + animation.InsertKeyFrame(0.75f, {300.0f, 050.0f, 75.0f}, easeIn); + animation.InsertKeyFrame(1.00f, {100.0f, 100.0f, 35.0f}, easeIn); + animation.Duration(std::chrono::milliseconds(4000)); + animation.IterationBehavior(winrt::Windows::UI::Composition::AnimationIterationBehavior::Forever); + + m_spotlight.StartAnimation(L"Offset", animation); + + return m_visual; + } + + // TODO - Once we get more complete native eventing we can move spotlight based on pointer position + void OnPointerMove() noexcept { + // m_spotlight.Offset({(float)x, (float)y, 15.0f}); + + // m_propSet.InsertVector2(L"Position", {x, y}); + // TODO expose coordinate translation methods + // TODO convert x/y into local coordinates + } + + static void RegisterViewComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) { + packageBuilder.as().AddViewComponent( + L"MyCustomComponent", [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { + builder.SetCreateProps( + [](winrt::Microsoft::ReactNative::ViewProps props) noexcept { return winrt::make(props); }); + auto compBuilder = + builder.as(); + compBuilder.SetCreateView( + [](winrt::Microsoft::ReactNative::IReactContext reactContext, + winrt::Microsoft::ReactNative::Composition::ICompositionContext context) noexcept { + return winrt::make(reactContext, context); + }); + compBuilder.SetPropsUpdater([](winrt::Windows::Foundation::IInspectable handle, + winrt::Microsoft::ReactNative::IComponentProps props) noexcept { + handle.as()->UpdateProps(props); + }); + compBuilder.SetLayoutMetricsUpdater( + [](winrt::Windows::Foundation::IInspectable handle, + winrt::Microsoft::ReactNative::Composition::LayoutMetrics metrics) noexcept { + handle.as()->UpdateLayoutMetrics(metrics); + }); + compBuilder.SetVisualCreator([](winrt::Windows::Foundation::IInspectable handle) noexcept { + return handle.as()->CreateVisual(); + }); + }); + } + + private: + winrt::Windows::UI::Composition::SpotLight m_spotlight{nullptr}; + + winrt::Microsoft::ReactNative::Composition::ISpriteVisual m_visual{nullptr}; + winrt::Microsoft::ReactNative::Composition::ICompositionContext m_compContext; +}; + +void RegisterCustomComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept { + CustomComponent::RegisterViewComponent(packageBuilder); +} diff --git a/packages/playground/windows/playground-composition/DeviceInfoModule.cpp b/packages/playground/windows/playground-composition/DeviceInfoModule.cpp new file mode 100644 index 00000000000..7956f497303 --- /dev/null +++ b/packages/playground/windows/playground-composition/DeviceInfoModule.cpp @@ -0,0 +1,46 @@ +#include "pch.h" + +#include "../../../../vnext/codegen/NativeDeviceInfoSpec.g.h" + +#include "NativeModules.h" + +// Temporary Work around crash in DeviceInfo when running outside of XAML environment +// TODO rework built-in DeviceInfo to allow it to be driven without use of HWNDs or XamlApps +REACT_MODULE(DeviceInfo) +struct DeviceInfo { + using ModuleSpec = Microsoft::ReactNativeSpecs::DeviceInfoSpec; + + REACT_INIT(Initialize) + void Initialize(React::ReactContext const &reactContext) noexcept { + m_context = reactContext; + } + + REACT_GET_CONSTANTS(GetConstants) + Microsoft::ReactNativeSpecs::DeviceInfoSpec_DeviceInfoConstants GetConstants() noexcept { + Microsoft::ReactNativeSpecs::DeviceInfoSpec_DeviceInfoConstants constants; + Microsoft::ReactNativeSpecs::DeviceInfoSpec_DisplayMetrics screenDisplayMetrics; + screenDisplayMetrics.fontScale = 1; + screenDisplayMetrics.height = 1024; + screenDisplayMetrics.width = 1024; + screenDisplayMetrics.scale = 1; + constants.Dimensions.screen = screenDisplayMetrics; + constants.Dimensions.window = screenDisplayMetrics; + return constants; + } + + private: + winrt::Microsoft::ReactNative::ReactContext m_context; +}; + +// Have to use TurboModules to override built in modules.. so the standard attributed package provider doesn't work. +struct StubDeviceInfoReactPackageProvider + : winrt::implements { + public: // IReactPackageProvider + void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept { + AddAttributedModules(packageBuilder, true); + } +}; + +winrt::Microsoft::ReactNative::IReactPackageProvider CreateStubDeviceInfoPackageProvider() noexcept { + return winrt::make(); +} diff --git a/packages/playground/windows/playground-composition/Playground-Composition.cpp b/packages/playground/windows/playground-composition/Playground-Composition.cpp index 30b3f214407..b7f19f28ebb 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.cpp +++ b/packages/playground/windows/playground-composition/Playground-Composition.cpp @@ -5,6 +5,7 @@ #include #include +#include // Disabled until we have a 3rd party story for custom components // #include "AutolinkedNativeModules.g.h" @@ -24,198 +25,55 @@ #include "NativeModules.h" #include "ReactPropertyBag.h" -struct CustomProps : winrt::implements { - CustomProps(winrt::Microsoft::ReactNative::ViewProps props) : m_props(props) {} - - void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept { - if (propName == L"label") { - if (!value) { - label.clear(); - } else { - label = winrt::to_string(value.GetString()); - } - } - } - - std::string label; - winrt::Microsoft::ReactNative::ViewProps m_props; -}; - -struct CustomComponent : winrt::implements { - CustomComponent( - winrt::Microsoft::ReactNative::IReactContext reactContext, - winrt::Microsoft::ReactNative::Composition::ICompositionContext compContext) - : m_compContext(compContext) {} - - void UpdateProps(winrt::Microsoft::ReactNative::IComponentProps props) noexcept { - auto customProps = props.as(); - } - - void UpdateLayoutMetrics(winrt::Microsoft::ReactNative::Composition::LayoutMetrics metrics) noexcept { - m_visual.Size({metrics.Frame.Width, metrics.Frame.Height}); - } - - winrt::Microsoft::ReactNative::Composition::IVisual CreateVisual() noexcept { - m_visual = m_compContext.CreateSpriteVisual(); - m_visual.Brush(m_compContext.CreateColorBrush(winrt::Windows::UI::Colors::White())); - - auto compositor = - winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::InnerCompositor(m_compContext); - - m_spotlight = compositor.CreateSpotLight(); - m_spotlight.InnerConeAngleInDegrees(50.0f); - m_spotlight.InnerConeColor(winrt::Windows::UI::Colors::FloralWhite()); - m_spotlight.InnerConeIntensity(5.0f); - m_spotlight.OuterConeAngleInDegrees(0.0f); - m_spotlight.ConstantAttenuation(1.0f); - m_spotlight.LinearAttenuation(0.253f); - m_spotlight.QuadraticAttenuation(0.58f); - m_spotlight.CoordinateSpace( - winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::InnerVisual(m_visual)); - m_spotlight.Targets().Add( - winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::InnerVisual(m_visual)); - - auto animation = compositor.CreateVector3KeyFrameAnimation(); - auto easeIn = compositor.CreateCubicBezierEasingFunction({0.5f, 0.0f}, {1.0f, 1.0f}); - animation.InsertKeyFrame(0.00f, {100.0f, 100.0f, 35.0f}); - animation.InsertKeyFrame(0.25f, {300.0f, 200.0f, 75.0f}, easeIn); - animation.InsertKeyFrame(0.50f, {050.0f, 300.0f, 15.0f}, easeIn); - animation.InsertKeyFrame(0.75f, {300.0f, 050.0f, 75.0f}, easeIn); - animation.InsertKeyFrame(1.00f, {100.0f, 100.0f, 35.0f}, easeIn); - animation.Duration(std::chrono::milliseconds(4000)); - animation.IterationBehavior(winrt::Windows::UI::Composition::AnimationIterationBehavior::Forever); - - m_spotlight.StartAnimation(L"Offset", animation); - - return m_visual; - } - - // TODO - Once we get more complete native eventing we can move spotlight based on pointer position - int64_t SendMessage(uint32_t msg, uint64_t wParam, int64_t lParam) noexcept { - if (msg == WM_MOUSEMOVE) { - auto x = GET_X_LPARAM(lParam); - auto y = GET_Y_LPARAM(lParam); - - m_spotlight.Offset({(float)x, (float)y, 15.0f}); - - // m_propSet.InsertVector2(L"Position", {x, y}); - // TODO expose coordinate translation methods - // TODO convert x/y into local coordinates - } - - return 0; - } - - static void RegisterViewComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) { - packageBuilder.as().AddViewComponent( - L"MyCustomComponent", [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { - builder.SetCreateProps( - [](winrt::Microsoft::ReactNative::ViewProps props) noexcept { return winrt::make(props); }); - auto compBuilder = - builder.as(); - compBuilder.SetCreateView( - [](winrt::Microsoft::ReactNative::IReactContext reactContext, - winrt::Microsoft::ReactNative::Composition::ICompositionContext context) noexcept { - return winrt::make(reactContext, context); - }); - compBuilder.SetPropsUpdater([](winrt::Windows::Foundation::IInspectable handle, - winrt::Microsoft::ReactNative::IComponentProps props) noexcept { - handle.as()->UpdateProps(props); - }); - compBuilder.SetLayoutMetricsUpdater( - [](winrt::Windows::Foundation::IInspectable handle, - winrt::Microsoft::ReactNative::Composition::LayoutMetrics metrics) noexcept { - handle.as()->UpdateLayoutMetrics(metrics); - }); - compBuilder.SetVisualCreator([](winrt::Windows::Foundation::IInspectable handle) noexcept { - return handle.as()->CreateVisual(); - }); - compBuilder.SetMessageHandler( - [](winrt::Windows::Foundation::IInspectable handle, - uint32_t Msg, - uint64_t WParam, - int64_t LParam) noexcept { return handle.as()->SendMessage(Msg, WParam, LParam); }); - }); - } - - private: - winrt::Windows::UI::Composition::SpotLight m_spotlight{nullptr}; - - winrt::Microsoft::ReactNative::Composition::ISpriteVisual m_visual{nullptr}; - winrt::Microsoft::ReactNative::Composition::ICompositionContext m_compContext; -}; +#if USE_WINUI3 +#include +#include +#include +#include +#include +#endif -// Work around crash in DeviceInfo when running outside of XAML environment -// TODO rework built-in DeviceInfo to allow it to be driven without use of HWNDs or XamlApps -REACT_MODULE(DeviceInfo) -struct DeviceInfo { - using ModuleSpec = Microsoft::ReactNativeSpecs::DeviceInfoSpec; +#if USE_WINUI3 +winrt::Microsoft::UI::Dispatching::DispatcherQueueController g_liftedDispatcherQueueController{nullptr}; +winrt::Microsoft::UI::Composition::Compositor g_liftedCompositor{nullptr}; - REACT_INIT(Initialize) - void Initialize(React::ReactContext const &reactContext) noexcept { - m_context = reactContext; - } +#endif - REACT_GET_CONSTANTS(GetConstants) - Microsoft::ReactNativeSpecs::DeviceInfoSpec_DeviceInfoConstants GetConstants() noexcept { - Microsoft::ReactNativeSpecs::DeviceInfoSpec_DeviceInfoConstants constants; - Microsoft::ReactNativeSpecs::DeviceInfoSpec_DisplayMetrics screenDisplayMetrics; - screenDisplayMetrics.fontScale = 1; - screenDisplayMetrics.height = 1024; - screenDisplayMetrics.width = 1024; - screenDisplayMetrics.scale = 1; - constants.Dimensions.screen = screenDisplayMetrics; - constants.Dimensions.window = screenDisplayMetrics; - return constants; - } +HWND g_hwndTopLevel; - private: - winrt::Microsoft::ReactNative::ReactContext m_context; -}; +void RegisterCustomComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept; // Have to use TurboModules to override built in modules.. so the standard attributed package provider doesn't work. struct CompReactPackageProvider : winrt::implements { public: // IReactPackageProvider void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept { - AddAttributedModules(packageBuilder, true); - - CustomComponent::RegisterViewComponent(packageBuilder); + RegisterCustomComponent(packageBuilder); } }; -#ifdef USE_WINUI3 -winrt::Microsoft::UI::Dispatching::DispatcherQueueController g_liftedDispatcherQueueController{nullptr}; -#endif winrt::Windows::System::DispatcherQueueController g_dispatcherQueueController{nullptr}; winrt::Windows::UI::Composition::Compositor g_compositor{nullptr}; constexpr auto WindowDataProperty = L"WindowData"; int RunPlayground(int showCmd, bool useWebDebugger); +winrt::Microsoft::ReactNative::IReactPackageProvider CreateStubDeviceInfoPackageProvider() noexcept; struct WindowData { static HINSTANCE s_instance; static constexpr uint16_t defaultDebuggerPort = 9229; std::wstring m_bundleFile; - bool m_windowInited{false}; - winrt::Microsoft::ReactNative::CompositionHwndHost m_CompositionHwndHost{nullptr}; + winrt::Microsoft::ReactNative::CompositionRootView m_compRootView{nullptr}; winrt::Microsoft::ReactNative::ReactNativeHost m_host{nullptr}; winrt::Microsoft::ReactNative::ReactInstanceSettings m_instanceSettings{nullptr}; + bool m_useLiftedComposition{true}; + winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget m_target{nullptr}; + LONG m_height{0}; + LONG m_width{0}; - bool m_useWebDebugger{false}; - bool m_fastRefreshEnabled{true}; - bool m_useDirectDebugger{false}; - bool m_breakOnNextLine{false}; - uint16_t m_debuggerPort{defaultDebuggerPort}; - xaml::ElementTheme m_theme{xaml::ElementTheme::Default}; - - WindowData(const winrt::Microsoft::ReactNative::CompositionHwndHost &compHost) : m_CompositionHwndHost(compHost) { - winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositionContext( - InstanceSettings().Properties(), - winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::CreateContext(g_compositor)); - } + WindowData() {} static WindowData *GetFromWindow(HWND hwnd) { auto data = reinterpret_cast(GetProp(hwnd, WindowDataProperty)); @@ -233,11 +91,50 @@ struct WindowData { winrt::Microsoft::ReactNative::ReactInstanceSettings InstanceSettings() noexcept { if (!m_instanceSettings) { m_instanceSettings = winrt::Microsoft::ReactNative::ReactInstanceSettings(); + m_instanceSettings.UseFastRefresh(true); } return m_instanceSettings; } + winrt::Microsoft::UI::WindowId + CreateChildWindow(winrt::Microsoft::UI::WindowId parentWindowId, LPCWSTR title, int x, int y, int w, int h) { + LPCWSTR childWindowClassName = L"TestChildWindowClass"; + + // Register child window class + static std::once_flag onceFlag; + std::call_once(onceFlag, [&childWindowClassName]() { + WNDCLASSEX childWindowClass = {}; + + childWindowClass.cbSize = sizeof(WNDCLASSEX); + childWindowClass.style = CS_HREDRAW | CS_VREDRAW; + childWindowClass.lpfnWndProc = ::DefWindowProc; + childWindowClass.hInstance = GetModuleHandle(nullptr); + childWindowClass.lpszClassName = childWindowClassName; + + RegisterClassEx(&childWindowClass); + }); + + HWND parentHwnd; + parentHwnd = winrt::Microsoft::UI::GetWindowFromWindowId(parentWindowId); + + HWND childHwnd = ::CreateWindowEx( + 0 /* dwExStyle */, + childWindowClassName, + title, + WS_CHILD | WS_CLIPSIBLINGS | WS_VISIBLE, + x, + y, + w, + h, + parentHwnd /* hWndParent */, + nullptr /* hMenu */, + GetModuleHandle(nullptr), + nullptr /* lpParam */); + + return winrt::Microsoft::UI::GetWindowIdFromWindow(childHwnd); + } + LRESULT OnCommand(HWND hwnd, int id, HWND /* hwndCtl*/, UINT) { switch (id) { case IDM_OPENJSFILE: { @@ -255,15 +152,11 @@ struct WindowData { host.InstanceSettings().JavaScriptBundleFile(m_bundleFile); - host.InstanceSettings().UseWebDebugger(m_useWebDebugger); - host.InstanceSettings().UseDirectDebugger(m_useDirectDebugger); host.InstanceSettings().BundleRootPath( std::wstring(L"file:").append(workingDir).append(L"\\Bundle\\").c_str()); - host.InstanceSettings().DebuggerBreakOnNextLine(m_breakOnNextLine); - host.InstanceSettings().UseFastRefresh(m_fastRefreshEnabled); - host.InstanceSettings().DebuggerPort(m_debuggerPort); host.InstanceSettings().UseDeveloperSupport(true); + host.PackageProviders().Append(CreateStubDeviceInfoPackageProvider()); host.PackageProviders().Append(winrt::make()); winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId( host.InstanceSettings().Properties(), reinterpret_cast(hwnd)); @@ -273,13 +166,83 @@ struct WindowData { winrt::Microsoft::ReactNative::ReactViewOptions viewOptions; viewOptions.ComponentName(appName); - m_CompositionHwndHost.ReactViewHost( + auto windowData = WindowData::GetFromWindow(hwnd); + + if (!m_compRootView) { + if (windowData->m_useLiftedComposition) { + m_compRootView = winrt::Microsoft::ReactNative::CompositionRootView(g_liftedCompositor); + } else { + m_compRootView = winrt::Microsoft::ReactNative::CompositionRootView(); + } + } + + m_compRootView.ReactViewHost( winrt::Microsoft::ReactNative::ReactCoreInjection::MakeViewHost(host, viewOptions)); - auto windowData = WindowData::GetFromWindow(hwnd); - if (!windowData->m_windowInited) { - m_CompositionHwndHost.Initialize((uint64_t)hwnd); - windowData->m_windowInited = true; + if (windowData->m_useLiftedComposition) { + // By using the MicrosoftCompositionContextHelper here, React Native Windows will use Lifted Visuals for its + // tree. + winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositionContext( + InstanceSettings().Properties(), + winrt::Microsoft::ReactNative::Composition::MicrosoftCompositionContextHelper::CreateContext( + g_liftedCompositor)); + + auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create( + g_liftedCompositor, winrt::Microsoft::UI::GetWindowIdFromWindow(hwnd)); + + auto appContent = m_compRootView.Island(); + + auto invScale = 1.0f / ScaleFactor(hwnd); + m_compRootView.RootVisual().Scale({invScale, invScale, invScale}); + + /* + // Future versions of WinAppSDK will have more capabilities around scale and size + auto site = bridge.Site(); + auto siteWindow = site.Environment(); + auto displayScale = siteWindow.DisplayScale(); + + site.ParentScale(displayScale); + site.ActualSize({metrics.Frame.Width / 2, metrics.Frame.Height / 2}); + site.ClientSize( + {static_cast(metrics.Frame.Width / 2 * metrics.PointScaleFactor), + static_cast(metrics.Frame.Height / 2 * metrics.PointScaleFactor)}); + */ + + // bridge.OverrideScale(ScaleFactor(hwnd)); + bridge.Connect(appContent); + bridge.Show(); + + m_compRootView.ScaleFactor(ScaleFactor(hwnd)); + // m_compRootView.ScaleFactor(1); + m_compRootView.Size({m_width / ScaleFactor(hwnd), m_height / ScaleFactor(hwnd)}); + + bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow); + + } else if (!m_target) { + // By using the WindowsCompositionContextHelper here, React Native Windows will use System Visuals for its + // tree. + winrt::Microsoft::ReactNative::Composition::CompositionUIService::SetCompositionContext( + InstanceSettings().Properties(), + winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::CreateContext( + g_compositor)); + + auto interop = g_compositor.as(); + winrt::Windows::UI::Composition::Desktop::DesktopWindowTarget target{nullptr}; + winrt::check_hresult(interop->CreateDesktopWindowTarget( + hwnd, + false, + reinterpret_cast( + winrt::put_abi(target)))); + m_target = target; + + auto root = g_compositor.CreateContainerVisual(); + root.RelativeSizeAdjustment({1.0f, 1.0f}); + root.Offset({0, 0, 0}); + m_target.Root(root); + m_compRootView.RootVisual( + winrt::Microsoft::ReactNative::Composition::WindowsCompositionContextHelper::CreateVisual(root)); + m_compRootView.ScaleFactor(ScaleFactor(hwnd)); + m_compRootView.Size({m_width / ScaleFactor(hwnd), m_height / ScaleFactor(hwnd)}); } } @@ -311,9 +274,29 @@ struct WindowData { return 0; } - LRESULT TranslateMessage(UINT message, WPARAM wparam, LPARAM lparam) noexcept { - if (m_CompositionHwndHost) { - return static_cast(m_CompositionHwndHost.TranslateMessage(message, wparam, lparam)); + float ScaleFactor(HWND hwnd) noexcept { + return GetDpiForWindow(hwnd) / 96.0f; + } + + void UpdateSize(HWND hwnd) noexcept { + RECT rc; + if (GetClientRect(hwnd, &rc)) { + if (m_height != (rc.bottom - rc.top) || m_width != (rc.right - rc.left)) { + m_height = rc.bottom - rc.top; + m_width = rc.right - rc.left; + + if (m_compRootView) { + winrt::Windows::Foundation::Size size{m_width / ScaleFactor(hwnd), m_height / ScaleFactor(hwnd)}; + m_compRootView.Arrange(size); + m_compRootView.Size(size); + } + } + } + } + + LRESULT TranslateMessage(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept { + if (m_compRootView) { + return static_cast(m_compRootView.SendMessage(message, wparam, lparam)); } return 0; } @@ -393,39 +376,33 @@ struct WindowData { case WM_INITDIALOG: { auto boolToCheck = [](bool b) { return b ? BST_CHECKED : BST_UNCHECKED; }; auto self = reinterpret_cast(lparam); - CheckDlgButton(hwnd, IDC_WEBDEBUGGER, boolToCheck(self->m_useWebDebugger)); - CheckDlgButton(hwnd, IDC_FASTREFRESH, boolToCheck(self->m_fastRefreshEnabled)); - CheckDlgButton(hwnd, IDC_DIRECTDEBUGGER, boolToCheck(self->m_useDirectDebugger)); - CheckDlgButton(hwnd, IDC_BREAKONNEXTLINE, boolToCheck(self->m_breakOnNextLine)); + CheckDlgButton(hwnd, IDC_LIFTEDCOMPOSITION, boolToCheck(self->m_useLiftedComposition)); + CheckDlgButton(hwnd, IDC_FASTREFRESH, boolToCheck(self->InstanceSettings().UseFastRefresh())); + CheckDlgButton(hwnd, IDC_DIRECTDEBUGGER, boolToCheck(self->InstanceSettings().UseDirectDebugger())); + CheckDlgButton(hwnd, IDC_BREAKONNEXTLINE, boolToCheck(self->InstanceSettings().DebuggerBreakOnNextLine())); auto portEditControl = GetDlgItem(hwnd, IDC_DEBUGGERPORT); - SetWindowTextW(portEditControl, std::to_wstring(self->m_debuggerPort).c_str()); + SetWindowTextW(portEditControl, std::to_wstring(self->InstanceSettings().DebuggerPort()).c_str()); SendMessageW(portEditControl, (UINT)EM_SETLIMITTEXT, (WPARAM)5, (LPARAM)0); auto cmbEngines = GetDlgItem(hwnd, IDC_JSENGINE); SendMessageW(cmbEngines, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)TEXT("Chakra")); SendMessageW(cmbEngines, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)TEXT("Hermes")); SendMessageW(cmbEngines, (UINT)CB_ADDSTRING, (WPARAM)0, (LPARAM)TEXT("V8")); + // Fabric only supports Hermes right now - So dont actually set JS engine override // SendMessageW(cmbEngines, CB_SETCURSEL, (WPARAM) static_cast(self->m_jsEngine), (LPARAM)0); - auto cmbTheme = GetDlgItem(hwnd, IDC_THEME); - SendMessageW(cmbTheme, CB_ADDSTRING, 0, (LPARAM)L"Default"); - SendMessageW(cmbTheme, CB_ADDSTRING, 0, (LPARAM)L"Light"); - SendMessageW(cmbTheme, CB_ADDSTRING, 0, (LPARAM)L"Dark"); - ComboBox_SetCurSel(cmbTheme, static_cast(self->m_theme)); - return TRUE; } case WM_COMMAND: { switch (LOWORD(wparam)) { case IDOK: { auto self = GetFromWindow(GetParent(hwnd)); - self->m_useWebDebugger = IsDlgButtonChecked(hwnd, IDC_WEBDEBUGGER) == BST_CHECKED; - self->m_fastRefreshEnabled = IsDlgButtonChecked(hwnd, IDC_FASTREFRESH) == BST_CHECKED; - self->m_useDirectDebugger = IsDlgButtonChecked(hwnd, IDC_DIRECTDEBUGGER) == BST_CHECKED; - self->m_breakOnNextLine = IsDlgButtonChecked(hwnd, IDC_BREAKONNEXTLINE) == BST_CHECKED; - - auto themeComboBox = GetDlgItem(hwnd, IDC_THEME); + self->m_useLiftedComposition = (IsDlgButtonChecked(hwnd, IDC_LIFTEDCOMPOSITION) == BST_CHECKED); + self->InstanceSettings().UseFastRefresh(IsDlgButtonChecked(hwnd, IDC_FASTREFRESH) == BST_CHECKED); + self->InstanceSettings().UseDirectDebugger(IsDlgButtonChecked(hwnd, IDC_DIRECTDEBUGGER) == BST_CHECKED); + self->InstanceSettings().DebuggerBreakOnNextLine( + IsDlgButtonChecked(hwnd, IDC_BREAKONNEXTLINE) == BST_CHECKED); WCHAR buffer[6] = {}; auto portEditControl = GetDlgItem(hwnd, IDC_DEBUGGERPORT); @@ -435,14 +412,15 @@ struct WindowData { auto port = std::stoi(buffer); if (port > UINT16_MAX) port = defaultDebuggerPort; - self->m_debuggerPort = static_cast(port); + self->InstanceSettings().DebuggerPort(static_cast(port)); } catch (const std::out_of_range &) { - self->m_debuggerPort = defaultDebuggerPort; + self->InstanceSettings().DebuggerPort(defaultDebuggerPort); } catch (const std::invalid_argument &) { // Don't update the debugger port if the new value can't be parsed // (E.g. includes letters or symbols). } + // Fabric only supports Hermes right now - So dont actually set JS engine override // auto cmbEngines = GetDlgItem(hwnd, IDC_JSENGINE); // int itemIndex = (int)SendMessageW(cmbEngines, (UINT)CB_GETCURSEL, (WPARAM)0, (LPARAM)0); // self->m_jsEngine = static_cast(itemIndex); @@ -466,7 +444,7 @@ HINSTANCE WindowData::s_instance = reinterpret_cast(&__ImageBase); LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) noexcept { auto windowData = WindowData::GetFromWindow(hwnd); if (windowData) { - auto result = WindowData::GetFromWindow(hwnd)->TranslateMessage(message, wparam, lparam); + auto result = WindowData::GetFromWindow(hwnd)->TranslateMessage(hwnd, message, wparam, lparam); if (result) return result; } @@ -492,18 +470,23 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) case WM_GETOBJECT: { if (lparam == UiaRootObjectId) { auto windowData = WindowData::GetFromWindow(hwnd); - if (windowData == nullptr || !windowData->m_windowInited) + if (windowData == nullptr || !windowData->m_compRootView) break; - auto hwndHost = windowData->m_CompositionHwndHost; + auto rootView = windowData->m_compRootView; winrt::com_ptr spReps; - if (!hwndHost.UiaProvider().try_as(spReps)) { + if (!rootView.GetUiaProvider().try_as(spReps)) { break; } LRESULT lResult = UiaReturnRawElementProvider(hwnd, wparam, lparam, spReps.get()); return lResult; } } + case WM_WINDOWPOSCHANGED: { + auto windowData = WindowData::GetFromWindow(hwnd); + windowData->UpdateSize(hwnd); + break; + } } return DefWindowProc(hwnd, message, wparam, lparam); @@ -514,7 +497,7 @@ constexpr PCWSTR c_windowClassName = L"MS_REACTNATIVE_PLAYGROUND_COMPOSITION"; int RunPlayground(int showCmd, bool useWebDebugger) { constexpr PCWSTR appName = L"React Native Playground (Composition)"; - auto windowData = std::make_unique(winrt::Microsoft::ReactNative::CompositionHwndHost()); + auto windowData = std::make_unique(); HWND hwnd = CreateWindow( c_windowClassName, appName, @@ -530,6 +513,8 @@ int RunPlayground(int showCmd, bool useWebDebugger) { WINRT_VERIFY(hwnd); + g_hwndTopLevel = hwnd; // Temporary for prototyping + windowData.release(); ShowWindow(hwnd, showCmd); @@ -539,11 +524,21 @@ int RunPlayground(int showCmd, bool useWebDebugger) { HACCEL hAccelTable = LoadAccelerators(WindowData::s_instance, MAKEINTRESOURCE(IDC_PLAYGROUND_COMPOSITION)); MSG msg = {}; + + // This would be the same as the loop below. - Using the lower loop right now for easier debugging. + // g_liftedDispatcherQueueController.DispatcherQueue().RunEventLoop(); + while (GetMessage(&msg, nullptr, 0, 0)) { +#if USE_WINUI3 + // if (!ContentPreTranslateMessage(&msg)) { +#endif if (!TranslateAccelerator(hwnd, hAccelTable, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } +#if USE_WINUI3 + // } +#endif } return static_cast(msg.wParam); @@ -577,12 +572,15 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR options, reinterpret_cast( winrt::put_abi(g_dispatcherQueueController)))); + g_compositor = winrt::Windows::UI::Composition::Compositor(); #ifdef USE_WINUI3 + // Create a Lifted (WinAppSDK) DispatcherQueue for this thread. This is needed for + // Microsoft.UI.Composition, Content, and Input APIs. g_liftedDispatcherQueueController = winrt::Microsoft::UI::Dispatching::DispatcherQueueController::CreateOnCurrentThread(); + g_liftedCompositor = winrt::Microsoft::UI::Composition::Compositor(); #endif - g_compositor = winrt::Windows::UI::Composition::Compositor(); return RunPlayground(showCmd, false); } diff --git a/packages/playground/windows/playground-composition/Playground-Composition.rc b/packages/playground/windows/playground-composition/Playground-Composition.rc index ab8352c99de..c354aacac30 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.rc +++ b/packages/playground/windows/playground-composition/Playground-Composition.rc @@ -105,7 +105,7 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSM CAPTION "Settings" FONT 8, "MS Shell Dlg", 400, 0, 0x1 BEGIN - CONTROL "&Web Debugger",IDC_WEBDEBUGGER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,8,104,10 + CONTROL "&Lifted Composition",IDC_LIFTEDCOMPOSITION,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,8,104,10 CONTROL "&Fast Refresh",IDC_FASTREFRESH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,26,104,10 CONTROL "&Direct Debugger",IDC_DIRECTDEBUGGER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,44,104,10 CONTROL "&Break On Next Line",IDC_BREAKONNEXTLINE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,8,63,104,10 diff --git a/packages/playground/windows/playground-composition/Playground-Composition.vcxproj b/packages/playground/windows/playground-composition/Playground-Composition.vcxproj index a556872eaa3..03ae7b3ecee 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.vcxproj +++ b/packages/playground/windows/playground-composition/Playground-Composition.vcxproj @@ -116,6 +116,8 @@ Create + + diff --git a/packages/playground/windows/playground-composition/resource.h b/packages/playground/windows/playground-composition/resource.h index c6a21456087..917cfeeaf37 100644 --- a/packages/playground/windows/playground-composition/resource.h +++ b/packages/playground/windows/playground-composition/resource.h @@ -4,7 +4,7 @@ // #define IDC_PLAYGROUND_COMPOSITION 100 #define IDD_ABOUTBOX 100 -#define IDC_WEBDEBUGGER 100 +#define IDC_LIFTEDCOMPOSITION 100 #define IDC_JSBUNDLELIST 100 #define IDM_ABOUT 100 #define IDD_SETTINGSBOX 101 diff --git a/vnext/Microsoft.ReactNative/CompositionRootView.idl b/vnext/Microsoft.ReactNative/CompositionRootView.idl index 54ba69979ab..a7b15732305 100644 --- a/vnext/Microsoft.ReactNative/CompositionRootView.idl +++ b/vnext/Microsoft.ReactNative/CompositionRootView.idl @@ -49,6 +49,10 @@ namespace Microsoft.ReactNative DOC_STRING("Creates a new instance of @CompositionRootView.") CompositionRootView(); +#ifdef USE_WINUI3 + CompositionRootView(Microsoft.UI.Composition.Compositor compositor); +#endif + DOC_STRING( "A ReactViewHost specifies the root UI component and initial properties to render in this RootView" "It must be set to show any React UI elements.") @@ -60,7 +64,7 @@ namespace Microsoft.ReactNative Windows.Foundation.Size Size {get; set; }; DOC_STRING("ScaleFactor for this windows (DPI/96)") - Double ScaleFactor {get; set;}; + Single ScaleFactor {get; set;}; DOC_STRING("Move focus to this @CompositionRootView") FocusNavigationResult NavigateFocus(FocusNavigationRequest request); @@ -71,6 +75,10 @@ namespace Microsoft.ReactNative Int64 SendMessage(UInt32 Msg, UInt64 WParam, Int64 LParam); Object GetUiaProvider(); + +#ifdef USE_WINUI3 + Microsoft.UI.Content.ContentIsland Island { get; }; +#endif } } // namespace Microsoft.ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp index 12d4ecf0c9d..ded94c74847 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp @@ -33,6 +33,20 @@ KeyRoutedEventArgs::KeyRoutedEventArgs(facebook::react::Tag tag, uint32_t msg, u m_key = static_cast(wParam); } +#ifdef USE_WINUI3 +KeyRoutedEventArgs::KeyRoutedEventArgs(facebook::react::Tag tag, winrt::Microsoft::UI::Input::KeyEventArgs const &args) + : m_tag(tag) { + auto keyStatus = args.KeyStatus(); + m_keyStatus.RepeatCount = keyStatus.RepeatCount; + m_keyStatus.ScanCode = keyStatus.ScanCode; + m_keyStatus.IsExtendedKey = keyStatus.IsExtendedKey; + m_keyStatus.IsMenuKeyDown = keyStatus.IsMenuKeyDown; + m_keyStatus.WasKeyDown = keyStatus.WasKeyDown; + m_keyStatus.IsKeyReleased = keyStatus.IsKeyReleased; + m_key = args.VirtualKey(); +} +#endif + int32_t KeyRoutedEventArgs::OriginalSource() noexcept { return m_tag; } @@ -61,8 +75,10 @@ winrt::Windows::System::VirtualKey KeyRoutedEventArgs::OriginalKey() noexcept { return m_key; } -PointerPointProperties::PointerPointProperties(const winrt::Windows::UI::Input::PointerPointProperties &ppp) +#ifdef USE_WINUI3 +PointerPointProperties::PointerPointProperties(const winrt::Microsoft::UI::Input::PointerPointProperties &ppp) : m_sysPointerPointProps(ppp) {} +#endif PointerPointProperties::PointerPointProperties( bool isBarrelButtonPressed, @@ -107,97 +123,204 @@ PointerPointProperties::PointerPointProperties( m_yTilt(yTilt) {} winrt::Windows::Foundation::Rect PointerPointProperties::ContactRect() noexcept { +#ifdef USE_WINUI3 return m_sysPointerPointProps.ContactRect(); +#else + assert(false); + return {}; +#endif } bool PointerPointProperties::IsBarrelButtonPressed() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsBarrelButtonPressed() : m_isBarrelButtonPressed; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsBarrelButtonPressed(); + } +#endif + return m_isBarrelButtonPressed; } bool PointerPointProperties::IsCanceled() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsCanceled() : m_isCanceled; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsCanceled(); + } +#endif + return m_isCanceled; } bool PointerPointProperties::IsEraser() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsEraser() : m_isEraser; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsEraser(); + } +#endif + return m_isEraser; } bool PointerPointProperties::IsHorizontalMouseWheel() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsHorizontalMouseWheel() : m_isHorizontalMouseWheel; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsHorizontalMouseWheel(); + } +#endif + return m_isHorizontalMouseWheel; } bool PointerPointProperties::IsInRange() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsInRange() : m_isInRange; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsInRange(); + } +#endif + return m_isInRange; } bool PointerPointProperties::IsInverted() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsInverted() : m_isInverted; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsInverted(); + } +#endif + return m_isInverted; } bool PointerPointProperties::IsLeftButtonPressed() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsLeftButtonPressed() : m_isLeftButtonPressed; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsLeftButtonPressed(); + } +#endif + return m_isLeftButtonPressed; } bool PointerPointProperties::IsMiddleButtonPressed() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsMiddleButtonPressed() : m_isMiddleButtonPressed; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsMiddleButtonPressed(); + } +#endif + return m_isMiddleButtonPressed; } bool PointerPointProperties::IsPrimary() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsPrimary() : m_isPrimary; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsPrimary(); + } +#endif + return m_isPrimary; } bool PointerPointProperties::IsRightButtonPressed() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsRightButtonPressed() : m_isRightButtonPressed; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsRightButtonPressed(); + } +#endif + return m_isRightButtonPressed; } bool PointerPointProperties::IsXButton1Pressed() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsXButton1Pressed() : m_isXButton1Pressed; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsXButton1Pressed(); + } +#endif + return m_isXButton1Pressed; } bool PointerPointProperties::IsXButton2Pressed() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.IsXButton2Pressed() : m_isXButton2Pressed; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.IsXButton2Pressed(); + } +#endif + return m_isXButton2Pressed; } int32_t PointerPointProperties::MouseWheelDelta() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.MouseWheelDelta() : m_mouseWheelDelta; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.MouseWheelDelta(); + } +#endif + return m_mouseWheelDelta; } float PointerPointProperties::Orientation() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.Orientation() : m_orientation; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.Orientation(); + } +#endif + return m_orientation; } PointerUpdateKind PointerPointProperties::PointerUpdateKind() noexcept { - return m_sysPointerPointProps ? static_cast( - m_sysPointerPointProps.PointerUpdateKind()) - : m_pointerUpdateKind; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return static_cast( + m_sysPointerPointProps.PointerUpdateKind()); + } +#endif + return m_pointerUpdateKind; } float PointerPointProperties::Pressure() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.Pressure() : m_pressure; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.Pressure(); + } +#endif + return m_pressure; } bool PointerPointProperties::TouchConfidence() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.TouchConfidence() : m_touchConfidence; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.TouchConfidence(); + } +#endif + return m_touchConfidence; } float PointerPointProperties::Twist() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.Twist() : m_twist; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.Twist(); + } +#endif + return m_twist; } float PointerPointProperties::XTilt() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.XTilt() : m_xTilt; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.XTilt(); + } +#endif + return m_xTilt; } float PointerPointProperties::YTilt() noexcept { - return m_sysPointerPointProps ? m_sysPointerPointProps.YTilt() : m_yTilt; +#ifdef USE_WINUI3 + if (m_sysPointerPointProps) { + return m_sysPointerPointProps.YTilt(); + } +#endif + return m_yTilt; } -PointerPoint::PointerPoint(const winrt::Windows::UI::Input::PointerPoint &pp) : m_sysPointerPoint(pp) {} +#ifdef USE_WINUI3 +PointerPoint::PointerPoint(const winrt::Microsoft::UI::Input::PointerPoint &pp) : m_sysPointerPoint(pp) {} +#endif -PointerPoint::PointerPoint(uint32_t msg, uint64_t wParam, int64_t lParam) { +PointerPoint::PointerPoint(uint32_t msg, uint64_t wParam, int64_t lParam, float scaleFactor) { m_msg = msg; m_wParam = wParam; m_lParam = lParam; + m_scaleFactor = scaleFactor; if (IsPointerMessage(msg)) { const unsigned int pointerId = GET_POINTERID_WPARAM(wParam); bool result = ::GetPointerInfo(pointerId, &m_pi); @@ -206,19 +329,30 @@ PointerPoint::PointerPoint(uint32_t msg, uint64_t wParam, int64_t lParam) { } uint32_t PointerPoint::FrameId() noexcept { - return m_sysPointerPoint ? m_sysPointerPoint.FrameId() : m_pi.frameId; +#ifdef USE_WINUI3 + if (m_sysPointerPoint) { + return m_sysPointerPoint.FrameId(); + } +#endif + return m_pi.frameId; } bool PointerPoint::IsInContact() noexcept { - return m_sysPointerPoint ? m_sysPointerPoint.IsInContact() - : ((m_pi.pointerFlags & POINTER_FLAG_INCONTACT) == POINTER_FLAG_INCONTACT); +#ifdef USE_WINUI3 + if (m_sysPointerPoint) { + return m_sysPointerPoint.IsInContact(); + } +#endif + return ((m_pi.pointerFlags & POINTER_FLAG_INCONTACT) == POINTER_FLAG_INCONTACT); } winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType PointerPoint::PointerDeviceType() noexcept { +#ifdef USE_WINUI3 if (m_sysPointerPoint) { return static_cast( - m_sysPointerPoint.PointerDevice().PointerDeviceType()); + m_sysPointerPoint.PointerDeviceType()); } +#endif if (m_pi.pointerId) { switch (m_pi.pointerType) { @@ -239,23 +373,34 @@ winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType PointerPoin } uint32_t PointerPoint::PointerId() noexcept { - return m_sysPointerPoint ? m_sysPointerPoint.PointerId() : (m_pi.pointerId ? m_pi.pointerId : 1 /* MOUSE */); +#ifdef USE_WINUI3 + if (m_sysPointerPoint) { + return m_sysPointerPoint.PointerId(); + } +#endif + return (m_pi.pointerId ? m_pi.pointerId : 1 /* MOUSE */); } winrt::Windows::Foundation::Point PointerPoint::Position() noexcept { - return m_sysPointerPoint - ? m_sysPointerPoint.Position() - : (m_pi.pointerId - ? winrt::Windows::Foundation:: - Point{static_cast(m_pi.ptPixelLocation.x), static_cast(m_pi.ptPixelLocation.y)} - : winrt::Windows::Foundation::Point{ - static_cast(GET_X_LPARAM(m_lParam)), static_cast(GET_Y_LPARAM(m_lParam))}); +#ifdef USE_WINUI3 + if (m_sysPointerPoint) { + return m_sysPointerPoint.Position(); + } +#endif + return m_pi.pointerId + ? winrt::Windows::Foundation:: + Point{static_cast(m_pi.ptPixelLocation.x / m_scaleFactor), static_cast(m_pi.ptPixelLocation.y / m_scaleFactor)} + : winrt::Windows::Foundation::Point{ + static_cast(GET_X_LPARAM(m_lParam) / m_scaleFactor), + static_cast(GET_Y_LPARAM(m_lParam) / m_scaleFactor)}; } winrt::Microsoft::ReactNative::Composition::Input::PointerPointProperties PointerPoint::Properties() noexcept { +#ifdef USE_WINUI3 if (m_sysPointerPoint) { return winrt::make(m_sysPointerPoint.Properties()); } +#endif if (m_pi.pointerId) { auto pointerUpdateKind = winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::Other; @@ -373,12 +518,22 @@ winrt::Microsoft::ReactNative::Composition::Input::PointerPointProperties Pointe } uint64_t PointerPoint::Timestamp() noexcept { - return m_sysPointerPoint ? m_sysPointerPoint.Timestamp() : m_pi.dwTime; +#ifdef USE_WINUI3 + if (m_sysPointerPoint) { + return m_sysPointerPoint.Timestamp(); + } +#endif + return m_pi.dwTime; } winrt::Microsoft::ReactNative::Composition::Input::PointerPoint PointerPoint::GetTransformedPoint( const IPointerPointTransform &transform) noexcept { - return winrt::make(m_sysPointerPoint); +#ifdef USE_WINUI3 + if (m_sysPointerPoint) { + return winrt::make(m_sysPointerPoint); + } +#endif + return winrt::make(m_msg, m_wParam, m_lParam, m_scaleFactor); } bool PointerPoint::IsPointerMessage(uint32_t message) noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h index 62c207bd646..7b69ec809c1 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h @@ -9,6 +9,9 @@ #include #include #include +#ifdef USE_WINUI3 +#include +#endif namespace winrt::Microsoft::ReactNative::Composition::Input::implementation { @@ -17,6 +20,9 @@ struct KeyRoutedEventArgs : winrt::implements< winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs, winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> { KeyRoutedEventArgs(facebook::react::Tag tag, uint32_t msg, uint64_t wParam, int64_t lParam); +#ifdef USE_WINUI3 + KeyRoutedEventArgs(facebook::react::Tag tag, winrt::Microsoft::UI::Input::KeyEventArgs const &args); +#endif int32_t OriginalSource() noexcept; winrt::hstring DeviceId() noexcept; @@ -34,7 +40,9 @@ struct KeyRoutedEventArgs : winrt::implements< }; struct PointerPointProperties : PointerPointPropertiesT { - PointerPointProperties(const winrt::Windows::UI::Input::PointerPointProperties &ppp); +#ifdef USE_WINUI3 + PointerPointProperties(const winrt::Microsoft::UI::Input::PointerPointProperties &ppp); +#endif PointerPointProperties( bool isBarrelButtonPressed, @@ -81,7 +89,9 @@ struct PointerPointProperties : PointerPointPropertiesT float YTilt() noexcept; private: - winrt::Windows::UI::Input::PointerPointProperties m_sysPointerPointProps{nullptr}; +#ifdef USE_WINUI3 + winrt::Microsoft::UI::Input::PointerPointProperties m_sysPointerPointProps{nullptr}; +#endif // When not using m_sysPointerPointProps bool m_isBarrelButtonPressed : 1; @@ -107,8 +117,10 @@ struct PointerPointProperties : PointerPointPropertiesT }; struct PointerPoint : PointerPointT { - PointerPoint(const winrt::Windows::UI::Input::PointerPoint &pp); - PointerPoint(uint32_t msg, uint64_t wParam, int64_t lParam); +#ifdef USE_WINUI3 + PointerPoint(const winrt::Microsoft::UI::Input::PointerPoint &pp); +#endif + PointerPoint(uint32_t msg, uint64_t wParam, int64_t lParam, float scaleFactor); uint32_t FrameId() noexcept; bool IsInContact() noexcept; @@ -124,13 +136,16 @@ struct PointerPoint : PointerPointT { bool IsPointerMessage(uint32_t message) noexcept; // Windows::Input - winrt::Windows::UI::Input::PointerPoint m_sysPointerPoint{nullptr}; +#ifdef USE_WINUI3 + winrt::Microsoft::UI::Input::PointerPoint m_sysPointerPoint{nullptr}; +#endif // WM_POINTER* POINTER_INFO m_pi = {0}; // WM_*MOUSE uint32_t m_msg; uint64_t m_wParam; int64_t m_lParam; + float m_scaleFactor; }; struct PointerRoutedEventArgs : PointerRoutedEventArgsT { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp index 3327f2f275d..1f3883ebec9 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp @@ -18,6 +18,10 @@ #include "CompositionViewComponentView.h" #include "RootComponentView.h" +#ifdef USE_WINUI3 +#include +#endif + namespace Microsoft::ReactNative { const PointerId MOUSE_POINTER_ID = 1; @@ -98,17 +102,120 @@ struct CompositionKeyboardSource } private: - CompositionEventHandler *m_outer; + CompositionEventHandler *m_outer{nullptr}; }; -CompositionEventHandler::CompositionEventHandler(const winrt::Microsoft::ReactNative::ReactContext &context) - : m_context(context) {} +#ifdef USE_WINUI3 +struct CompositionInputKeyboardSource : winrt::implements< + CompositionInputKeyboardSource, + winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource> { + CompositionInputKeyboardSource(winrt::Microsoft::UI::Input::InputKeyboardSource source) : m_source(source) {} + + winrt::Windows::UI::Core::CoreVirtualKeyStates GetKeyState(winrt::Windows::System::VirtualKey key) noexcept { + if (!m_source) + return winrt::Windows::UI::Core::CoreVirtualKeyStates::None; + + static_assert( + static_cast( + winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == + winrt::Windows::UI::Core::CoreVirtualKeyStates::Down); + static_assert( + static_cast( + winrt::Microsoft::UI::Input::VirtualKeyStates::Locked) == + winrt::Windows::UI::Core::CoreVirtualKeyStates::Locked); + static_assert( + static_cast( + winrt::Microsoft::UI::Input::VirtualKeyStates::None) == + winrt::Windows::UI::Core::CoreVirtualKeyStates::None); + return static_cast(m_source.GetKeyState(key)); + } + + void Disconnect() noexcept { + m_source = nullptr; + } + + private: + winrt::Microsoft::UI::Input::InputKeyboardSource m_source{nullptr}; +}; +#endif CompositionEventHandler::CompositionEventHandler( const winrt::Microsoft::ReactNative::ReactContext &context, const winrt::Microsoft::ReactNative::CompositionRootView &CompositionRootView) - : CompositionEventHandler(context) { + : m_context(context) { m_compRootView = CompositionRootView; + +#ifdef USE_WINUI3 + if (auto island = m_compRootView.Island()) { + auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(island); + + pointerSource.PointerPressed([this]( + winrt::Microsoft::UI::Input::InputPointerSource const &, + winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + auto pp = winrt::make( + args.CurrentPoint()); + onPointerPressed(pp, args.KeyModifiers()); + }); + + pointerSource.PointerReleased([this]( + winrt::Microsoft::UI::Input::InputPointerSource const &, + winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + auto pp = winrt::make( + args.CurrentPoint()); + onPointerReleased(pp, args.KeyModifiers()); + }); + + pointerSource.PointerMoved([this]( + winrt::Microsoft::UI::Input::InputPointerSource const &, + winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + auto pp = winrt::make( + args.CurrentPoint()); + onPointerMoved(pp, args.KeyModifiers()); + }); + + pointerSource.PointerWheelChanged([this]( + winrt::Microsoft::UI::Input::InputPointerSource const &, + winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + auto pp = winrt::make( + args.CurrentPoint()); + onPointerWheelChanged(pp, args.KeyModifiers()); + }); + + auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island); + + keyboardSource.KeyDown([this]( + winrt::Microsoft::UI::Input::InputKeyboardSource const &source, + winrt::Microsoft::UI::Input::KeyEventArgs const &args) { + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto keyArgs = winrt::make( + focusedComponent + ? focusedComponent->tag() + : static_cast( + winrt::get_self(m_compRootView) + ->GetTag()), + args); + auto keyboardSource = winrt::make(source); + onKeyDown(keyboardSource, keyArgs); + winrt::get_self(keyboardSource)->Disconnect(); + }); + + keyboardSource.KeyUp([this]( + winrt::Microsoft::UI::Input::InputKeyboardSource const &source, + winrt::Microsoft::UI::Input::KeyEventArgs const &args) { + auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto keyArgs = winrt::make( + focusedComponent + ? focusedComponent->tag() + : static_cast( + winrt::get_self(m_compRootView) + ->GetTag()), + args); + auto keyboardSource = winrt::make(source); + onKeyUp(keyboardSource, keyArgs); + winrt::get_self(keyboardSource)->Disconnect(); + }); + } +#endif }; CompositionEventHandler::~CompositionEventHandler() {} @@ -133,9 +240,7 @@ void CompositionEventHandler::onPointerWheelChanged( auto position = pointerPoint.Position(); facebook::react::Point ptLocal; - facebook::react::Point ptScaled = { - static_cast(position.X / m_compRootView.ScaleFactor()), - static_cast(position.Y / m_compRootView.ScaleFactor())}; + facebook::react::Point ptScaled = {static_cast(position.X), static_cast(position.Y)}; auto tag = RootComponentView().hitTest(ptScaled, ptLocal); @@ -186,37 +291,37 @@ int64_t CompositionEventHandler::SendMessage(uint32_t msg, uint64_t wParam, int6 switch (msg) { case WM_LBUTTONDOWN: { auto pp = winrt::make( - msg, wParam, lParam); + msg, wParam, lParam, m_compRootView.ScaleFactor()); onPointerPressed(pp, GetKeyModifiers(wParam)); return 0; } case WM_POINTERDOWN: { auto pp = winrt::make( - msg, wParam, lParam); + msg, wParam, lParam, m_compRootView.ScaleFactor()); onPointerPressed(pp, GetKeyModifiers(wParam)); return 0; } case WM_LBUTTONUP: { auto pp = winrt::make( - msg, wParam, lParam); + msg, wParam, lParam, m_compRootView.ScaleFactor()); onPointerReleased(pp, GetKeyModifiers(wParam)); return 0; } case WM_POINTERUP: { auto pp = winrt::make( - msg, wParam, lParam); + msg, wParam, lParam, m_compRootView.ScaleFactor()); onPointerReleased(pp, GetKeyModifiers(wParam)); return 0; } case WM_MOUSEMOVE: { auto pp = winrt::make( - msg, wParam, lParam); + msg, wParam, lParam, m_compRootView.ScaleFactor()); onPointerMoved(pp, GetKeyModifiers(wParam)); return 0; } case WM_MOUSEWHEEL: { auto pp = winrt::make( - msg, wParam, lParam); + msg, wParam, lParam, m_compRootView.ScaleFactor()); onPointerWheelChanged(pp, GetKeyModifiers(wParam)); } case WM_CHAR: @@ -536,6 +641,9 @@ facebook::react::PointerEvent CreatePointerEventFromIncompleteHoverData( void CompositionEventHandler::onPointerMoved( const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint, winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept { + if (SurfaceId() == -1) + return; + int pointerId = pointerPoint.PointerId(); auto position = pointerPoint.Position(); @@ -544,9 +652,7 @@ void CompositionEventHandler::onPointerMoved( ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) { facebook::react::Point ptLocal; - facebook::react::Point ptScaled = { - static_cast(position.X / m_compRootView.ScaleFactor()), - static_cast(position.Y / m_compRootView.ScaleFactor())}; + facebook::react::Point ptScaled = {position.X, position.Y}; auto tag = RootComponentView().hitTest(ptScaled, ptLocal); if (tag == -1) @@ -600,9 +706,7 @@ void CompositionEventHandler::onPointerPressed( ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) { facebook::react::Point ptLocal; - facebook::react::Point ptScaled = { - static_cast(position.X / m_compRootView.ScaleFactor()), - static_cast(position.Y / m_compRootView.ScaleFactor())}; + facebook::react::Point ptScaled = {position.X, position.Y}; auto tag = RootComponentView().hitTest(ptScaled, ptLocal); if (tag == -1) @@ -665,9 +769,7 @@ void CompositionEventHandler::onPointerReleased( ::Microsoft::ReactNative::FabricUIManager::FromProperties(m_context.Properties())) { facebook::react::Point ptLocal; - facebook::react::Point ptScaled = { - static_cast(position.X / m_compRootView.ScaleFactor()), - static_cast(position.Y / m_compRootView.ScaleFactor())}; + facebook::react::Point ptScaled = {position.X, position.Y}; auto tag = RootComponentView().hitTest(ptScaled, ptLocal); if (tag == -1) diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h index 9b149e28ec2..9d6f5a31ed5 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h @@ -28,7 +28,6 @@ typedef int PointerId; class CompositionEventHandler { public: - CompositionEventHandler(const winrt::Microsoft::ReactNative::ReactContext &context); CompositionEventHandler( const winrt::Microsoft::ReactNative::ReactContext &context, const winrt::Microsoft::ReactNative::CompositionRootView &CompositionRootView); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp index a9746c9955d..ae09c2c76e2 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.cpp @@ -67,8 +67,8 @@ void CompositionHwndHost::Initialize(uint64_t hwnd) noexcept { UpdateSize(); } -double CompositionHwndHost::ScaleFactor() noexcept { - return GetDpiForWindow(m_hwnd) / 96.0; +float CompositionHwndHost::ScaleFactor() noexcept { + return GetDpiForWindow(m_hwnd) / 96.0f; } void CompositionHwndHost::UpdateSize() noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.h index 33fd872826b..bf2890b40e3 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionHwndHost.h @@ -38,7 +38,7 @@ struct CompositionHwndHost : CompositionHwndHostT { void CreateDesktopWindowTarget(HWND window); void CreateCompositionRoot(); void UpdateSize() noexcept; - double ScaleFactor() noexcept; + float ScaleFactor() noexcept; HWND m_hwnd; winrt::Microsoft::ReactNative::CompositionRootView m_compRootView{nullptr}; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp index f0dd6e35880..1518d364615 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.cpp @@ -3,6 +3,10 @@ #include #include "UiaHelpers.h" +#ifdef USE_WINUI3 +#include +#endif + namespace winrt::Microsoft::ReactNative::implementation { CompositionRootAutomationProvider::CompositionRootAutomationProvider( @@ -56,6 +60,14 @@ HRESULT __stdcall CompositionRootAutomationProvider::get_HostRawElementProvider( if (pRetVal == nullptr) return E_POINTER; +#ifdef USE_WINUI3 + if (m_island) { + winrt::Windows::Foundation::IInspectable host = m_island.GetAutomationHostProvider(); + *pRetVal = host.as().detach(); + return S_OK; + } +#endif + // TODO: assumes windowed if (!IsWindow(m_hwnd)) return UIA_E_ELEMENTNOTAVAILABLE; @@ -172,6 +184,12 @@ void CompositionRootAutomationProvider::SetHwnd(HWND hwnd) noexcept { m_hwnd = hwnd; } +#ifdef USE_WINUI3 +void CompositionRootAutomationProvider::SetIsland(winrt::Microsoft::UI::Content::ContentIsland &island) noexcept { + m_island = island; +} +#endif + HRESULT __stdcall CompositionRootAutomationProvider::Navigate( NavigateDirection direction, IRawElementProviderFragment **pRetVal) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h index 8ba2f23a4e4..aafe93559e9 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootAutomationProvider.h @@ -43,6 +43,9 @@ class CompositionRootAutomationProvider : public winrt::implements< const std::shared_ptr<::Microsoft::ReactNative::RootComponentView> &componentView) noexcept; void SetHwnd(HWND hwnd) noexcept; +#ifdef USE_WINUI3 + void SetIsland(winrt::Microsoft::UI::Content::ContentIsland &island) noexcept; +#endif bool WasPropertyAdvised(PROPERTYID prop) noexcept; bool WasEventAdvised(EVENTID event) noexcept; @@ -70,6 +73,9 @@ class CompositionRootAutomationProvider : public winrt::implements< std::vector m_advisedProperties{}; ::Microsoft::ReactNative::ReactTaggedView m_view; HWND m_hwnd{nullptr}; +#ifdef USE_WINUI3 + winrt::Microsoft::UI::Content::ContentIsland m_island{nullptr}; +#endif }; } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp index 6413aeb6ce8..7e984a171aa 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.cpp @@ -21,6 +21,11 @@ #include "ReactNativeHost.h" #include "RootComponentView.h" +#ifdef USE_WINUI3 +#include +#include "CompositionRootAutomationProvider.h" +#endif + namespace winrt::Microsoft::ReactNative::implementation { //! This class ensures that we access ReactRootView from UI thread. @@ -98,6 +103,11 @@ inline Mso::Future CompositionReactViewInstance::PostInUIQueue(TAction &&a CompositionRootView::CompositionRootView() noexcept {} +#ifdef USE_WINUI3 +CompositionRootView::CompositionRootView(winrt::Microsoft::UI::Composition::Compositor compositor) noexcept + : m_compositor(compositor) {} +#endif + ReactNative::IReactViewHost CompositionRootView::ReactViewHost() noexcept { return m_reactViewHost; } @@ -138,11 +148,11 @@ void CompositionRootView::Size(winrt::Windows::Foundation::Size value) noexcept m_size = value; } -double CompositionRootView::ScaleFactor() noexcept { +float CompositionRootView::ScaleFactor() noexcept { return m_scaleFactor; } -void CompositionRootView::ScaleFactor(double value) noexcept { +void CompositionRootView::ScaleFactor(float value) noexcept { m_scaleFactor = value; } @@ -356,6 +366,38 @@ winrt::Windows::Foundation::Size CompositionRootView::Arrange(winrt::Windows::Fo return finalSize; } +#ifdef USE_WINUI3 +winrt::Microsoft::UI::Content::ContentIsland CompositionRootView::Island() noexcept { + if (!m_compositor) { + return nullptr; + } + + if (!m_island) { + auto rootVisual = m_compositor.CreateSpriteVisual(); + rootVisual.RelativeSizeAdjustment({1, 1}); + + RootVisual(winrt::Microsoft::ReactNative::Composition::MicrosoftCompositionContextHelper::CreateVisual(rootVisual)); + m_island = winrt::Microsoft::UI::Content::ContentIsland::Create(rootVisual); + + m_island.AutomationProviderRequested( + [this]( + winrt::Microsoft::UI::Content::ContentIsland const &, + winrt::Microsoft::UI::Content::ContentIslandAutomationProviderRequestedEventArgs const &args) { + auto provider = GetUiaProvider(); + auto pRootProvider = + static_cast( + provider.as().get()); + if (pRootProvider != nullptr) { + pRootProvider->SetIsland(m_island); + } + args.AutomationProvider(std::move(provider)); + args.Handled(true); + }); + } + return m_island; +} +#endif + ::Microsoft::ReactNative::RootComponentView *CompositionRootView::GetComponentView() noexcept { if (!m_context || m_context.Handle().LoadingState() != winrt::Microsoft::ReactNative::LoadingState::Loaded) return nullptr; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h index 15806b902a5..247e6048033 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView.h @@ -43,6 +43,11 @@ struct FocusNavigationResult : FocusNavigationResultT { struct CompositionRootView : CompositionRootViewT, ::Microsoft::ReactNative::ICompositionRootView { CompositionRootView() noexcept; +#ifdef USE_WINUI3 + CompositionRootView(winrt::Microsoft::UI::Composition::Compositor compositor) noexcept; + winrt::Microsoft::UI::Content::ContentIsland Island() noexcept; +#endif + // property ReactViewHost ReactNative::IReactViewHost ReactViewHost() noexcept; void ReactViewHost(ReactNative::IReactViewHost const &value) noexcept; @@ -56,8 +61,8 @@ struct CompositionRootView : CompositionRootViewT, ::Micros void Size(winrt::Windows::Foundation::Size value) noexcept; // ScaleFactor (DPI) - double ScaleFactor() noexcept; - void ScaleFactor(double value) noexcept; + float ScaleFactor() noexcept; + void ScaleFactor(float value) noexcept; winrt::Windows::Foundation::Size Measure(winrt::Windows::Foundation::Size const &availableSize) const; winrt::Windows::Foundation::Size Arrange(winrt::Windows::Foundation::Size finalSize) const; @@ -89,11 +94,16 @@ struct CompositionRootView : CompositionRootViewT, ::Micros void UninitRootView() noexcept; private: +#ifdef USE_WINUI3 + winrt::Microsoft::UI::Composition::Compositor m_compositor{nullptr}; + winrt::Microsoft::UI::Content::ContentIsland m_island{nullptr}; +#endif + bool m_isInitialized{false}; bool m_isJSViewAttached{false}; IReactDispatcher m_uiDispatcher{nullptr}; int64_t m_rootTag{-1}; - double m_scaleFactor{1.0}; + float m_scaleFactor{1.0}; winrt::Windows::Foundation::Size m_size; winrt::Microsoft::ReactNative::ReactContext m_context; winrt::Microsoft::ReactNative::IReactViewHost m_reactViewHost; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp index 6bb2bbf0fd4..24635c8936a 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionRootView_emptyimpl.cpp @@ -47,11 +47,11 @@ winrt::Windows::Foundation::Size CompositionRootView::Size() noexcept { void CompositionRootView::Size(winrt::Windows::Foundation::Size) noexcept {} -double CompositionRootView::ScaleFactor() noexcept { +float CompositionRootView::ScaleFactor() noexcept { return 0; } -void CompositionRootView::ScaleFactor(double) noexcept {} +void CompositionRootView::ScaleFactor(float) noexcept {} winrt::IInspectable CompositionRootView::GetUiaProvider() noexcept { return nullptr; diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp index 780194cb936..39e422bcd24 100644 --- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp @@ -183,8 +183,8 @@ void FabricUIManager::startSurface( facebook::react::LayoutContext context; facebook::react::LayoutConstraints constraints; - context.pointScaleFactor = static_cast(CompositionRootView->ScaleFactor()); - context.fontSizeMultiplier = static_cast(CompositionRootView->ScaleFactor()); + context.pointScaleFactor = CompositionRootView->ScaleFactor(); + context.fontSizeMultiplier = CompositionRootView->ScaleFactor(); constraints.minimumSize.height = static_cast(CompositionRootView->GetActualHeight()); constraints.minimumSize.width = static_cast(CompositionRootView->GetActualWidth()); constraints.maximumSize.height = static_cast(CompositionRootView->GetActualHeight()); diff --git a/vnext/Microsoft.ReactNative/Views/ICompositionRootView.h b/vnext/Microsoft.ReactNative/Views/ICompositionRootView.h index 09a1dacb8ab..c97fae29a50 100644 --- a/vnext/Microsoft.ReactNative/Views/ICompositionRootView.h +++ b/vnext/Microsoft.ReactNative/Views/ICompositionRootView.h @@ -13,7 +13,7 @@ namespace Microsoft::ReactNative { struct ICompositionRootView : public facebook::react::IReactRootView { virtual winrt::Microsoft::ReactNative::Composition::IVisual GetVisual() const noexcept = 0; - virtual double ScaleFactor() noexcept = 0; + virtual float ScaleFactor() noexcept = 0; }; } // namespace Microsoft::ReactNative