From 15107f630728494e06a7dd4a7382d57a2b4cdb01 Mon Sep 17 00:00:00 2001 From: Andrew <30809111+acoates-ms@users.noreply.github.com> Date: Fri, 30 Aug 2024 13:53:56 -0700 Subject: [PATCH 1/2] Cherry pick changes to 0.74 --- ...-0aaea0d3-da44-4492-a0e8-fe75cfbeb247.json | 7 + ...-58e392a3-bd7d-42b1-84d3-f2a6dd66f6ff.json | 7 + ...-d8175a4a-46c8-40d0-b42b-097f50a633c1.json | 7 + ...-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json | 7 + .../RNTesterApp-Fabric/RNTesterApp-Fabric.cpp | 61 +- .../CustomComponent.cpp | 268 +++-- .../CustomComponent.idl | 3 +- .../playground-composition/DrawingIsland.cpp | 976 ++++++++++++++++++ .../playground-composition/DrawingIsland.h | 250 +++++ .../Playground-Composition.cpp | 70 +- .../Playground-Composition.rc | 1 + .../Playground-Composition.vcxproj | 1 + .../windows/playground-composition/resource.h | 1 + vnext/Desktop/module.g.cpp | 7 - vnext/Microsoft.ReactNative/ComponentView.idl | 65 +- .../Composition.Input.idl | 2 + .../CompositionComponentView.idl | 67 +- .../Fabric/AbiEventEmitter.cpp | 21 + .../Fabric/AbiEventEmitter.h | 23 + .../Fabric/AbiShadowNode.cpp | 7 + .../Fabric/AbiShadowNode.h | 3 + .../Fabric/ComponentView.cpp | 368 +++++-- .../Fabric/ComponentView.h | 185 +++- .../ActivityIndicatorComponentView.cpp | 4 +- .../Composition/ComponentViewRegistry.cpp | 3 + .../Fabric/Composition/Composition.Input.cpp | 32 +- .../Fabric/Composition/Composition.Input.h | 26 +- .../CompositionDynamicAutomationProvider.cpp | 15 + .../Composition/CompositionEventHandler.cpp | 266 ++--- .../Composition/CompositionEventHandler.h | 12 +- .../CompositionViewComponentView.cpp | 190 ++-- .../CompositionViewComponentView.h | 78 +- .../ContentIslandComponentView.cpp | 133 +++ .../Composition/ContentIslandComponentView.h | 60 ++ .../DebuggingOverlayComponentView.cpp | 3 +- .../Fabric/Composition/ImageComponentView.cpp | 5 +- .../WindowsModalHostViewComponentView.cpp | 3 +- .../Composition/ParagraphComponentView.cpp | 7 +- .../Composition/ParagraphComponentView.h | 1 - .../ReactCompositionViewComponentBuilder.cpp | 126 ++- .../ReactCompositionViewComponentBuilder.h | 38 +- .../Fabric/Composition/ReactNativeIsland.cpp | 58 +- .../Fabric/Composition/ReactNativeIsland.h | 6 + .../Fabric/Composition/RootComponentView.cpp | 10 +- .../Fabric/Composition/RootComponentView.h | 2 + .../Composition/ScrollViewComponentView.cpp | 377 +++---- .../Composition/ScrollViewComponentView.h | 4 +- .../Composition/SwitchComponentView.cpp | 6 +- .../Fabric/Composition/SwitchComponentView.h | 4 +- .../WindowsTextInputComponentView.cpp | 29 +- .../TextInput/WindowsTextInputComponentView.h | 15 +- .../Fabric/Composition/UiaHelpers.cpp | 19 + .../Fabric/Composition/UiaHelpers.h | 4 + .../UnimplementedNativeViewComponentView.cpp | 3 +- .../Fabric/FabricUIManagerModule.cpp | 17 +- .../WindowsComponentDescriptorRegistry.cpp | 4 +- .../components/view/HostPlatformViewProps.cpp | 21 +- .../components/view/HostPlatformViewProps.h | 3 + .../IReactCompositionViewComponentBuilder.idl | 16 +- .../IReactViewComponentBuilder.idl | 61 +- .../ReactNativeIsland.idl | 3 + vnext/PropertySheets/WinUI.props | 2 +- vnext/Shared/Shared.vcxitems | 7 + 63 files changed, 3185 insertions(+), 895 deletions(-) create mode 100644 change/react-native-windows-0aaea0d3-da44-4492-a0e8-fe75cfbeb247.json create mode 100644 change/react-native-windows-58e392a3-bd7d-42b1-84d3-f2a6dd66f6ff.json create mode 100644 change/react-native-windows-d8175a4a-46c8-40d0-b42b-097f50a633c1.json create mode 100644 change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json create mode 100644 packages/playground/windows/playground-composition/DrawingIsland.cpp create mode 100644 packages/playground/windows/playground-composition/DrawingIsland.h create mode 100644 vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp create mode 100644 vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h create mode 100644 vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp create mode 100644 vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h diff --git a/change/react-native-windows-0aaea0d3-da44-4492-a0e8-fe75cfbeb247.json b/change/react-native-windows-0aaea0d3-da44-4492-a0e8-fe75cfbeb247.json new file mode 100644 index 00000000000..fc4be19c60b --- /dev/null +++ b/change/react-native-windows-0aaea0d3-da44-4492-a0e8-fe75cfbeb247.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Add Support for AccessibilityPosInSet", + "packageName": "react-native-windows", + "email": "34109996+chiaramooney@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-58e392a3-bd7d-42b1-84d3-f2a6dd66f6ff.json b/change/react-native-windows-58e392a3-bd7d-42b1-84d3-f2a6dd66f6ff.json new file mode 100644 index 00000000000..44c62ee4aeb --- /dev/null +++ b/change/react-native-windows-58e392a3-bd7d-42b1-84d3-f2a6dd66f6ff.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Rework custom components to not rely on open compose patterns", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-d8175a4a-46c8-40d0-b42b-097f50a633c1.json b/change/react-native-windows-d8175a4a-46c8-40d0-b42b-097f50a633c1.json new file mode 100644 index 00000000000..947dbc6563f --- /dev/null +++ b/change/react-native-windows-d8175a4a-46c8-40d0-b42b-097f50a633c1.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "[Fabric] Fix ScrollViewComponentView object leak", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json b/change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json new file mode 100644 index 00000000000..df261766d15 --- /dev/null +++ b/change/react-native-windows-dd026530-5f0b-4ca5-b43e-a7a6960d7696.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "[Fabric] Enable implementation of custom events on custom components", + "packageName": "react-native-windows", + "email": "30809111+acoates-ms@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/RNTesterApp-Fabric.cpp b/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/RNTesterApp-Fabric.cpp index 65328553a8e..4192aac0411 100644 --- a/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/RNTesterApp-Fabric.cpp +++ b/packages/e2e-test-app-fabric/windows/RNTesterApp-Fabric/RNTesterApp-Fabric.cpp @@ -223,6 +223,16 @@ void InsertNumberValueIfNotDefault( } } +void InsertIntValueIfNotDefault( + const winrt::Windows::Data::Json::JsonObject &obj, + winrt::hstring name, + int value, + int defaultValue = 0) { + if (value != defaultValue) { + obj.Insert(name, winrt::Windows::Data::Json::JsonValue::CreateNumberValue(value)); + } +} + void InsertBooleanValueIfNotDefault( const winrt::Windows::Data::Json::JsonObject &obj, winrt::hstring name, @@ -233,6 +243,26 @@ void InsertBooleanValueIfNotDefault( } } +void InsertLiveSettingValueIfNotDefault( + const winrt::Windows::Data::Json::JsonObject &obj, + winrt::hstring name, + LiveSetting value, + LiveSetting defaultValue = LiveSetting::Off) { + if (value != defaultValue) { + switch (value) { + case 0: + obj.Insert(name, winrt::Windows::Data::Json::JsonValue::CreateStringValue(L"Off")); + break; + case 1: + obj.Insert(name, winrt::Windows::Data::Json::JsonValue::CreateStringValue(L"Polite")); + break; + case 2: + obj.Insert(name, winrt::Windows::Data::Json::JsonValue::CreateStringValue(L"Assertive")); + break; + } + } +} + void InsertSizeValue( const winrt::Windows::Data::Json::JsonObject &obj, winrt::hstring name, @@ -288,6 +318,11 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse( BOOL isKeyboardFocusable; BSTR localizedControlType; BSTR name; + int positionInSet = 0; + int sizeOfSet = 0; + BSTR value; + BOOL isReadOnly; + LiveSetting liveSetting = LiveSetting::Off; pTarget->get_CurrentAutomationId(&automationId); pTarget->get_CurrentControlType(&controlType); @@ -296,6 +331,14 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse( pTarget->get_CurrentIsKeyboardFocusable(&isKeyboardFocusable); pTarget->get_CurrentLocalizedControlType(&localizedControlType); pTarget->get_CurrentName(&name); + IUIAutomationElement4 *pTarget4; + HRESULT hr = pTarget->QueryInterface(__uuidof(IUIAutomationElement4), reinterpret_cast(&pTarget4)); + if (SUCCEEDED(hr) && pTarget4) { + pTarget4->get_CurrentPositionInSet(&positionInSet); + pTarget4->get_CurrentSizeOfSet(&sizeOfSet); + pTarget4->get_CurrentLiveSetting(&liveSetting); + pTarget4->Release(); + } result.Insert(L"AutomationId", winrt::Windows::Data::Json::JsonValue::CreateStringValue(automationId)); result.Insert(L"ControlType", winrt::Windows::Data::Json::JsonValue::CreateNumberValue(controlType)); InsertStringValueIfNotEmpty(result, L"HelpText", helpText); @@ -304,7 +347,23 @@ winrt::Windows::Data::Json::JsonObject DumpUIATreeRecurse( result.Insert( L"LocalizedControlType", winrt::Windows::Data::Json::JsonValue::CreateStringValue(localizedControlType)); InsertStringValueIfNotEmpty(result, L"Name", name); - + InsertIntValueIfNotDefault(result, L"PositionInSet", positionInSet); + InsertIntValueIfNotDefault(result, L"SizeofSet", sizeOfSet); + InsertLiveSettingValueIfNotDefault(result, L"LiveSetting", liveSetting); + + IValueProvider *valuePattern; + hr = pTarget->GetCurrentPattern(UIA_ValuePatternId, reinterpret_cast(&valuePattern)); + if (SUCCEEDED(hr) && valuePattern) { + hr = valuePattern->get_Value(&value); + if (SUCCEEDED(hr)) { + InsertStringValueIfNotEmpty(result, L"ValuePattern.Value", value); + } + hr = valuePattern->get_IsReadOnly(&isReadOnly); + if (SUCCEEDED(hr)) { + InsertBooleanValueIfNotDefault(result, L"ValuePattern.IsReadOnly", isReadOnly, true); + } + valuePattern->Release(); + } IUIAutomationElement *pChild; IUIAutomationElement *pSibling; pWalker->GetFirstChildElement(pTarget, &pChild); diff --git a/packages/playground/windows/playground-composition/CustomComponent.cpp b/packages/playground/windows/playground-composition/CustomComponent.cpp index dac689de3dc..7e13b72f088 100644 --- a/packages/playground/windows/playground-composition/CustomComponent.cpp +++ b/packages/playground/windows/playground-composition/CustomComponent.cpp @@ -1,12 +1,9 @@ #include "pch.h" +#include #include #include #include -#include -#include - -#ifdef USE_WINUI3 #include #include #include @@ -14,9 +11,9 @@ #include #include #include -#endif +#include +#include -#include "App.CustomComponent.g.h" #include #include "YogaXamlPanel.h" @@ -47,100 +44,63 @@ struct CustomXamlComponentStateData : winrt::implements { - CustomComponent( - bool nativeLayout, - const winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs &args) - : base_type(args), - m_nativeLayout(nativeLayout), - m_compContext( - args.as() - .CompositionContext()) {} - - ~CustomComponent() { -#ifdef USE_EXPERIMENTAL_WINUI3 - m_xamlIsland.Close(); - m_siteBridge.Close(); - // Hit a crash when calling m_contentIsland.Close? - // m_contentIsland.Close(); -#endif - } +// Should be codegen'd +REACT_STRUCT(OnMyEvent) +struct OnMyEvent { + REACT_FIELD(value) + bool value; - void HandleCommand(winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) { - // Custom commands would be implemented here - base_type::HandleCommand(commandName, args); - } + REACT_FIELD(target) + int32_t target; +}; - void UpdateProps( - const winrt::Microsoft::ReactNative::IComponentProps &props, - const winrt::Microsoft::ReactNative::IComponentProps &oldProps) { - auto myProps = props.as(); -#ifdef USE_EXPERIMENTAL_WINUI3 - m_buttonLabelTextBlock.Text(myProps->label); -#endif - } - void UpdateState(const winrt::Microsoft::ReactNative::IComponentState &state) { - m_state = state; - } +// Should be codegen'd +struct CustomXamlComponentEventEmitter { + CustomXamlComponentEventEmitter(const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) + : m_eventEmitter(eventEmitter) {} - void UpdateLayoutMetrics( - const winrt::Microsoft::ReactNative::LayoutMetrics &layoutMetrics, - const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) { - base_type::UpdateLayoutMetrics(layoutMetrics, oldLayoutMetrics); -#ifdef USE_EXPERIMENTAL_WINUI3 - auto site = m_siteBridge.Site(); - auto siteWindow = site.Environment(); - auto displayScale = siteWindow.DisplayScale(); - - site.ParentScale(displayScale); - site.ActualSize({layoutMetrics.Frame.Width, layoutMetrics.Frame.Height}); - site.ClientSize(winrt::Windows::Graphics::SizeInt32{ - static_cast(layoutMetrics.Frame.Width * layoutMetrics.PointScaleFactor), - static_cast(layoutMetrics.Frame.Height * layoutMetrics.PointScaleFactor)}); -#endif + void onMyEvent(OnMyEvent &value) const { + m_eventEmitter.DispatchEvent(L"MyEvent", [value](const winrt::Microsoft::ReactNative::IJSValueWriter writer) { + winrt::Microsoft::ReactNative::WriteValue(writer, value); + }); } - void FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) { - base_type::FinalizeUpdates(updateMask); - } + private: + winrt::Microsoft::ReactNative::EventEmitter m_eventEmitter{nullptr}; +}; - winrt::Microsoft::ReactNative::Composition::Experimental::IVisual CreateInternalVisual() noexcept { +struct CustomComponentUserData : winrt::implements { + void Initialize( + const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView, + bool nativeLayout) { + nativeLayout; + islandView; #ifdef USE_EXPERIMENTAL_WINUI3 - auto systemCompContext = - m_compContext - .try_as(); - if (systemCompContext) { - m_xamlIsland = winrt::Microsoft::UI::Xaml::XamlIsland{}; - m_xamlIsland.Content(CreateXamlButtonContent()); - - m_contentIsland = m_xamlIsland.ContentIsland(); - } + m_xamlIsland = winrt::Microsoft::UI::Xaml::XamlIsland{}; + m_xamlIsland.Content(CreateXamlButtonContent(nativeLayout)); + islandView.Connect(m_xamlIsland.ContentIsland()); #endif + } - m_visual = m_compContext.CreateSpriteVisual(); - // m_visual.Brush(m_compContext.CreateColorBrush({255, 255, 0, 255})); + void PropsChanged( + const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView & /*islandView*/, + const winrt::Microsoft::ReactNative::IComponentProps &newProps, + const winrt::Microsoft::ReactNative::IComponentProps & /*oldProps*/) { + auto myProps = newProps.as(); #ifdef USE_EXPERIMENTAL_WINUI3 - - if (systemCompContext) { - auto hwnd = reinterpret_cast( - winrt::Microsoft::ReactNative::ReactCoreInjection::GetTopLevelWindowId(ReactContext().Properties())); - - auto containerVisual = - winrt::Microsoft::ReactNative::Composition::Experimental::SystemCompositionContextHelper::InnerVisual( - m_visual) - .as(); - m_siteBridge = winrt::Microsoft::UI::Content::SystemVisualSiteBridge::Create( - m_contentIsland.Compositor(), containerVisual, winrt::Microsoft::UI::GetWindowIdFromWindow(hwnd)); - m_siteBridge.Connect(m_contentIsland); - - auto rootXamlVisualSize = m_contentIsland.Root().Size(); - } + m_buttonLabelTextBlock.Text(myProps->label); #endif + } - return m_visual; + void FinalizeUpdates() noexcept { + if (m_eventEmitter) { + OnMyEvent args; + args.value = false; + m_eventEmitter->onMyEvent(args); + } } - winrt::Microsoft::UI::Xaml::UIElement CreateXamlButtonContent() { + winrt::Microsoft::UI::Xaml::UIElement CreateXamlButtonContent(bool nativeLayout) { m_buttonLabelTextBlock = winrt::Microsoft::UI::Xaml::Controls::TextBlock(); m_buttonLabelTextBlock.Text(L"This is a Xaml Button set to ellipisify on truncation"); m_buttonLabelTextBlock.TextTrimming(winrt::Microsoft::UI::Xaml::TextTrimming::CharacterEllipsis); @@ -151,7 +111,7 @@ struct CustomComponent : CustomComponentT { // If we are using native layout then wrap the element in a YogaXamlPanel which reports any changes to desired size // of the XAML element. - if (!m_nativeLayout) { + if (!nativeLayout) { return button; } @@ -177,16 +137,94 @@ struct CustomComponent : CustomComponentT { return yogaXamlPanel; } + static void ConfigureBuilderForCustomComponent( + winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder, + bool nativeLayout) { + builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props) noexcept { + return winrt::make(props); + }); + + builder.SetFinalizeUpdateHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + winrt::Microsoft::ReactNative::ComponentViewUpdateMask /*mask*/) { + auto userData = source.UserData().as(); + userData->FinalizeUpdates(); + }); + + auto compBuilder = builder.as(); + + compBuilder.SetContentIslandComponentViewInitializer( + [nativeLayout]( + const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept { + auto userData = winrt::make_self(); + userData->Initialize(islandView, nativeLayout); + islandView.UserData(*userData); + +#ifdef USE_EXPERIMENTAL_WINUI3 + islandView.Destroying([](const winrt::IInspectable &sender, const winrt::IInspectable &args) { + auto senderIslandView = sender.as(); + auto userData = senderIslandView.UserData().as(); + userData->m_xamlIsland.Close(); + }); +#endif + }); + + builder.SetUpdateEventEmitterHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + const winrt::Microsoft::ReactNative::EventEmitter &eventEmitter) { + auto senderIslandView = source.as(); + auto userData = senderIslandView.UserData().as(); + userData->m_eventEmitter = std::make_unique(eventEmitter); + }); + + if (nativeLayout) { + builder.SetMeasureContentHandler([](winrt::Microsoft::ReactNative::ShadowNode shadowNode, + winrt::Microsoft::ReactNative::LayoutContext layoutContext, + winrt::Microsoft::ReactNative::LayoutConstraints layoutContraints) noexcept { + shadowNode.Tag(winrt::box_value(layoutContraints)); + + auto currentState = winrt::get_self(shadowNode.StateData()); + + if (currentState) { + // Snap up to the nearest whole pixel to avoid pixel snapping truncations + auto size = winrt::Windows::Foundation::Size{ + std::ceil(currentState->desiredSize.Width * layoutContext.PointScaleFactor()) / + layoutContext.PointScaleFactor() + + 1.0f, + std::ceil(currentState->desiredSize.Height * layoutContext.PointScaleFactor()) / + layoutContext.PointScaleFactor() + + 1.0f, + }; + return size; + } + + return winrt::Windows::Foundation::Size{0, 0}; + }); + builder.SetInitialStateDataFactory([](const winrt::Microsoft::ReactNative::IComponentProps & /*props*/) noexcept { + return winrt::make(winrt::Windows::Foundation::Size{0, 0}); + }); + } + + builder.SetUpdatePropsHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + const winrt::Microsoft::ReactNative::IComponentProps &newProps, + const winrt::Microsoft::ReactNative::IComponentProps &oldProps) { + auto senderIslandView = source.as(); + auto userData = senderIslandView.UserData().as(); + userData->PropsChanged(senderIslandView, newProps, oldProps); + }); + + builder.SetUpdateStateHandler([](const winrt::Microsoft::ReactNative::ComponentView &source, + const winrt::Microsoft::ReactNative::IComponentState &newState) { + auto senderIslandView = source.as(); + auto userData = senderIslandView.UserData().as(); + userData->m_state = newState; + }); + } + private: - const bool m_nativeLayout; - winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext; winrt::Microsoft::UI::Xaml::Controls::TextBlock m_buttonLabelTextBlock{nullptr}; winrt::Microsoft::ReactNative::IComponentState m_state; - winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_visual; + std::unique_ptr m_eventEmitter{nullptr}; #ifdef USE_EXPERIMENTAL_WINUI3 winrt::Microsoft::UI::Xaml::XamlIsland m_xamlIsland{nullptr}; - winrt::Microsoft::UI::Content::ContentIsland m_contentIsland{nullptr}; - winrt::Microsoft::UI::Content::SystemVisualSiteBridge m_siteBridge{nullptr}; #endif }; @@ -194,55 +232,13 @@ static void RegisterViewComponent(winrt::Microsoft::ReactNative::IReactPackageBu packageBuilder.as().AddViewComponent( L"CustomXamlComponentWithNativeLayout", [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { - builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props) noexcept { - return winrt::make(props); - }); - auto compBuilder = - builder.as(); - compBuilder.SetCreateViewComponentView( - [](const winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs &args) noexcept { - return winrt::make(true, args); - }); - builder.SetMeasureContentHandler( - [](winrt::Microsoft::ReactNative::ShadowNode shadowNode, - winrt::Microsoft::ReactNative::LayoutContext layoutContext, - winrt::Microsoft::ReactNative::LayoutConstraints layoutContraints) noexcept { - shadowNode.Tag(winrt::box_value(layoutContraints)); - - auto currentState = winrt::get_self(shadowNode.StateData()); - - if (currentState) { - // Snap up to the nearest whole pixel to avoid pixel snapping truncations - auto size = winrt::Windows::Foundation::Size{ - std::ceil(currentState->desiredSize.Width * layoutContext.PointScaleFactor()) / - layoutContext.PointScaleFactor() + - 1.0f, - std::ceil(currentState->desiredSize.Height * layoutContext.PointScaleFactor()) / - layoutContext.PointScaleFactor() + - 1.0f, - }; - return size; - } - - return winrt::Windows::Foundation::Size{0, 0}; - }); - builder.SetInitialStateDataFactory([](const winrt::Microsoft::ReactNative::IComponentProps & /*props*/) { - return winrt::make(winrt::Windows::Foundation::Size{0, 0}); - }); + CustomComponentUserData::ConfigureBuilderForCustomComponent(builder, true /*nativeLayout*/); }); packageBuilder.as().AddViewComponent( L"CustomXamlComponentWithYogaLayout", [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { - builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props) noexcept { - return winrt::make(props); - }); - auto compBuilder = - builder.as(); - compBuilder.SetCreateViewComponentView( - [](const winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs &args) noexcept { - return winrt::make(false, args); - }); + CustomComponentUserData::ConfigureBuilderForCustomComponent(builder, false /*nativeLayout*/); }); } } // namespace winrt::PlaygroundApp::implementation diff --git a/packages/playground/windows/playground-composition/CustomComponent.idl b/packages/playground/windows/playground-composition/CustomComponent.idl index c64ba4fbe91..f16b6a92886 100644 --- a/packages/playground/windows/playground-composition/CustomComponent.idl +++ b/packages/playground/windows/playground-composition/CustomComponent.idl @@ -6,7 +6,8 @@ namespace PlaygroundApp [default_interface] [webhosthidden] [experimental] - runtimeclass CustomComponent : Microsoft.ReactNative.Composition.ViewComponentView, Microsoft.ReactNative.Composition.Experimental.IInternalCreateVisual + runtimeclass DrawingIsland : Windows.Foundation.IClosable { + DrawingIsland(Microsoft.UI.Composition.Compositor compositor); } } diff --git a/packages/playground/windows/playground-composition/DrawingIsland.cpp b/packages/playground/windows/playground-composition/DrawingIsland.cpp new file mode 100644 index 00000000000..d4fd5ffb69f --- /dev/null +++ b/packages/playground/windows/playground-composition/DrawingIsland.cpp @@ -0,0 +1,976 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DrawingIsland.h" + +#include "App.DrawingIsland.g.cpp" + +namespace winrt { +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::Foundation::Numerics; +using namespace winrt::Windows::System; +using namespace winrt::Windows::UI; + +using namespace winrt::Microsoft::UI; +using namespace winrt::Microsoft::UI::Composition; +using namespace winrt::Microsoft::UI::Composition::SystemBackdrops; +using namespace winrt::Microsoft::UI::Content; +using namespace winrt::Microsoft::UI::Input; +} // namespace winrt + +namespace winrt::PlaygroundApp::implementation { + +// std::map> g_visualToFragmentMap; + +DrawingIsland::DrawingIsland(const winrt::Microsoft::UI::Composition::Compositor &compositor) { + m_compositor = compositor; + + // Create the Compositor and the Content: + // - The Bridge's connection to the Window will keep everything alive, and perform an orderly + // tear-down: + // + // Window -> Bridge -> Content -> Visual -> InputSite -> InputObject + // + // - When the ContentIsland is destroyed, ContentIsland.AppData will call IClosable.Close on + // this instance to break cycles and clean up expensive resources. + + m_backgroundVisual = m_compositor.CreateSpriteVisual(); + m_backgroundVisual.RelativeSizeAdjustment(winrt::float2(1.0f, 1.0f)); + + m_island = winrt::ContentIsland::Create(m_backgroundVisual); + m_island.AppData(get_strong().as()); + + Output_Initialize(); + Input_Initialize(); + Window_Initialize(); + m_prevRasterizationScale = m_island.RasterizationScale(); + + // Get notifications for resize, bridge changes, etc. + (void)m_island.StateChanged([&](auto &&...) { return Island_OnStateChanged(); }); + + // Just adding EnqueueFromBackgroundThread method for testing. + EnqueueFromBackgroundThread(); + + // Get notifications for island disconnection. +#ifdef USE_EXPERIMENTAL_WINUI3 + (void)m_island.Connected([&](auto &&...) { return Island_OnConnected(); }); + + (void)m_island.Disconnected([&](auto &&...) { return Island_OnDisconnected(); }); +#endif + (void)m_island.Closed([&]() { return Island_OnClosed(); }); +} + +DrawingIsland::~DrawingIsland() { + m_siteBridge = nullptr; +#ifdef USE_EXPERIMENTAL_WINUI3 + m_popupSiteBridge = nullptr; +#endif + // m_fragmentRoot = nullptr; + m_compositor = nullptr; +} + +// IClosable methods +void DrawingIsland::Close() { + WCHAR msg2[300]; + StringCbPrintf(msg2, sizeof(msg2), L"DrawingIsland::Close \n"); + OutputDebugStringW(msg2); + m_visuals = nullptr; + m_selectedVisual = nullptr; + m_backgroundBrushDefault = nullptr; + m_backgroundBrushA = nullptr; + m_backgroundBrushB = nullptr; + m_backgroundBrushC = nullptr; + m_backgroundVisual = nullptr; + + // Destroy SystemBackdropController objects. + if (m_backdropController != nullptr) { + if (m_backdropConfiguration != nullptr) { + // m_backdropConfiguration is only initialized for the DrawingIsland that owns the system backdrop controller. + m_backdropController.Close(); + m_backdropController = nullptr; + m_backdropConfiguration = nullptr; + } else { + // We're closing a DrawingIsland in a popup, not the owner DrawingIsland of the system backdrop controller. + // Remove the current target from the system backdrop controller. + m_backdropController.RemoveSystemBackdropTarget(m_backdropTarget); + } + } + + // Destroy Content: + // - This handles if the ContentIsland has already started destruction and is notifying the app. + m_island.Close(); + m_island = nullptr; + + // TODO: Add the following test case in automated tests: + // 1. We are recursively calling ContentIsland.Close while inside the ContentIsland.Closed + // event. + // 2. We are testing the potential final Release() of ContentIsland while inside the Closed + // event. +} + +// Properties +boolean DrawingIsland::UseSystemBackdrop() { + return m_useSystemBackdrop; +} + +void DrawingIsland::UseSystemBackdrop(boolean value) { + if (m_useSystemBackdrop != value) { + m_useSystemBackdrop = value; + + EvaluateUseSystemBackdrop(); + } +} + +boolean DrawingIsland::InputActivatePopup() { + return m_inputActivatePopup; +} + +void DrawingIsland::InputActivatePopup(boolean value) { + if (m_inputActivatePopup != value) { + m_inputActivatePopup = value; + + EvaluateInputActivatePopup(); + } +} + +boolean DrawingIsland::LightDismissPopup() { + return m_lightDismissPopup; +} + +void DrawingIsland::LightDismissPopup(boolean value) { + if (m_lightDismissPopup != value) { + m_lightDismissPopup = value; + + EvaluateLightDismissPopup(); + } +} + +boolean DrawingIsland::IgnoreLeftButtonPressed() { + return m_ignoreLeftButtonPressed; +} + +void DrawingIsland::IgnoreLeftButtonPressed(boolean value) { + if (m_ignoreLeftButtonPressed != value) { + m_ignoreLeftButtonPressed = value; + } +} + +winrt::ContentIsland DrawingIsland::Island() const { + return m_island; +} + +// Methods + +void DrawingIsland::LeftClickAndRelease(const winrt::float2 currentPoint) { + OnLeftClick(currentPoint, false); + Input_OnPointerReleased(); +} + +void DrawingIsland::RightClickAndRelease(const winrt::float2 currentPoint) { + OnRightClick(currentPoint); + Input_OnPointerReleased(); +} + +void DrawingIsland::SetHostBridge(const winrt::IContentSiteBridge &bridge) { + m_siteBridge = bridge; +#ifdef USE_EXPERIMENTAL_WINUI3 + m_popupSiteBridge = m_siteBridge.try_as(); +#endif + Accessibility_Initialize(); + SystemBackdrop_Initialize(); + EvaluateLightDismissPopup(); + EvaluateInputActivatePopup(); +} + +void DrawingIsland::InitializeForCrossProc() { + m_crossProcUIA = true; + Accessibility_Initialize(); + SystemBackdrop_Initialize(); +} + +void DrawingIsland::SetSystemBackdropController(const winrt::ISystemBackdropControllerWithTargets &backdropController) { + m_backdropController = backdropController; +} + +void DrawingIsland::SetBackroundOpacity(float backgroundOpacity) { + m_backgroundOpacity = backgroundOpacity; +} + +void DrawingIsland::SetColorIndex(std::uint32_t colorIndex) { + m_currentColorIndex = std::clamp(colorIndex, 0, _countof(s_colors) - 1); + if (m_currentColorIndex >= 4) { + m_backgroundBrushDefault = m_compositor.CreateColorBrush(s_colors[m_currentColorIndex]); + + m_backgroundVisual.Brush(m_backgroundBrushDefault); + } + Output_UpdateCurrentColorVisual(); +} + +/* +// IInputKeyboardSourcePreTranslateHandler methods +IFACEMETHOD(DrawingIsland::OnDirectMessage) +(IInputPreTranslateKeyboardSourceInterop *source, const MSG *msg, UINT keyboardModifiers, _Inout_ bool *handled) { + *handled = false; + + // Alt+A Debug print to see the order of the PreTranslate callbacks + if ((keyboardModifiers & FALT) && (msg->message == WM_SYSKEYDOWN) && + (static_cast(msg->wParam) == winrt::VirtualKey::A)) { + WCHAR msg2[300]; + StringCbPrintf(msg2, sizeof(msg2), L"OnDirectMessage called on ContentIslandId=%d\n", m_island.Id()); + OutputDebugStringW(msg2); + } + + if (keyboardModifiers & FALT) { + *handled = Input_AcceleratorKeyActivated(static_cast(msg->wParam)); + } + + return S_OK; +} + +IFACEMETHOD(DrawingIsland::OnTreeMessage) +(IInputPreTranslateKeyboardSourceInterop *source, const MSG *msg, UINT keyboardModifiers, _Inout_ bool *handled) { + // Alt+A Debug print to see the order of the PreTranslate callbacks + if ((keyboardModifiers & FALT) && (msg->message == WM_SYSKEYDOWN) && + (static_cast(msg->wParam) == winrt::VirtualKey::A)) { + WCHAR msg2[300]; + StringCbPrintf(msg2, sizeof(msg2), L"OnTreeMessage called on ContentIslandId=%d\n", m_island.Id()); + OutputDebugStringW(msg2); + } + + *handled = false; + return S_OK; +} +*/ + +void DrawingIsland::Accessibility_Initialize() { + /* + m_fragmentRoot = winrt::make_self(m_island); + + if (m_crossProcUIA) { + m_fragmentRoot->SetName(L"CrossProc Island"); + } else if (m_siteBridge != nullptr) { + auto desktopChildBridge = m_siteBridge.try_as(); + if (desktopChildBridge != nullptr) { + m_fragmentRoot->SetName(L"Child Island"); + } else { + m_fragmentRoot->SetName(L"Popup Island"); + } + } + + (void)m_island.AutomationProviderRequested({this, &DrawingIsland::Accessibility_OnAutomationProviderRequested}); + */ +} + +/* +void DrawingIsland::Accessibility_OnAutomationProviderRequested( + const winrt::ContentIsland &island, + const winrt::ContentIslandAutomationProviderRequestedEventArgs &args) { +IInspectable providerAsIInspectable; +m_fragmentRoot->QueryInterface(winrt::guid_of(), winrt::put_abi(providerAsIInspectable)); +args.AutomationProvider(std::move(providerAsIInspectable)); + +args.Handled(true); +} +*/ + +void DrawingIsland::CreateUIAProviderForVisual() { + /* + winrt::com_ptr fragment = + m_fragmentFactory.Create(s_colorNames[m_currentColorIndex].c_str(), m_fragmentRoot); + + g_visualToFragmentMap[m_selectedVisual] = fragment; + + fragment->SetVisual(m_selectedVisual); + // Set up children roots + m_fragmentRoot->AddChild(fragment); + + // Finally set up parent chain + fragment->SetParent(m_fragmentRoot); + */ +} + +void DrawingIsland::EnqueueFromBackgroundThread() { + std::thread updateThread1{[&]() { + winrt::Microsoft::UI::Dispatching::DispatcherQueue dispatcherQueue = m_island.DispatcherQueue(); + + bool result = dispatcherQueue.TryEnqueue(winrt::Microsoft::UI::Dispatching::DispatcherQueuePriority::High, [&] { + WCHAR msg2[300]; + StringCbPrintf( + msg2, sizeof(msg2), L"Enqueued for dispatching on background thread ContentIslandId=%d\n", m_island.Id()); + OutputDebugStringW(msg2); + winrt::Windows::Foundation::Numerics::float2 size = {500, 500}; + m_island.RequestSize(size); + }); + + if (!result) { + WCHAR msg2[300]; + StringCbPrintf( + msg2, + sizeof(msg2), + L"This means the dispatcherQueue has shutdown already and the message can't be enqueued.\n"); + OutputDebugStringW(msg2); + } + }}; + updateThread1.join(); +} + +void DrawingIsland::EvaluateUseSystemBackdrop() { + BYTE backgroundBrushOpacity = 0xFF; + // If we use system backdrops, it will be behind our background visual. Make sure we can + // see through the background visual to reveal the system backdrop. + // reveal the system backdrop underneath. + if (m_useSystemBackdrop) { + backgroundBrushOpacity = 0x30; + } + + // Create the background parent Visual that the individual square will be added into. + m_backgroundBrushDefault = m_compositor.CreateColorBrush(winrt::Color{backgroundBrushOpacity, 0x20, 0x20, 0x20}); + m_backgroundBrushA = m_compositor.CreateColorBrush(winrt::Color{backgroundBrushOpacity, 0x99, 0x20, 0x20}); + m_backgroundBrushB = m_compositor.CreateColorBrush(winrt::Color{backgroundBrushOpacity, 0x20, 0x99, 0x20}); + m_backgroundBrushC = m_compositor.CreateColorBrush(winrt::Color{backgroundBrushOpacity, 0x20, 0x20, 0x99}); + + m_backgroundVisual.Brush(m_backgroundBrushDefault); +} + +void DrawingIsland::EvaluateInputActivatePopup() { + // This policy only applies to popups, check if we are a popup before proceeding. + if (!IsHostedByPopupWindowSiteBridge()) + return; + + /* + if (m_inputActivatePopup) { + m_pointerSource.ActivationBehavior(winrt::Microsoft::UI::Input::InputPointerActivationBehavior::Activate); + } else { + m_pointerSource.ActivationBehavior(winrt::InputPointerActivationBehavior::NoActivate); + } + */ +} + +void DrawingIsland::EvaluateLightDismissPopup() { + /* + // This policy only applies to popups, check if we are a popup before proceeding. + if (!IsHostedByPopupWindowSiteBridge()) + return; + + if (m_lightDismissPopup) { + m_lightDismissAction = winrt::InputLightDismissAction::GetForIsland(m_island); + m_lightDismissAction.Dismissed( + [this](winrt::InputLightDismissAction const &, winrt::InputLightDismissEventArgs const & args) { + m_popupSiteBridge.Hide(); + m_popupSiteBridge.Close(); + + m_siteBridge = nullptr; + m_popupSiteBridge = nullptr; + }); + } else { + m_lightDismissAction = nullptr; + } + */ +} + +winrt::Visual DrawingIsland::HitTestVisual(winrt::float2 const point) { + winrt::Visual selectedVisual{nullptr}; + for (winrt::Visual visual : m_visuals) { + winrt::float3 const offset = visual.Offset(); + winrt::float2 const size = visual.Size(); + + if (point.x >= offset.x && point.x < offset.x + size.x && point.y >= offset.y && point.y < offset.y + size.y) { + selectedVisual = visual; + } + } + + return selectedVisual; +} + +void DrawingIsland::Input_Initialize() { + m_pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(m_island); + + m_pointerSource.PointerPressed( + [this](winrt::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + auto currentPoint = args.CurrentPoint(); + auto properties = currentPoint.Properties(); + + if (properties.IsLeftButtonPressed()) { + Input_OnLeftButtonPressed(args); + } else if (properties.IsRightButtonPressed()) { + Input_OnRightButtonPressed(args); + } + }); + + m_pointerSource.PointerMoved( + [this](winrt::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { + Input_OnPointerMoved(args); + }); + + m_pointerSource.PointerReleased([&](auto &&...) { Input_OnPointerReleased(); }); + + // Set up the keyboard source. We store this in a member variable so we can easily call + // TrySetFocus() in response to left clicks in the content later on. + m_keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(m_island); + + m_keyboardSource.KeyDown([this]( + winrt::Microsoft::UI::Input::InputKeyboardSource const &, + winrt::Microsoft::UI::Input::KeyEventArgs const &args) { + bool handled = Input_OnKeyDown(args.VirtualKey()); + + // Mark the event as handled + if (handled) { + args.Handled(true); + } + }); + + /* + m_pretranslateSource = winrt::InputPreTranslateKeyboardSource::GetForIsland(m_island); + + m_pretranslateSource.as()->SetPreTranslateHandler( + this); + */ + + auto activationListener = winrt::Microsoft::UI::Input::InputActivationListener::GetForIsland(m_island); + (void)activationListener.InputActivationChanged( + [this, activationListener]( + winrt::InputActivationListener const &, + winrt::Microsoft::UI::Input::InputActivationListenerActivationChangedEventArgs const &) { + switch (activationListener.State()) { + case winrt::InputActivationState::Activated: + m_backgroundVisual.Opacity(1.0f); + break; + + default: + m_backgroundVisual.Opacity(m_backgroundOpacity); + break; + } + }); + + m_focusController = winrt::Microsoft::UI::Input::InputFocusController::GetForIsland(m_island); + m_focusController.NavigateFocusRequested( + [this]( + winrt::Microsoft::UI::Input::InputFocusController const &, + winrt::Microsoft::UI::Input::FocusNavigationRequestEventArgs const &args) { + bool setFocus = m_focusController.TrySetFocus(); + // Mark the event as handled + if (setFocus) { + args.Result(winrt::FocusNavigationResult::Moved); + } + }); +} + +bool DrawingIsland::IsHostedByPopupWindowSiteBridge() { +#ifdef USE_EXPERIMENTAL_WINUI3 + return (m_popupSiteBridge != nullptr); +#else + return false; +#endif +} + +bool DrawingIsland::Input_OnKeyDown(winrt::Windows::System::VirtualKey virtualKey) { + bool handled = false; + + switch (virtualKey) { + case winrt::VirtualKey::A: { + m_backgroundVisual.Brush(m_backgroundBrushA); + handled = true; + break; + } + + case winrt::VirtualKey::B: { + m_backgroundVisual.Brush(m_backgroundBrushB); + handled = true; + break; + } + + case winrt::VirtualKey::C: { + m_backgroundVisual.Brush(m_backgroundBrushC); + handled = true; + break; + } + + case winrt::VirtualKey::Space: { + m_backgroundVisual.Brush(m_backgroundBrushDefault); + break; + } + + case winrt::Windows::System::VirtualKey::Number1: { + m_currentColorIndex = 0; + handled = true; + break; + } + + case winrt::Windows::System::VirtualKey::Number2: { + m_currentColorIndex = 1; + handled = true; + break; + } + + case winrt::Windows::System::VirtualKey::Number3: { + m_currentColorIndex = 2; + handled = true; + break; + } + + case winrt::Windows::System::VirtualKey::Number4: { + m_currentColorIndex = 3; + handled = true; + break; + } + + case winrt::Windows::System::VirtualKey::Delete: + case winrt::Windows::System::VirtualKey::Escape: { + m_visuals.RemoveAll(); + handled = true; + break; + } + + case winrt::Windows::System::VirtualKey::Tab: { + auto request = winrt::Microsoft::UI::Input::FocusNavigationRequest::Create( + winrt::Microsoft::UI::Input::FocusNavigationReason::First); + m_focusController.DepartFocus(request); + } + } + + Output_UpdateCurrentColorVisual(); + + return handled; +} + +bool DrawingIsland::Input_AcceleratorKeyActivated(winrt::Windows::System::VirtualKey virtualKey) { + bool handled = false; + + switch (virtualKey) { + case winrt::VirtualKey::X: { + m_backgroundVisual.Brush(m_backgroundBrushA); + handled = true; + break; + } + + case winrt::VirtualKey::Y: { + m_backgroundVisual.Brush(m_backgroundBrushB); + handled = true; + break; + } + + case winrt::VirtualKey::Z: { + m_backgroundVisual.Brush(m_backgroundBrushC); + handled = true; + break; + } + } + + return handled; +} + +void DrawingIsland::Input_OnLeftButtonPressed(const winrt::Microsoft::UI::Input::PointerEventArgs &args) { + if (!m_ignoreLeftButtonPressed) { + // Left button manipulates the custom-drawn content + winrt::float2 const point = args.CurrentPoint().Position(); + + auto keyModifiers = args.KeyModifiers(); + bool controlPressed = + ((keyModifiers & winrt::Windows::System::VirtualKeyModifiers::Control) == + winrt::Windows::System::VirtualKeyModifiers::Control); + + OnLeftClick(point, controlPressed); + } +} + +void DrawingIsland::Input_OnRightButtonPressed(const winrt::Microsoft::UI::Input::PointerEventArgs &args) { + OnRightClick(args.CurrentPoint().Position()); +} + +void DrawingIsland::Input_OnPointerMoved(const winrt::Microsoft::UI::Input::PointerEventArgs &args) { + if (m_selectedVisual) { + winrt::float2 const point = args.CurrentPoint().Position(); + m_selectedVisual.Offset({point.x + m_offset.x, point.y + m_offset.y, 0.0f}); + } +} + +void DrawingIsland::Input_OnPointerReleased() { + m_selectedVisual = nullptr; +} + +void DrawingIsland::Island_OnStateChanged() { + if (m_prevRasterizationScale != m_island.RasterizationScale()) { + WCHAR msg2[300]; + StringCbPrintf( + msg2, + sizeof(msg2), + L"ContentIsland Id= %d, \n RasterizationScale = %f, ActualSize = {%f, %f} \n", + m_island.Id(), + m_island.RasterizationScale(), + m_island.ActualSize().x, + m_island.ActualSize().y); + OutputDebugStringW(msg2); + m_prevRasterizationScale = m_island.RasterizationScale(); + } + + if (m_prevLayout != m_island.LayoutDirection()) { + SetLayoutDirectionForVisuals(); + } + + Output_UpdateCurrentColorVisual(); +} + +void DrawingIsland::Island_OnConnected() { + SetLayoutDirectionForVisuals(); +} + +void DrawingIsland::Island_OnDisconnected() { + WCHAR msg2[300]; + StringCbPrintf(msg2, sizeof(msg2), L"Island_OnDisconnected Disconnected \n"); + OutputDebugStringW(msg2); +} + +void DrawingIsland::Island_OnClosed() { + WCHAR msg2[300]; + StringCbPrintf(msg2, sizeof(msg2), L"Island_OnClosed %d\n", m_island.IsClosed()); + OutputDebugStringW(msg2); +} + +void DrawingIsland::OnLeftClick(const winrt::float2 point, bool controlPressed) { + m_selectedVisual = HitTestVisual(point); + + if (m_selectedVisual) { + winrt::float3 const offset = m_selectedVisual.Offset(); + + m_offset.x = offset.x - point.x; + m_offset.y = offset.y - point.y; + + m_visuals.Remove(m_selectedVisual); + m_visuals.InsertAtTop(m_selectedVisual); + + // TODO: The m_fragmentRoots child should be removed and added to the end as well. + } else { + Output_AddVisual(point, controlPressed); + } + + // Only transfer focus when we are hosted inside a DesktopChildSiteBridge. When we are + // hosted inside a PopupWindowSiteBridge, we expect to control focus and activaton by + // setting InputPointerSource->ActivationBehavior. + // auto desktopChildBridge = m_siteBridge.try_as(); + // if (desktopChildBridge != nullptr) { + m_focusController.TrySetFocus(); + //} +} + +void DrawingIsland::OnRightClick(const winrt::float2 point) { + winrt::Visual selectedVisual = HitTestVisual(point); + + if (selectedVisual != nullptr) { + // Right button brings up a context menu if clicked on a visual +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_siteBridge != nullptr) { + // Create a new popup window. + auto desktopBridge2 = m_siteBridge.try_as(); + auto popupBridge = desktopBridge2.TryCreatePopupSiteBridge(); + + // Convert the current position to screen coordinates and display. + winrt::Rect initialPosition(point.x, point.y, 200, 300); + + auto convertedPosition = m_island.CoordinateConverter().ConvertLocalToScreen(initialPosition); + popupBridge.MoveAndResize(convertedPosition); + + // Put a new instance of the content and connect it with the popup bridge. + auto popupContent = winrt::make_self(m_compositor); + + // Transfer policies to the popup content. + popupContent->InputActivatePopup(m_inputActivatePopup); + popupContent->LightDismissPopup(m_lightDismissPopup); + popupContent->UseSystemBackdrop(m_useSystemBackdrop); + + // Funnel through the existing backdrop controller, we do not want to create a new one for every popup. + popupContent->SetSystemBackdropController(m_backdropController); + + // Connect the content and input site to the DesktopWindowDrawingIslandBridge + popupBridge.Connect(popupContent->Island()); + + // https://task.ms/32440118: We will not have to do this once we have + // ContentIsland.SiteBridge available, since then the ContentIsland will be able to call + // SiteBridge.TryCreatePopupSiteBridge by itself. + auto bridgeInterface = popupBridge.as(); + popupContent->SetHostBridge(bridgeInterface); + + popupBridge.Show(); + + if (m_inputActivatePopup) { + // We are using the InputFocusController as a temporary workaround until we have + // support on the "hosting" side for transfering activation/focus to a popup. + + auto focusController = winrt::InputFocusController::GetForIsland(m_island); + focusController.TrySetFocus(); + } + + /* + auto parentFragment = g_visualToFragmentMap[selectedVisual]; + auto childFragment = popupContent->m_fragmentRoot; + + parentFragment->AddChild(childFragment); + + // Finally set up parent chain + childFragment->SetParent(parentFragment); + */ + } +#endif + } +} + +void DrawingIsland::Output_Initialize() { + for (int i = 0; i < _countof(m_colorBrushes); i++) { + m_colorBrushes[i] = m_compositor.CreateColorBrush(s_colors[i]); + + winrt::Color halfTransparent = s_colors[i]; + halfTransparent.A = 0x80; + m_halfTransparentColorBrushes[i] = m_compositor.CreateColorBrush(halfTransparent); + } + + m_currentColorVisual = m_compositor.CreateSpriteVisual(); + m_currentColorVisual.Offset({0.0f, 0.0f, 0.0f}); + m_backgroundVisual.Children().InsertAtTop(m_currentColorVisual); + + winrt::ContainerVisual drawingVisualsRoot = m_compositor.CreateContainerVisual(); + m_visuals = drawingVisualsRoot.Children(); + m_backgroundVisual.Children().InsertAtTop(drawingVisualsRoot); + + EvaluateUseSystemBackdrop(); + + Output_UpdateCurrentColorVisual(); +} + +void DrawingIsland::Output_AddVisual(const winrt::float2 point, bool halfTransparent) { + winrt::SpriteVisual visual = m_compositor.CreateSpriteVisual(); + visual.Brush( + halfTransparent ? m_halfTransparentColorBrushes[m_currentColorIndex] : m_colorBrushes[m_currentColorIndex]); + + float const BlockSize = 30.0f; + visual.Size({BlockSize, BlockSize}); + visual.Offset({point.x - BlockSize / 2.0f, point.y - BlockSize / 2.0f, 0.0f}); + + m_visuals.InsertAtTop(visual); + + m_selectedVisual = visual; + m_offset.x = -BlockSize / 2.0f; + m_offset.y = -BlockSize / 2.0f; + + // **FIX**: SystemVisuals currently crash configuring UIA + if (m_siteBridge != nullptr || m_crossProcUIA) { + CreateUIAProviderForVisual(); + } +} + +void DrawingIsland::Output_UpdateCurrentColorVisual() { + m_currentColorVisual.Brush(m_colorBrushes[m_currentColorIndex]); + m_currentColorVisual.Offset({0.0f, m_island.ActualSize().y - 25.0f, 0.0f}); + m_currentColorVisual.Size({m_island.ActualSize().x, 25.0f}); +} + +void DrawingIsland::SystemBackdrop_Initialize() { + // Don't initilize system backdrop if we haven't been configured for it. + if (!m_useSystemBackdrop) + return; + + if (m_backdropController == nullptr) { + m_backdropConfiguration = winrt::SystemBackdropConfiguration(); + m_backdropController = winrt::DesktopAcrylicController(); + m_backdropController.SetSystemBackdropConfiguration(m_backdropConfiguration); + + auto activationListener = winrt::InputActivationListener::GetForIsland(m_island); + (void)activationListener.InputActivationChanged( + [this, activationListener]( + winrt::InputActivationListener const &, winrt::InputActivationListenerActivationChangedEventArgs const &) { + switch (activationListener.State()) { + case winrt::InputActivationState::Activated: + m_backdropConfiguration.IsInputActive(true); + break; + + default: + m_backdropConfiguration.IsInputActive(false); + break; + } + }); + } + +#ifdef USE_EXPERIMENTAL_WINUI3 + if (IsHostedByPopupWindowSiteBridge()) { + // For popups, we want to draw shadows around the edges, so clip the backdrop visual to + // allow room on the edges for the shadows. + m_backdropLink = winrt::ContentExternalBackdropLink::Create(m_compositor); + + // This will be the size of the "cut out" we will make in the lifted composition surface + // so that the Backdrop System Sprite Visual will show through. This is specified in + // logical coordinates. + m_backdropLink.PlacementVisual().Size(m_island.ActualSize()); + + // Clip the backdrop. + m_backdropClip = m_compositor.CreateRectangleClip( + 10.0f, + 10.0f, + m_island.ActualSize().x - 10.0f, + m_island.ActualSize().y - 10.0f, + {10.0f, 10.0f}, + {10.0f, 10.0f}, + {10.0f, 10.0f}, + {10.0f, 10.0f}); + m_backdropLink.PlacementVisual().Clip(m_backdropClip); + + // Clip the overall background. + m_backgroundClip = m_compositor.CreateRectangleClip( + 0.0f, + 0.0f, + m_island.ActualSize().x, + m_island.ActualSize().y, + {10.0f, 10.0f}, + {10.0f, 10.0f}, + {10.0f, 10.0f}, + {10.0f, 10.0f}); + m_backgroundVisual.Clip(m_backgroundClip); + + // Add the backdropLink into the LiftedVisual tree of the popup. + m_backgroundVisual.Children().InsertAtBottom(m_backdropLink.PlacementVisual()); + + auto animation = m_compositor.CreateVector3KeyFrameAnimation(); + animation.InsertKeyFrame(0.0f, {0.0f, -m_island.ActualSize().y, 0.0f}); + animation.InsertKeyFrame(1.0f, {0.0f, 0.0f, 0.0f}); + animation.Duration(std::chrono::milliseconds(2000)); + animation.IterationBehavior(AnimationIterationBehavior::Count); + animation.IterationCount(1); + m_backgroundVisual.StartAnimation(L"Offset", animation); + + // For Popups, we want to customize the clip and offset of the system backdrop, so we + // pass the ContentExternalBackdropLink as the target to the BackdropController. + + m_backdropTarget = m_backdropLink; + } else +#endif + { + // If we are the main content, we don't want to add custom clips or offsets to our + // backdrop, so we can pass the ContentIsland as the target to the BackdropController. + // This will by default fill the entire ContentIsland backdrop surface. + + m_backdropTarget = m_island; + } + + m_backdropController.AddSystemBackdropTarget(m_backdropTarget); +} + +void DrawingIsland::SetLayoutDirectionForVisuals() { + if (m_island.LayoutDirection() == ContentLayoutDirection::RightToLeft) { + // The following will mirror the visuals. If any text is inside the visuals the text + // is also mirrored. The text will need another RelativeOffsetAdjustment and Scale to + // return to the normal space. + m_backgroundVisual.RelativeOffsetAdjustment(winrt::float3(1, 0, 0)); + m_backgroundVisual.Scale(winrt::float3(-1, 1, 1)); + } else { + m_backgroundVisual.RelativeOffsetAdjustment(winrt::float3(0, 0, 0)); + m_backgroundVisual.Scale(winrt::float3(1, 1, 1)); + } + m_prevLayout = m_island.LayoutDirection(); +} + +void DrawingIsland::Window_Initialize() { + auto window = m_island.Environment(); + + (void)window.SettingChanged( + [this](winrt::ContentIslandEnvironment const &, winrt::ContentEnvironmentSettingChangedEventArgs const &args) { + return Window_OnSettingChanged(args); + }); + +#ifdef USE_EXPERIMENTAL_WINUI3 + (void)window.ThemeChanged( + [this](winrt::ContentIslandEnvironment const &, winrt::IInspectable const &) { return Window_OnThemeChanged(); }); +#endif + + (void)window.StateChanged([this](winrt::ContentIslandEnvironment const &sender, winrt::IInspectable const &) { + return Window_OnStateChanged(sender); + }); +} + +void DrawingIsland::Window_OnSettingChanged(const winrt::ContentEnvironmentSettingChangedEventArgs &args) { + auto settingChanged = args.SettingName(); + + if (settingChanged == L"intl") { + m_backgroundVisual.Brush(m_backgroundBrushA); + } +} + +void DrawingIsland::Window_OnThemeChanged() { + // Do nothing intentionally - For testing purposes only +} + +void DrawingIsland::Window_OnStateChanged(winrt::ContentIslandEnvironment const &sender) { + sender; +#ifdef USE_EXPERIMENTAL_WINUI3 + WCHAR msg[300]; + winrt::Microsoft::UI::DisplayId displayId = sender.DisplayId(); + float scale = sender.DisplayScale(); + winrt::Microsoft::UI::Content::ContentDisplayOrientations nativeOrientation = sender.NativeOrientation(); + winrt::Microsoft::UI::Content::ContentDisplayOrientations currentOrientation = sender.CurrentOrientation(); + HWND hwnd = winrt::GetWindowFromWindowId(sender.AppWindowId()); + RECT rect; + GetWindowRect(hwnd, &rect); + StringCbPrintf( + msg, + sizeof(msg), + L"AppWindow Hwnd = %x, Rect.top = %d, Rect.right = %d, Rect.bottom = %d, Rect.left = %d, DisplayId: %p, DisplayScale: %f, NativeOrientation: %d, CurrentOrientation: %d\n", + hwnd, + rect.top, + rect.right, + rect.bottom, + rect.left, + displayId.Value, + scale, + nativeOrientation, + currentOrientation); + OutputDebugStringW(msg); +#endif +} + +REACT_STRUCT(DrawingIslandComponentProps) +struct DrawingIslandComponentProps + : winrt::implements { + DrawingIslandComponentProps(winrt::Microsoft::ReactNative::ViewProps props) : m_props(props) {} + + void SetProp(uint32_t hash, winrt::hstring propName, winrt::Microsoft::ReactNative::IJSValueReader value) noexcept { + winrt::Microsoft::ReactNative::ReadProp(hash, propName, value, *this); + } + + private: + winrt::Microsoft::ReactNative::ViewProps m_props; +}; + +} // namespace winrt::PlaygroundApp::implementation + +void RegisterDrawingIslandComponentView(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) { + packageBuilder.as().AddViewComponent( + L"CustomXamlComponentWithYogaLayout", + [](winrt::Microsoft::ReactNative::IReactViewComponentBuilder const &builder) noexcept { + builder.SetCreateProps([](winrt::Microsoft::ReactNative::ViewProps props) noexcept { + return winrt::make(props); + }); + auto compBuilder = + builder.as(); + compBuilder.SetContentIslandComponentViewInitializer( + [](const winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView &islandView) noexcept { + auto drawingIsland = + winrt::make(islandView.Compositor()); + islandView.UserData(drawingIsland); + islandView.Connect( + winrt::get_self(drawingIsland)->Island()); + islandView.Destroying([](const winrt::IInspectable &, const winrt::IInspectable &args) { + auto view = args.as(); + auto drawingIsland = view.UserData().as(); + drawingIsland->Close(); + view.UserData(nullptr); + }); + }); + }); +} diff --git a/packages/playground/windows/playground-composition/DrawingIsland.h b/packages/playground/windows/playground-composition/DrawingIsland.h new file mode 100644 index 00000000000..3135497f4cc --- /dev/null +++ b/packages/playground/windows/playground-composition/DrawingIsland.h @@ -0,0 +1,250 @@ +#include "pch.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "App.DrawingIsland.g.h" +#include + +namespace winrt { +using namespace winrt::Windows::Foundation; +using namespace winrt::Windows::Foundation::Collections; +using namespace winrt::Windows::Foundation::Numerics; +using namespace winrt::Windows::System; +using namespace winrt::Windows::UI; + +using namespace winrt::Microsoft::UI; +using namespace winrt::Microsoft::UI::Composition; +using namespace winrt::Microsoft::UI::Composition::SystemBackdrops; +using namespace winrt::Microsoft::UI::Content; +using namespace winrt::Microsoft::UI::Input; +} // namespace winrt + +namespace winrt::PlaygroundApp::implementation { + +// std::map> g_visualToFragmentMap; + +struct DrawingIsland + : public DrawingIslandT { + public: + DrawingIsland(const winrt::Microsoft::UI::Composition::Compositor &compositor); + + ~DrawingIsland(); + + // IClosable methods + void Close(); + + // Properties + boolean UseSystemBackdrop(); + + void UseSystemBackdrop(boolean value); + + boolean InputActivatePopup(); + + void InputActivatePopup(boolean value); + + boolean LightDismissPopup(); + + void LightDismissPopup(boolean value); + + boolean IgnoreLeftButtonPressed(); + + void IgnoreLeftButtonPressed(boolean value); + + winrt::ContentIsland Island() const; + + // Methods + + void LeftClickAndRelease(const winrt::float2 currentPoint); + + void RightClickAndRelease(const winrt::float2 currentPoint); + + void SetHostBridge(const winrt::IContentSiteBridge &bridge); + + void InitializeForCrossProc(); + + void SetSystemBackdropController(const winrt::ISystemBackdropControllerWithTargets &backdropController); + + void SetBackroundOpacity(float backgroundOpacity); + + void SetColorIndex(std::uint32_t colorIndex); + + /* + // IInputKeyboardSourcePreTranslateHandler methods + IFACEMETHOD(OnDirectMessage) + (IInputPreTranslateKeyboardSourceInterop *source, const MSG *msg, UINT keyboardModifiers, _Inout_ bool *handled); + + IFACEMETHOD(OnTreeMessage) + (IInputPreTranslateKeyboardSourceInterop *source, const MSG *msg, UINT keyboardModifiers, _Inout_ bool *handled); + */ + + private: + void Accessibility_Initialize(); + + /* +void Accessibility_OnAutomationProviderRequested( + const winrt::ContentIsland &island, + const winrt::ContentIslandAutomationProviderRequestedEventArgs &args); +*/ + + void CreateUIAProviderForVisual(); + + void EnqueueFromBackgroundThread(); + + void EvaluateUseSystemBackdrop(); + + void EvaluateInputActivatePopup(); + + void EvaluateLightDismissPopup(); + + winrt::Visual HitTestVisual(winrt::float2 const point); + + void Input_Initialize(); + + bool IsHostedByPopupWindowSiteBridge(); + + bool Input_OnKeyDown(winrt::Windows::System::VirtualKey virtualKey); + + bool Input_AcceleratorKeyActivated(winrt::Windows::System::VirtualKey virtualKey); + + void Input_OnLeftButtonPressed(const winrt::Microsoft::UI::Input::PointerEventArgs &args); + + void Input_OnRightButtonPressed(const winrt::Microsoft::UI::Input::PointerEventArgs &args); + + void Input_OnPointerMoved(const winrt::Microsoft::UI::Input::PointerEventArgs &args); + + void Input_OnPointerReleased(); + + void Island_OnStateChanged(); + + void Island_OnConnected(); + + void Island_OnDisconnected(); + + void Island_OnClosed(); + + void LightDismiss_Initialize(); + + void OnLeftClick(const winrt::float2 point, bool controlPressed); + + void OnRightClick(const winrt::float2 point); + + void Output_Initialize(); + + void Output_AddVisual(const winrt::float2 point, bool halfTransparent); + + void Output_UpdateCurrentColorVisual(); + + void SystemBackdrop_Initialize(); + + void SetLayoutDirectionForVisuals(); + + void Window_Initialize(); + + void Window_OnSettingChanged(const winrt::ContentEnvironmentSettingChangedEventArgs &args); + void Window_OnThemeChanged(); + + void Window_OnStateChanged(winrt::ContentIslandEnvironment const &sender); + + private: + static inline winrt::Color s_colors[] = { + {0xFF, 0x5B, 0x9B, 0xD5}, + {0xFF, 0xED, 0x7D, 0x31}, + {0xFF, 0x70, 0xAD, 0x47}, + {0xFF, 0xFF, 0xC0, 0x00}, + {0xFF, 0xFA, 0xEB, 0xD7}, + {0xFF, 0xFF, 0xFF, 0xFF}, + {0xFF, 0xFF, 0xFA, 0xFA}, + {0xFF, 0xFF, 0xC0, 0xCB}, + {0xFF, 0xB0, 0xE0, 0xE6}, + {0xFF, 0x98, 0xFB, 0x98}, + {0xFF, 0x87, 0xCE, 0xFA}, + }; + + static inline std::wstring s_colorNames[] = { + L"Blue", + L"Orange", + L"Green", + L"Yellow", + L"AntiqueWhite", + L"White", + L"Snow", + L"Pink", + L"PowderBlue", + L"PaleGreen", + L"LightSkyBlue", + }; + + // NodeSimpleFragmentFactory m_fragmentFactory; + // winrt::com_ptr m_fragmentRoot{nullptr}; + winrt::Compositor m_compositor{nullptr}; + winrt::ContentIsland m_island{nullptr}; + winrt::InputKeyboardSource m_keyboardSource{nullptr}; + winrt::InputPreTranslateKeyboardSource m_pretranslateSource{nullptr}; + winrt::InputPointerSource m_pointerSource{nullptr}; + winrt::InputLightDismissAction m_lightDismissAction{nullptr}; + winrt::InputFocusController m_focusController{nullptr}; + + // Background + winrt::CompositionColorBrush m_backgroundBrushDefault{nullptr}; + winrt::CompositionColorBrush m_backgroundBrushA{nullptr}; + winrt::CompositionColorBrush m_backgroundBrushB{nullptr}; + winrt::CompositionColorBrush m_backgroundBrushC{nullptr}; + winrt::Microsoft::UI::Composition::SpriteVisual m_backgroundVisual{nullptr}; + winrt::RectangleClip m_backgroundClip{nullptr}; + winrt::RectangleClip m_backdropClip{nullptr}; +#ifdef USE_EXPERIMENTAL_WINUI3 + winrt::ContentExternalBackdropLink m_backdropLink{nullptr}; +#endif + winrt::ICompositionSupportsSystemBackdrop m_backdropTarget{nullptr}; + + // Drawing squares + winrt::Microsoft::UI::Composition::VisualCollection m_visuals{nullptr}; + winrt::Microsoft::UI::Composition::Visual m_selectedVisual{nullptr}; + winrt::Microsoft::UI::Composition::SpriteVisual m_currentColorVisual{nullptr}; + winrt::float2 m_offset{}; + float m_backgroundOpacity = 0.5f; + + unsigned int m_currentColorIndex = 0; + winrt::CompositionColorBrush m_colorBrushes[_countof( + s_colors)]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + winrt::CompositionColorBrush m_halfTransparentColorBrushes[_countof( + s_colors)]{nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; + + // Popups + // https://task.ms/32440118: Add ContentIsland.SiteBridge to avoid this workaround + winrt::IContentSiteBridge m_siteBridge{nullptr}; +#ifdef USE_EXPERIMENTAL_WINUI3 + winrt::PopupWindowSiteBridge m_popupSiteBridge{nullptr}; +#endif + + // SystemBackdrops for Popups + winrt::ISystemBackdropControllerWithTargets m_backdropController{nullptr}; + winrt::SystemBackdropConfiguration m_backdropConfiguration{nullptr}; + + boolean m_ignoreLeftButtonPressed = false; + boolean m_useSystemBackdrop = false; + boolean m_inputActivatePopup = true; + boolean m_lightDismissPopup = false; + float m_prevRasterizationScale = 0; + winrt::ContentLayoutDirection m_prevLayout = winrt::ContentLayoutDirection::LeftToRight; + + boolean m_crossProcUIA = false; +}; + +} // namespace winrt::PlaygroundApp::implementation + +namespace winrt::PlaygroundApp::factory_implementation { +struct DrawingIsland : DrawingIslandT {}; +} // namespace winrt::PlaygroundApp::factory_implementation + +void RegisterDrawingIslandComponentView(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder); diff --git a/packages/playground/windows/playground-composition/Playground-Composition.cpp b/packages/playground/windows/playground-composition/Playground-Composition.cpp index 81b7be3a94f..c534a248362 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.cpp +++ b/packages/playground/windows/playground-composition/Playground-Composition.cpp @@ -24,21 +24,18 @@ #include #include "App.xaml.h" #include "AutoDraw.h" +#include "DrawingIsland.h" #include "NativeModules.h" #include "ReactPropertyBag.h" -#if USE_WINUI3 #include #include #include #include #include -#endif -#if USE_WINUI3 winrt::Microsoft::UI::Dispatching::DispatcherQueueController g_liftedDispatcherQueueController{nullptr}; winrt::Microsoft::UI::Composition::Compositor g_liftedCompositor{nullptr}; -#endif void RegisterCustomComponent(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept; @@ -105,7 +102,10 @@ struct CompReactPackageProvider : winrt::implements { public: // IReactPackageProvider void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept { - RegisterCustomComponent(packageBuilder); + // Replace RegisterDrawingIslandComponentView with RegisterCustomComponent when we have better XamlIsland hosting + // support + RegisterDrawingIslandComponentView(packageBuilder); + // RegisterCustomComponent(packageBuilder); } }; @@ -123,6 +123,7 @@ struct WindowData { std::wstring m_bundleFile; winrt::Microsoft::ReactNative::ReactNativeIsland m_compRootView{nullptr}; + winrt::Microsoft::UI::Content::DesktopChildSiteBridge m_bridge{nullptr}; winrt::Microsoft::ReactNative::ReactNativeHost m_host{nullptr}; winrt::Microsoft::ReactNative::ReactInstanceSettings m_instanceSettings{nullptr}; bool m_useLiftedComposition{true}; @@ -221,12 +222,11 @@ struct WindowData { std::wstring(L"file://").append(appDirectory).append(L"\\Bundle\\").c_str()); host.InstanceSettings().UseDeveloperSupport(true); - // Currently there is only SystemVisualSiteBridge which supports hosing ContentIslands within System - // Composition So our custom components do not run when running on lifted composition. This can be enabled in - // lifted once we have a VisualSiteBridge that works in lifted - if (!m_useLiftedComposition) { - host.PackageProviders().Append(winrt::make()); - } + // Some of the images in RNTester require a user-agent header to properly fetch + winrt::Microsoft::ReactNative::HttpSettings::SetDefaultUserAgent( + host.InstanceSettings(), L"React Native Windows Playground"); + + host.PackageProviders().Append(winrt::make()); winrt::Microsoft::ReactNative::ReactCoreInjection::SetTopLevelWindowId( host.InstanceSettings().Properties(), reinterpret_cast(hwnd)); @@ -249,13 +249,13 @@ struct WindowData { // Register ellipse:// uri hander for images host.PackageProviders().Append(winrt::make()); - auto bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create( + m_bridge = winrt::Microsoft::UI::Content::DesktopChildSiteBridge::Create( g_liftedCompositor, winrt::Microsoft::UI::GetWindowIdFromWindow(hwnd)); auto appContent = m_compRootView.Island(); - bridge.Connect(appContent); - bridge.Show(); + m_bridge.Connect(appContent); + m_bridge.Show(); m_compRootView.ScaleFactor(ScaleFactor(hwnd)); winrt::Microsoft::ReactNative::LayoutConstraints constraints; @@ -288,7 +288,7 @@ struct WindowData { } m_compRootView.Arrange(constraints, {0, 0}); - bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow); + m_bridge.ResizePolicy(winrt::Microsoft::UI::Content::ContentSizePolicy::ResizeContentToParentWindow); } else if (!m_target) { // General users of RNW should never set CompositionContext - this is an advanced usage to inject another @@ -330,15 +330,6 @@ struct WindowData { } } - /* - * Uncomment this to run using the bridge. This isn't publicly exposed, and isn't a mode that we will - * support (Fabric will always be bridgeless for windows.) But it can be useful for internal bug diagnosis. - */ - /* - winrt::Microsoft::ReactNative::ReactPropertyBag{host.InstanceSettings().Properties()}.Set( - winrt::Microsoft::ReactNative::ReactPropertyId(L"ReactNative", L"IsBridgeless"), true); - */ - // Nudge the ReactNativeHost to create the instance and wrapping context host.ReloadInstance(); @@ -369,6 +360,23 @@ struct WindowData { case IDM_SETTINGS: DialogBoxParam(s_instance, MAKEINTRESOURCE(IDD_SETTINGSBOX), hwnd, &Settings, reinterpret_cast(this)); break; + case IDM_UNLOAD: { + auto async = Host().UnloadInstance(); + async.Completed([&, uidispatch = InstanceSettings().UIDispatcher()]( + auto asyncInfo, winrt::Windows::Foundation::AsyncStatus asyncStatus) { + asyncStatus; + OutputDebugStringA("Instance Unload completed\n"); + + uidispatch.Post([&]() { + m_bridge.Close(); + m_bridge = nullptr; + }); + assert(asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed); + }); + m_compRootView = nullptr; + m_instanceSettings = nullptr; + m_host = nullptr; + } break; } return 0; @@ -571,11 +579,21 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) bool shouldPostQuitMessage = true; if (data->m_host) { shouldPostQuitMessage = false; + + winrt::Microsoft::ReactNative::ReactPropertyBag properties(data->m_host.InstanceSettings().Properties()); + + properties.Remove(winrt::Microsoft::ReactNative::ReactPropertyId< + winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext>{ + L"ReactNative.Composition", L"CompositionContext"}); + auto async = data->m_host.UnloadInstance(); async.Completed([host = data->m_host](auto asyncInfo, winrt::Windows::Foundation::AsyncStatus asyncStatus) { assert(asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed); host.InstanceSettings().UIDispatcher().Post([]() { PostQuitMessage(0); }); }); + data->m_compRootView = nullptr; + data->m_instanceSettings = nullptr; + data->m_host = nullptr; } delete WindowData::GetFromWindow(hwnd); @@ -695,7 +713,6 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR 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 = @@ -706,8 +723,7 @@ _Use_decl_annotations_ int CALLBACK WinMain(HINSTANCE instance, HINSTANCE, PSTR #ifdef USE_EXPERIMENTAL_WINUI3 // Island-support: Create our custom Xaml App object. This is needed to properly use the controls and metadata // in Microsoft.ui.xaml.controls.dll. - auto playgroundApp{winrt::make()}; -#endif + // auto playgroundApp{winrt::make()}; #endif 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 bcc1f139def..cff2595c7bd 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.rc +++ b/packages/playground/windows/playground-composition/Playground-Composition.rc @@ -57,6 +57,7 @@ BEGIN MENUITEM "&Open Javascript File...\tCtrl+O", IDM_OPENJSFILE MENUITEM "&New Window\tCtrl+N", IDM_NEWWINDOW MENUITEM "&Refresh\tF5", IDM_REFRESH + MENUITEM "&Unload", IDM_UNLOAD MENUITEM SEPARATOR MENUITEM "&Settings...\tAlt+S", IDM_SETTINGS MENUITEM SEPARATOR diff --git a/packages/playground/windows/playground-composition/Playground-Composition.vcxproj b/packages/playground/windows/playground-composition/Playground-Composition.vcxproj index 0073af5eeff..12e7bbe8180 100644 --- a/packages/playground/windows/playground-composition/Playground-Composition.vcxproj +++ b/packages/playground/windows/playground-composition/Playground-Composition.vcxproj @@ -132,6 +132,7 @@ Code + diff --git a/packages/playground/windows/playground-composition/resource.h b/packages/playground/windows/playground-composition/resource.h index 30955840a00..a542b5251d9 100644 --- a/packages/playground/windows/playground-composition/resource.h +++ b/packages/playground/windows/playground-composition/resource.h @@ -24,6 +24,7 @@ #define IDC_THEMELABEL 110 #define IDC_JSENGINELABEL 111 #define IDC_SIZETOCONTENT 112 +#define IDM_UNLOAD 113 #define IDI_ICON1 1008 // Next default values for new objects diff --git a/vnext/Desktop/module.g.cpp b/vnext/Desktop/module.g.cpp index 3762ad99669..789fa1b564b 100644 --- a/vnext/Desktop/module.g.cpp +++ b/vnext/Desktop/module.g.cpp @@ -15,7 +15,6 @@ void *winrt_make_Microsoft_ReactNative_Composition_Experimental_MicrosoftComposi #endif void *winrt_make_Microsoft_ReactNative_Composition_Experimental_SystemCompositionContextHelper(); void *winrt_make_Microsoft_ReactNative_Composition_CompositionUIService(); -void* winrt_make_Microsoft_ReactNative_Composition_ViewComponentView(); void *winrt_make_Microsoft_ReactNative_Composition_FocusManager(); void* winrt_make_Microsoft_ReactNative_JsiRuntime(); void* winrt_make_Microsoft_ReactNative_ReactCoreInjection(); @@ -43,9 +42,6 @@ void *winrt_make_Microsoft_ReactNative_Composition_Experimental_UriBrushFactoryI void* winrt_make_Microsoft_ReactNative_ReactNativeIsland() { winrt::throw_hresult(E_NOTIMPL); } -void* winrt_make_Microsoft_ReactNative_Composition_ViewComponentView() { - winrt::throw_hresult(E_NOTIMPL); -} void *winrt_make_Microsoft_ReactNative_Composition_FocusManager() { winrt::throw_hresult(E_NOTIMPL); } @@ -99,9 +95,6 @@ void* __stdcall winrt_get_activation_factory([[maybe_unused]] std::wstring_view if (requal(name, L"Microsoft.ReactNative.Composition.FocusManager")) { return winrt_make_Microsoft_ReactNative_Composition_FocusManager(); } - if (requal(name, L"Microsoft.ReactNative.Composition.ViewComponentView")) { - return winrt_make_Microsoft_ReactNative_Composition_ViewComponentView(); - } if (requal(name, L"Microsoft.ReactNative.JsiRuntime")) { return winrt_make_Microsoft_ReactNative_JsiRuntime(); diff --git a/vnext/Microsoft.ReactNative/ComponentView.idl b/vnext/Microsoft.ReactNative/ComponentView.idl index 1b1207e7a32..e731bf04cc0 100644 --- a/vnext/Microsoft.ReactNative/ComponentView.idl +++ b/vnext/Microsoft.ReactNative/ComponentView.idl @@ -60,13 +60,6 @@ namespace Microsoft.ReactNative void UpdateStateWithMutationAndPriority(StateUpdateMutation mutation, EventPriority priority); }; - [experimental] - [webhosthidden] - unsealed runtimeclass CreateComponentViewArgs { - Int32 Tag {get;}; - IReactContext ReactContext { get;}; - }; - [experimental] [webhosthidden] runtimeclass LosingFocusEventArgs : Microsoft.ReactNative.Composition.Input.RoutedEventArgs { @@ -89,40 +82,48 @@ namespace Microsoft.ReactNative [experimental] [webhosthidden] - unsealed runtimeclass ComponentView { - ComponentView(CreateComponentViewArgs args); + runtimeclass LayoutMetricsChangedArgs { + LayoutMetrics NewLayoutMetrics { get; }; + LayoutMetrics OldLayoutMetrics { get; }; + }; + + // [exclusiveto(ComponentView)] + // [uuid(AEE8C30B-0E56-4FB8-8A80-D5CA9DE90916)] + // interface IComponentViewFactory + // { + // } + + // [composable(IComponentViewFactory, protected)] + [experimental] + [webhosthidden] + unsealed runtimeclass ComponentView { Int32 Tag { get; }; ComponentView Parent { get; }; IVectorView Children { get; }; - IReactContext ReactContext { get;}; + IReactContext ReactContext { get; }; + LayoutMetrics LayoutMetrics { get; }; + IInspectable UserData; - overridable void MountChildComponentView(ComponentView childComponentView, UInt32 index); - overridable void UnmountChildComponentView(ComponentView childComponentView, UInt32 index); - overridable void HandleCommand(String commandName, IJSValueReader args); - overridable void UpdateProps(IComponentProps props, IComponentProps oldProps); - overridable void UpdateState(IComponentState state); - overridable void UpdateLayoutMetrics(LayoutMetrics metrics, LayoutMetrics oldMetrics); - overridable void FinalizeUpdates(ComponentViewUpdateMask updateMask); + Boolean TryFocus(); DOC_STRING("Used to handle key down events when this component is focused, or if a child component did not handle the key down") - overridable void OnKeyDown(Microsoft.ReactNative.Composition.Input.KeyboardSource source, Microsoft.ReactNative.Composition.Input.KeyRoutedEventArgs args); - + event Windows.Foundation.EventHandler KeyDown; DOC_STRING("Used to handle key up events when this component is focused, or if a child component did not handle the key up") - overridable void OnKeyUp(Microsoft.ReactNative.Composition.Input.KeyboardSource source, Microsoft.ReactNative.Composition.Input.KeyRoutedEventArgs args); - - overridable void OnCharacterReceived(Microsoft.ReactNative.Composition.Input.KeyboardSource source, Microsoft.ReactNative.Composition.Input.CharacterReceivedRoutedEventArgs args); - - overridable void OnPointerPressed(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args); - overridable void OnPointerReleased(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args); - overridable void OnPointerMoved(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args); - overridable void OnPointerWheelChanged(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args); - overridable void OnPointerEntered(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args); - overridable void OnPointerExited(Microsoft.ReactNative.Composition.Input.PointerRoutedEventArgs args); - overridable void OnPointerCaptureLost(); - - Boolean TryFocus(); + event Windows.Foundation.EventHandler KeyUp; + event Windows.Foundation.EventHandler CharacterReceived; + event Windows.Foundation.EventHandler PointerPressed; + event Windows.Foundation.EventHandler PointerReleased; + event Windows.Foundation.EventHandler PointerMoved; + event Windows.Foundation.EventHandler PointerWheelChanged; + event Windows.Foundation.EventHandler PointerEntered; + event Windows.Foundation.EventHandler PointerExited; + event Windows.Foundation.EventHandler PointerCaptureLost; + event Windows.Foundation.EventHandler Destroying; + event Windows.Foundation.EventHandler LayoutMetricsChanged; + event Windows.Foundation.EventHandler Mounted; + event Windows.Foundation.EventHandler Unmounted; event Windows.Foundation.EventHandler LosingFocus; event Windows.Foundation.EventHandler GettingFocus; event Windows.Foundation.EventHandler LostFocus; diff --git a/vnext/Microsoft.ReactNative/Composition.Input.idl b/vnext/Microsoft.ReactNative/Composition.Input.idl index 44e8f25e69a..cf2cbd8df6e 100644 --- a/vnext/Microsoft.ReactNative/Composition.Input.idl +++ b/vnext/Microsoft.ReactNative/Composition.Input.idl @@ -23,6 +23,7 @@ namespace Microsoft.ReactNative.Composition.Input Windows.System.VirtualKey Key { get; }; Microsoft.UI.Input.PhysicalKeyStatus KeyStatus { get; }; Windows.System.VirtualKey OriginalKey { get; }; + KeyboardSource KeyboardSource { get; }; } interface CharacterReceivedRoutedEventArgs requires RoutedEventArgs @@ -30,6 +31,7 @@ namespace Microsoft.ReactNative.Composition.Input Boolean Handled { get; set; }; Microsoft.UI.Input.PhysicalKeyStatus KeyStatus { get; }; Int32 KeyCode { get; }; + KeyboardSource KeyboardSource { get; }; }; interface IPointerPointTransform diff --git a/vnext/Microsoft.ReactNative/CompositionComponentView.idl b/vnext/Microsoft.ReactNative/CompositionComponentView.idl index b2627f4ddf8..306edd07ab8 100644 --- a/vnext/Microsoft.ReactNative/CompositionComponentView.idl +++ b/vnext/Microsoft.ReactNative/CompositionComponentView.idl @@ -29,116 +29,135 @@ namespace Microsoft.ReactNative.Composition namespace Experimental { [webhosthidden] [experimental] - interface IInternalCreateComponentViewArgs + interface IInternalComponentView { ICompositionContext CompositionContext { get; }; } } - [experimental] - [webhosthidden] - runtimeclass CreateCompositionComponentViewArgs : Microsoft.ReactNative.CreateComponentViewArgs { - Microsoft.UI.Composition.Compositor Compositor { get; }; - ComponentViewFeatures Features; - }; + // [exclusiveto(ComponentView)] + // [uuid(ABFAC092-E527-47DC-9CF9-7A4003B0AFB0)] + // interface IComponentViewFactory + // { + // } + // [composable(IComponentViewFactory, protected)] [experimental] [webhosthidden] unsealed runtimeclass ComponentView : Microsoft.ReactNative.ComponentView { - ComponentView(CreateCompositionComponentViewArgs args); - Microsoft.UI.Composition.Compositor Compositor { get; }; RootComponentView Root { get; }; Theme Theme; - overridable void OnThemeChanged(); + + event Windows.Foundation.EventHandler ThemeChanged; Boolean CapturePointer(Microsoft.ReactNative.Composition.Input.Pointer pointer); void ReleasePointerCapture(Microsoft.ReactNative.Composition.Input.Pointer pointer); }; namespace Experimental { + + [webhosthidden] + [experimental] + delegate Microsoft.ReactNative.Composition.Experimental.IVisual CreateInternalVisualDelegate(); + [webhosthidden] [experimental] DOC_STRING("Custom ViewComponents need to implement this interface to be able to provide a custom" " visual using the composition context that allows custom compositors. This is only required for" " custom components that need to support running in RNW instances with custom compositors. Most" - " custom components can just override CreateVisual on ViewComponentView." + " custom components can just set CreateVisualHandler on ViewComponentView." " This will be removed in a future version") interface IInternalCreateVisual { - Microsoft.ReactNative.Composition.Experimental.IVisual CreateInternalVisual(); + CreateInternalVisualDelegate CreateInternalVisualHandler; } } + // [exclusiveto(ViewComponentView)] + // [uuid(756AA1DF-ED74-467E-9BAA-3797B39B1875)] + // interface IViewComponentViewFactory + // { + // } + + // [composable(IViewComponentViewFactory, protected)] [experimental] [webhosthidden] unsealed runtimeclass ViewComponentView : ComponentView { - ViewComponentView(CreateCompositionComponentViewArgs args); Microsoft.ReactNative.ViewProps ViewProps { get; }; + }; - overridable Microsoft.UI.Composition.Visual CreateVisual(); + // Some other interfaces we could consider implementing/exposing + // Use ifdef USE_EXPERIMENTAL_WINUI3 to add these + // Microsoft.UI.Content.IContentLink // Use ifdef USE_EXPERIMENTAL_WINUI3 + // Microsoft.UI.Content.IContentSiteBridge + // Microsoft.UI.Content.IContentSiteBridge2 // Use ifdef USE_EXPERIMENTAL_WINUI3 + [experimental] + [webhosthidden] + runtimeclass ContentIslandComponentView : ViewComponentView { + void Connect(Microsoft.UI.Content.ContentIsland contentIsland); }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass SwitchComponentView : ViewComponentView { + runtimeclass SwitchComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass RootComponentView : ViewComponentView { + runtimeclass RootComponentView : ViewComponentView { Microsoft.ReactNative.ComponentView GetFocusedComponent(); }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass DebuggingOverlayComponentView : ViewComponentView { + runtimeclass DebuggingOverlayComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass ActivityIndicatorComponentView : ViewComponentView { + runtimeclass ActivityIndicatorComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass WindowsModalHostComponentView : ViewComponentView { + runtimeclass WindowsModalHostComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass ImageComponentView : ViewComponentView { + runtimeclass ImageComponentView : ViewComponentView { Microsoft.ReactNative.ImageProps ViewProps { get; }; }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass ParagraphComponentView : ViewComponentView { + runtimeclass ParagraphComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass ScrollViewComponentView : ViewComponentView { + runtimeclass ScrollViewComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass UnimplementedNativeViewComponentView : ViewComponentView { + runtimeclass UnimplementedNativeViewComponentView : ViewComponentView { }; [experimental] [webhosthidden] [default_interface] - unsealed runtimeclass WindowsTextInputComponentView : ViewComponentView { + runtimeclass WindowsTextInputComponentView : ViewComponentView { }; } // namespace Microsoft.ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp new file mode 100644 index 00000000000..0cbbaa32526 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.cpp @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "AbiEventEmitter.h" +#include "JsiWriter.h" + +namespace winrt::Microsoft::ReactNative::implementation { + +EventEmitter::EventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) : m_eventEmitter(eventEmitter) {} + +void EventEmitter::DispatchEvent( + winrt::hstring eventName, + const winrt::Microsoft::ReactNative::JSValueArgWriter &args) { + m_eventEmitter->dispatchEvent(winrt::to_string(eventName), [args](facebook::jsi::Runtime &runtime) { + auto writer = winrt::make(runtime); + args(writer); + return winrt::get_self(writer)->MoveResult(); + }); +} + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h new file mode 100644 index 00000000000..2daa0986947 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/AbiEventEmitter.h @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "ViewProps.g.h" + +#include "EventEmitter.g.h" +#include +#include + +namespace winrt::Microsoft::ReactNative::implementation { + +struct EventEmitter : EventEmitterT { + EventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter); + + void DispatchEvent(winrt::hstring eventName, const winrt::Microsoft::ReactNative::JSValueArgWriter &args); + + private: + facebook::react::EventEmitter::Shared const m_eventEmitter; +}; + +} // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp index 34ff1adb230..d35d25104f2 100644 --- a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.cpp @@ -65,6 +65,13 @@ void ShadowNode::StateData(winrt::IInspectable tag) noexcept { } } +winrt::Microsoft::ReactNative::EventEmitter ShadowNode::EventEmitter() const noexcept { + if (auto shadowNode = m_shadowNode.lock()) { + return winrt::make(shadowNode->getEventEmitter()); + } + return nullptr; +} + } // namespace winrt::Microsoft::ReactNative::implementation namespace Microsoft::ReactNative { diff --git a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h index a847f6112bb..fe689b105fa 100644 --- a/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h +++ b/vnext/Microsoft.ReactNative/Fabric/AbiShadowNode.h @@ -8,6 +8,7 @@ #include "YogaLayoutableShadowNode.g.h" #include #include +#include "AbiEventEmitter.h" #include "AbiState.h" #include "AbiViewProps.h" @@ -42,6 +43,8 @@ struct ShadowNode : ShadowNodeT { winrt::IInspectable StateData() const noexcept; void StateData(winrt::IInspectable tag) noexcept; + winrt::Microsoft::ReactNative::EventEmitter EventEmitter() const noexcept; + protected: facebook::react::ShadowNode::Weak m_shadowNode; winrt::IInspectable m_tag; diff --git a/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp index 6876376d759..c7af2ca7ebf 100644 --- a/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/ComponentView.cpp @@ -8,8 +8,11 @@ #include "DynamicReader.h" #include "ComponentView.g.cpp" -#include "CreateComponentViewArgs.g.cpp" +#include "LayoutMetricsChangedArgs.g.cpp" +#include "MountChildComponentViewArgs.g.cpp" +#include "UnmountChildComponentViewArgs.g.cpp" #include +#include "AbiEventEmitter.h" #include "AbiShadowNode.h" namespace winrt::Microsoft::ReactNative::Composition::implementation { @@ -18,31 +21,29 @@ struct RootComponentView; namespace winrt::Microsoft::ReactNative::implementation { -CreateComponentViewArgs::CreateComponentViewArgs( - const winrt::Microsoft::ReactNative::IReactContext &reactContext, - facebook::react::Tag tag) - : m_tag(tag), m_reactContext(reactContext){}; +ComponentView::ComponentView(facebook::react::Tag tag, winrt::Microsoft::ReactNative::ReactContext const &reactContext) + : m_tag(tag), m_reactContext(reactContext) {} -facebook::react::Tag CreateComponentViewArgs::Tag() const noexcept { - return m_tag; +void ComponentView::MarkAsCustomComponent() noexcept { + m_customComponent = true; } -winrt::Microsoft::ReactNative::IReactContext CreateComponentViewArgs::ReactContext() const noexcept { - return m_reactContext; +std::vector +ComponentView::supplementalComponentDescriptorProviders() noexcept { + return {}; } -ComponentView::ComponentView( - facebook::react::Tag tag, - winrt::Microsoft::ReactNative::ReactContext const &reactContext, - bool customComponent) - : m_tag(tag), m_reactContext(reactContext), m_customComponent(customComponent) {} +winrt::event_token ComponentView::Destroying( + winrt::Windows::Foundation::EventHandler const &handler) noexcept { + return m_destroyingEvent.add(handler); +} -ComponentView::ComponentView(winrt::Microsoft::ReactNative::CreateComponentViewArgs const &args) - : ComponentView(args.Tag(), args.ReactContext(), true) {} +void ComponentView::Destroying(winrt::event_token const &token) noexcept { + m_destroyingEvent.remove(token); +} -std::vector -ComponentView::supplementalComponentDescriptorProviders() noexcept { - return {}; +void ComponentView::onDestroying() noexcept { + m_destroyingEvent(*this, *this); } void ComponentView::MountChildComponentView( @@ -50,73 +51,198 @@ void ComponentView::MountChildComponentView( uint32_t index) noexcept { m_children.InsertAt(index, childComponentView); winrt::get_self(childComponentView)->parent(*this); + if (m_mountChildComponentViewHandler) { + m_mountChildComponentViewHandler(*this, winrt::make(childComponentView, index)); + } + if (m_mounted) { + winrt::get_self(childComponentView)->onMounted(); + } +} + +void ComponentView::MountChildComponentViewHandler(const MountChildComponentViewDelegate &handler) noexcept { + m_mountChildComponentViewHandler = handler; +} + +void ComponentView::onMounted() noexcept { + assert(!m_mounted); + m_mounted = true; + for (auto it = m_children.begin(); it != m_children.end(); ++it) { + winrt::get_self(*it)->onMounted(); + } + m_mountedEvent(*this, *this); +} + +winrt::event_token ComponentView::Mounted( + winrt::Windows::Foundation::EventHandler const &handler) noexcept { + return m_mountedEvent.add(handler); +} + +void ComponentView::Mounted(winrt::event_token const &token) noexcept { + m_mountedEvent.remove(token); } void ComponentView::UnmountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, uint32_t index) noexcept { + if (m_mountChildComponentViewHandler) { + m_mountChildComponentViewHandler(*this, winrt::make(childComponentView, index)); + } m_children.RemoveAt(index); winrt::get_self(childComponentView)->parent(nullptr); + winrt::get_self(childComponentView)->onUnmounted(); +} +void ComponentView::UnmountChildComponentViewHandler(const UnmountChildComponentViewDelegate &handler) noexcept { + m_unmountChildComponentViewHandler = handler; +} + +void ComponentView::onUnmounted() noexcept { + assert(m_mounted); + m_mounted = false; + for (auto it = m_children.begin(); it != m_children.end(); ++it) { + winrt::get_self(*it)->onUnmounted(); + } + m_unmountedEvent(*this, *this); +} + +winrt::event_token ComponentView::Unmounted( + winrt::Windows::Foundation::EventHandler const &handler) noexcept { + return m_unmountedEvent.add(handler); +} + +void ComponentView::Unmounted(winrt::event_token const &token) noexcept { + m_unmountedEvent.remove(token); +} + +MountChildComponentViewArgs::MountChildComponentViewArgs( + const winrt::Microsoft::ReactNative::ComponentView &child, + uint32_t index) + : m_child(child), m_index(index) {} + +winrt::Microsoft::ReactNative::ComponentView MountChildComponentViewArgs::Child() const noexcept { + return m_child; +} + +uint32_t MountChildComponentViewArgs::Index() const noexcept { + return m_index; +} + +UnmountChildComponentViewArgs::UnmountChildComponentViewArgs( + const winrt::Microsoft::ReactNative::ComponentView &child, + uint32_t index) + : m_child(child), m_index(index) {} + +winrt::Microsoft::ReactNative::ComponentView UnmountChildComponentViewArgs::Child() const noexcept { + return m_child; +} + +uint32_t UnmountChildComponentViewArgs::Index() const noexcept { + return m_index; } void ComponentView::updateProps( facebook::react::Props::Shared const &props, facebook::react::Props::Shared const &oldProps) noexcept { - if (m_customComponent) { - // Review is it expected that I need this cast to call overridden methods? - winrt::Microsoft::ReactNative::ComponentView outer(*this); - outer.UpdateProps(userProps(props), oldProps ? userProps(oldProps) : nullptr); + if (m_updatePropsDelegate) { + m_updatePropsDelegate(*this, userProps(props), oldProps ? userProps(oldProps) : nullptr); } } -void ComponentView::UpdateProps( - const winrt::Microsoft::ReactNative::IComponentProps &props, - const winrt::Microsoft::ReactNative::IComponentProps &oldProps) noexcept {} +void ComponentView::UpdatePropsHandler(const UpdatePropsDelegate &handler) noexcept { + m_updatePropsDelegate = handler; +} const winrt::Microsoft::ReactNative::IComponentProps ComponentView::userProps( facebook::react::Props::Shared const &props) noexcept { - assert(m_customComponent); const auto &abiProps = *std::static_pointer_cast(props); return abiProps.UserProps(); } -void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept {} +void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept { + if (m_updateEventEmitterHandler) { + m_updateEventEmitterHandler(*this, winrt::make(eventEmitter)); + } +} + +void ComponentView::UpdateEventEmitterHandler(const UpdateEventEmitterDelegate &handler) noexcept { + m_updateEventEmitterHandler = handler; +} void ComponentView::updateState( facebook::react::State::Shared const &state, facebook::react::State::Shared const &oldState) noexcept { // Avoid new-ing up a new AbiComponentState on every state change if we are not a custom component - if (m_customComponent) { - // Review is it expected that I need this cast to call overridden methods? - winrt::Microsoft::ReactNative::ComponentView outer(*this); - outer.UpdateState(winrt::make<::Microsoft::ReactNative::AbiComponentState>(state)); + if (m_updateStateDelegate) { + m_updateStateDelegate(*this, winrt::make<::Microsoft::ReactNative::AbiComponentState>(state)); } } -void ComponentView::UpdateState(const winrt::Microsoft::ReactNative::IComponentState &state) noexcept {} +void ComponentView::UpdateStateHandler(const UpdateStateDelegate &handler) noexcept { + m_updateStateDelegate = handler; +} + +LayoutMetricsChangedArgs::LayoutMetricsChangedArgs( + const winrt::Microsoft::ReactNative::LayoutMetrics &newLayoutMetrics, + const winrt::Microsoft::ReactNative::LayoutMetrics &oldLayoutMetrics) + : m_old(oldLayoutMetrics), m_new(newLayoutMetrics) {} + +LayoutMetrics LayoutMetricsChangedArgs::OldLayoutMetrics() const noexcept { + return m_old; +} + +LayoutMetrics LayoutMetricsChangedArgs::NewLayoutMetrics() const noexcept { + return m_new; +} void ComponentView::updateLayoutMetrics( facebook::react::LayoutMetrics const &layoutMetrics, facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept { - // Review is it expected that I need this cast to call overridden methods? - winrt::Microsoft::ReactNative::ComponentView outer(*this); - outer.UpdateLayoutMetrics( - {{layoutMetrics.frame.origin.x, - layoutMetrics.frame.origin.y, - layoutMetrics.frame.size.width, - layoutMetrics.frame.size.height}, - layoutMetrics.pointScaleFactor}, - {{oldLayoutMetrics.frame.origin.x, - oldLayoutMetrics.frame.origin.y, - oldLayoutMetrics.frame.size.width, - oldLayoutMetrics.frame.size.height}, - oldLayoutMetrics.pointScaleFactor}); + winrt::Microsoft::ReactNative::LayoutMetrics oldMetrics{ + {oldLayoutMetrics.frame.origin.x, + oldLayoutMetrics.frame.origin.y, + oldLayoutMetrics.frame.size.width, + oldLayoutMetrics.frame.size.height}, + oldLayoutMetrics.pointScaleFactor}; + winrt::Microsoft::ReactNative::LayoutMetrics newMetrics{ + {layoutMetrics.frame.origin.x, + layoutMetrics.frame.origin.y, + layoutMetrics.frame.size.width, + layoutMetrics.frame.size.height}, + layoutMetrics.pointScaleFactor}; + + m_layoutMetrics = layoutMetrics; + + m_layoutMetricsChangedEvent(*this, winrt::make(newMetrics, oldMetrics)); +} + +LayoutMetrics ComponentView::LayoutMetrics() const noexcept { + return { + {m_layoutMetrics.frame.origin.x, + m_layoutMetrics.frame.origin.y, + m_layoutMetrics.frame.size.width, + m_layoutMetrics.frame.size.height}, + m_layoutMetrics.pointScaleFactor}; +} + +winrt::event_token ComponentView::LayoutMetricsChanged( + winrt::Windows::Foundation::EventHandler const + &handler) noexcept { + return m_layoutMetricsChangedEvent.add(handler); } -void ComponentView::UpdateLayoutMetrics(const LayoutMetrics &metrics, const LayoutMetrics &oldMetrics) noexcept {} +void ComponentView::LayoutMetricsChanged(winrt::event_token const &token) noexcept { + m_layoutMetricsChangedEvent.remove(token); +} -void ComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept {} +void ComponentView::FinalizeUpdateHandler(const UpdateFinalizerDelegate &handler) noexcept { + m_finalizeUpdateHandler = handler; +} + +void ComponentView::FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept { + if (m_finalizeUpdateHandler) { + m_finalizeUpdateHandler(*this, updateMask); + } +} void ComponentView::prepareForRecycle() noexcept {} @@ -125,9 +251,17 @@ facebook::react::Props::Shared ComponentView::props() noexcept { return {}; } +void ComponentView::CustomCommandHandler(const HandleCommandDelegate &handler) noexcept { + m_customCommandHandler = handler; +} + void ComponentView::HandleCommand( winrt::hstring commandName, - const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept {} + const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept { + if (m_customCommandHandler) { + m_customCommandHandler(*this, commandName, args); + } +} winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView * ComponentView::rootComponentView() noexcept { @@ -295,14 +429,118 @@ bool ComponentView::TryFocus() noexcept { return false; } +winrt::event_token ComponentView::KeyDown( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs> const &handler) noexcept { + return m_keyDownEvent.add(handler); +} + +void ComponentView::KeyDown(winrt::event_token const &token) noexcept { + m_keyDownEvent.remove(token); +} + +winrt::event_token ComponentView::KeyUp( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs> const &handler) noexcept { + return m_keyUpEvent.add(handler); +} + +void ComponentView::KeyUp(winrt::event_token const &token) noexcept { + m_keyUpEvent.remove(token); +} + +winrt::event_token ComponentView::CharacterReceived( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs> const &handler) noexcept { + return m_characterReceivedEvent.add(handler); +} + +void ComponentView::CharacterReceived(winrt::event_token const &token) noexcept { + m_characterReceivedEvent.remove(token); +} + +winrt::event_token ComponentView::PointerPressed( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept { + return m_pointerPressedEvent.add(handler); +} + +void ComponentView::PointerPressed(winrt::event_token const &token) noexcept { + m_pointerPressedEvent.remove(token); +} + +winrt::event_token ComponentView::PointerReleased( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept { + return m_pointerReleasedEvent.add(handler); +} + +void ComponentView::PointerReleased(winrt::event_token const &token) noexcept { + m_pointerReleasedEvent.remove(token); +} + +winrt::event_token ComponentView::PointerMoved( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept { + return m_pointerMovedEvent.add(handler); +} + +void ComponentView::PointerMoved(winrt::event_token const &token) noexcept { + m_pointerMovedEvent.remove(token); +} + +winrt::event_token ComponentView::PointerWheelChanged( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept { + return m_pointerWheelChangedEvent.add(handler); +} + +void ComponentView::PointerWheelChanged(winrt::event_token const &token) noexcept { + m_pointerWheelChangedEvent.remove(token); +} + +winrt::event_token ComponentView::PointerEntered( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept { + return m_pointerEnteredEvent.add(handler); +} + +void ComponentView::PointerEntered(winrt::event_token const &token) noexcept { + m_pointerEnteredEvent.remove(token); +} + +winrt::event_token ComponentView::PointerExited( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept { + return m_pointerExitedEvent.add(handler); +} + +void ComponentView::PointerExited(winrt::event_token const &token) noexcept { + m_pointerExitedEvent.remove(token); +} + +winrt::event_token ComponentView::PointerCaptureLost( + winrt::Windows::Foundation::EventHandler const &handler) noexcept { + return m_pointerCaptureLostEvent.add(handler); +} + +void ComponentView::PointerCaptureLost(winrt::event_token const &token) noexcept { + m_pointerCaptureLostEvent.remove(token); +} + void ComponentView::OnPointerEntered( - const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {} + const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { + m_pointerEnteredEvent(*this, args); +} void ComponentView::OnPointerExited( - const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept {} + const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { + m_pointerExitedEvent(*this, args); +} void ComponentView::OnPointerPressed( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { + m_pointerPressedEvent(*this, args); if (m_parent && !args.Handled()) { winrt::get_self(m_parent)->OnPointerPressed(args); } @@ -310,6 +548,7 @@ void ComponentView::OnPointerPressed( void ComponentView::OnPointerReleased( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { + m_pointerReleasedEvent(*this, args); if (m_parent && !args.Handled()) { winrt::get_self(m_parent)->OnPointerReleased(args); } @@ -317,6 +556,7 @@ void ComponentView::OnPointerReleased( void ComponentView::OnPointerMoved( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { + m_pointerMovedEvent(*this, args); if (m_parent && !args.Handled()) { winrt::get_self(m_parent)->OnPointerMoved(args); } @@ -324,36 +564,38 @@ void ComponentView::OnPointerMoved( void ComponentView::OnPointerWheelChanged( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept { + m_pointerWheelChangedEvent(*this, args); if (m_parent && !args.Handled()) { winrt::get_self(m_parent)->OnPointerWheelChanged( args); } } -void ComponentView::OnPointerCaptureLost() noexcept {} +void ComponentView::OnPointerCaptureLost() noexcept { + m_pointerCaptureLostEvent(*this, *this); +} void ComponentView::OnKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { + m_keyDownEvent(*this, args); if (m_parent && !args.Handled()) { - winrt::get_self(m_parent)->OnKeyDown(source, args); + winrt::get_self(m_parent)->OnKeyDown(args); } } void ComponentView::OnKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { + m_keyUpEvent(*this, args); if (m_parent && !args.Handled()) { - winrt::get_self(m_parent)->OnKeyUp(source, args); + winrt::get_self(m_parent)->OnKeyUp(args); } } void ComponentView::OnCharacterReceived( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept { + m_characterReceivedEvent(*this, args); if (m_parent && !args.Handled()) { - winrt::get_self(m_parent)->OnCharacterReceived( - source, args); + winrt::get_self(m_parent)->OnCharacterReceived(args); } } @@ -373,6 +615,14 @@ facebook::react::Tag ComponentView::Tag() const noexcept { return m_tag; } +void ComponentView::UserData(const winrt::IInspectable &userData) noexcept { + m_userData = userData; +} + +winrt::IInspectable ComponentView::UserData() const noexcept { + return m_userData; +} + // By default, hitTests according the pointerEvents prop on the Component. // If ignorePointerEvents = true, all Components are treated as valid targets facebook::react::Tag ComponentView::hitTest( diff --git a/vnext/Microsoft.ReactNative/Fabric/ComponentView.h b/vnext/Microsoft.ReactNative/Fabric/ComponentView.h index 5683d19a5f3..f9f60bb52c9 100644 --- a/vnext/Microsoft.ReactNative/Fabric/ComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/ComponentView.h @@ -16,7 +16,9 @@ #include #include "ComponentView.g.h" -#include "CreateComponentViewArgs.g.h" +#include "LayoutMetricsChangedArgs.g.h" +#include "MountChildComponentViewArgs.g.h" +#include "UnmountChildComponentViewArgs.g.h" namespace winrt::Microsoft::ReactNative::Composition::implementation { struct RootComponentView; @@ -41,25 +43,41 @@ struct BringIntoViewOptions { float VerticalOffset{0}; }; -struct CreateComponentViewArgs : public CreateComponentViewArgsT { - CreateComponentViewArgs(const winrt::Microsoft::ReactNative::IReactContext &reactContext, facebook::react::Tag tag); +struct LayoutMetricsChangedArgs : public LayoutMetricsChangedArgsT { + LayoutMetricsChangedArgs(const LayoutMetrics &newLayoutMetrics, const LayoutMetrics &oldLayoutMetrics); - facebook::react::Tag Tag() const noexcept; + LayoutMetrics OldLayoutMetrics() const noexcept; + LayoutMetrics NewLayoutMetrics() const noexcept; - winrt::Microsoft::ReactNative::IReactContext ReactContext() const noexcept; + private: + LayoutMetrics m_old; + LayoutMetrics m_new; +}; + +struct MountChildComponentViewArgs : public MountChildComponentViewArgsT { + MountChildComponentViewArgs(const winrt::Microsoft::ReactNative::ComponentView &child, uint32_t index); + + winrt::Microsoft::ReactNative::ComponentView Child() const noexcept; + uint32_t Index() const noexcept; private: - const facebook::react::Tag m_tag; - const winrt::Microsoft::ReactNative::IReactContext m_reactContext; + winrt::Microsoft::ReactNative::ComponentView m_child; + uint32_t m_index; }; -struct ComponentView : public ComponentViewT { - ComponentView(winrt::Microsoft::ReactNative::CreateComponentViewArgs const &args); +struct UnmountChildComponentViewArgs : public UnmountChildComponentViewArgsT { + UnmountChildComponentViewArgs(const winrt::Microsoft::ReactNative::ComponentView &child, uint32_t index); - ComponentView( - facebook::react::Tag tag, - winrt::Microsoft::ReactNative::ReactContext const &reactContext, - bool customComponent); + winrt::Microsoft::ReactNative::ComponentView Child() const noexcept; + uint32_t Index() const noexcept; + + private: + winrt::Microsoft::ReactNative::ComponentView m_child; + uint32_t m_index; +}; + +struct ComponentView : public ComponentViewT { + ComponentView(facebook::react::Tag tag, winrt::Microsoft::ReactNative::ReactContext const &reactContext); virtual std::vector supplementalComponentDescriptorProviders() noexcept; virtual void updateProps( @@ -91,7 +109,20 @@ struct ComponentView : public ComponentViewT { virtual void onGettingFocus(const winrt::Microsoft::ReactNative::GettingFocusEventArgs &args) noexcept; virtual void onLostFocus(const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept; virtual void onGotFocus(const winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs &args) noexcept; + void MarkAsCustomComponent() noexcept; + virtual void onMounted() noexcept; + virtual void onUnmounted() noexcept; + void onDestroying() noexcept; + winrt::event_token Destroying( + winrt::Windows::Foundation::EventHandler const &handler) noexcept; + void Destroying(winrt::event_token const &token) noexcept; + winrt::event_token Mounted( + winrt::Windows::Foundation::EventHandler const &handler) noexcept; + void Mounted(winrt::event_token const &token) noexcept; + winrt::event_token Unmounted( + winrt::Windows::Foundation::EventHandler const &handler) noexcept; + void Unmounted(winrt::event_token const &token) noexcept; winrt::event_token LosingFocus( winrt::Windows::Foundation::EventHandler const &handler) noexcept; @@ -108,6 +139,52 @@ struct ComponentView : public ComponentViewT { winrt::Windows::Foundation::EventHandler const &handler) noexcept; void GotFocus(winrt::event_token const &token) noexcept; + winrt::event_token LayoutMetricsChanged( + winrt::Windows::Foundation::EventHandler const + &handler) noexcept; + void LayoutMetricsChanged(winrt::event_token const &token) noexcept; + + winrt::event_token KeyDown( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs> const &handler) noexcept; + void KeyDown(winrt::event_token const &token) noexcept; + winrt::event_token KeyUp( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs> const &handler) noexcept; + void KeyUp(winrt::event_token const &token) noexcept; + winrt::event_token CharacterReceived( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs> const &handler) noexcept; + void CharacterReceived(winrt::event_token const &token) noexcept; + winrt::event_token PointerPressed( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept; + void PointerPressed(winrt::event_token const &token) noexcept; + winrt::event_token PointerReleased( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept; + void PointerReleased(winrt::event_token const &token) noexcept; + winrt::event_token PointerMoved( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept; + void PointerMoved(winrt::event_token const &token) noexcept; + winrt::event_token PointerWheelChanged( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept; + void PointerWheelChanged(winrt::event_token const &token) noexcept; + winrt::event_token PointerEntered( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept; + void PointerEntered(winrt::event_token const &token) noexcept; + winrt::event_token PointerExited( + winrt::Windows::Foundation::EventHandler< + winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs> const &handler) noexcept; + void PointerExited(winrt::event_token const &token) noexcept; + winrt::event_token PointerCaptureLost( + winrt::Windows::Foundation::EventHandler const &handler) noexcept; + void PointerCaptureLost(winrt::event_token const &token) noexcept; + + LayoutMetrics LayoutMetrics() const noexcept; bool TryFocus() noexcept; @@ -131,7 +208,17 @@ struct ComponentView : public ComponentViewT { virtual const winrt::Microsoft::ReactNative::IComponentProps userProps( facebook::react::Props::Shared const &props) noexcept; - // Publicaly overridable APIs + void UserData(const winrt::IInspectable &userData) noexcept; + winrt::IInspectable UserData() const noexcept; + + void CustomCommandHandler(const HandleCommandDelegate &handler) noexcept; + void UpdatePropsHandler(const UpdatePropsDelegate &handler) noexcept; + void UpdateStateHandler(const UpdateStateDelegate &handler) noexcept; + void UpdateEventEmitterHandler(const UpdateEventEmitterDelegate &handler) noexcept; + void MountChildComponentViewHandler(const MountChildComponentViewDelegate &handler) noexcept; + void UnmountChildComponentViewHandler(const UnmountChildComponentViewDelegate &handler) noexcept; + void FinalizeUpdateHandler(const UpdateFinalizerDelegate &handler) noexcept; + virtual void MountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, uint32_t index) noexcept; @@ -141,11 +228,6 @@ struct ComponentView : public ComponentViewT { virtual void HandleCommand( winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept; - virtual void UpdateProps( - const winrt::Microsoft::ReactNative::IComponentProps &props, - const winrt::Microsoft::ReactNative::IComponentProps &oldProps) noexcept; - virtual void UpdateState(const winrt::Microsoft::ReactNative::IComponentState &state) noexcept; - virtual void UpdateLayoutMetrics(const LayoutMetrics &metrics, const LayoutMetrics &oldMetrics) noexcept; virtual void FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept; virtual void OnPointerEntered( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept; @@ -160,25 +242,68 @@ struct ComponentView : public ComponentViewT { virtual void OnPointerWheelChanged( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept; virtual void OnPointerCaptureLost() noexcept; - virtual void OnKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; - virtual void OnKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; + virtual void OnKeyDown(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; + virtual void OnKeyUp(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; virtual void OnCharacterReceived( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept; protected: - const bool m_customComponent; // Is a user custom component, and so needs to call external override functions + bool m_customComponent : 1 {false}; // Is a user custom component, and so needs to call external override functions + bool m_mounted : 1 {false}; const facebook::react::Tag m_tag; + winrt::IInspectable m_userData; winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView *m_rootView{nullptr}; mutable winrt::Microsoft::ReactNative::Composition::implementation::Theme *m_theme{nullptr}; const winrt::Microsoft::ReactNative::ReactContext m_reactContext; winrt::Microsoft::ReactNative::ComponentView m_parent{nullptr}; + facebook::react::LayoutMetrics m_layoutMetrics; winrt::Windows::Foundation::Collections::IVector m_children{ winrt::single_threaded_vector()}; + + UpdatePropsDelegate m_updatePropsDelegate{nullptr}; + UpdateStateDelegate m_updateStateDelegate{nullptr}; + HandleCommandDelegate m_customCommandHandler{nullptr}; + UpdateFinalizerDelegate m_finalizeUpdateHandler{nullptr}; + MountChildComponentViewDelegate m_mountChildComponentViewHandler{nullptr}; + UnmountChildComponentViewDelegate m_unmountChildComponentViewHandler{nullptr}; + UpdateEventEmitterDelegate m_updateEventEmitterHandler{nullptr}; + + winrt::event< + winrt::Windows::Foundation::EventHandler> + m_keyDownEvent; + winrt::event< + winrt::Windows::Foundation::EventHandler> + m_keyUpEvent; + winrt::event> + m_characterReceivedEvent; + winrt::event> + m_pointerPressedEvent; + winrt::event> + m_pointerReleasedEvent; + winrt::event> + m_pointerMovedEvent; + winrt::event> + m_pointerWheelChangedEvent; + winrt::event> + m_pointerEnteredEvent; + winrt::event> + m_pointerExitedEvent; + winrt::event> + m_pointerCaptureLostEvent; + + winrt::event> + m_layoutMetricsChangedEvent; + winrt::event> + m_destroyingEvent; + winrt::event> m_mountedEvent; + winrt::event> m_unmountedEvent; winrt::event> m_losingFocusEvent; winrt::event> @@ -199,9 +324,3 @@ bool walkTree( Mso::Functor &fn) noexcept; } // namespace winrt::Microsoft::ReactNative::implementation - -namespace winrt::Microsoft::ReactNative::factory_implementation { - -struct ComponentView : ComponentViewT {}; - -} // namespace winrt::Microsoft::ReactNative::factory_implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ActivityIndicatorComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ActivityIndicatorComponentView.cpp index dc5da5c276a..3b698ada7b7 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ActivityIndicatorComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ActivityIndicatorComponentView.cpp @@ -29,8 +29,7 @@ ActivityIndicatorComponentView::ActivityIndicatorComponentView( compContext, tag, reactContext, - ComponentViewFeatures::Default, - false) {} + ComponentViewFeatures::Default) {} void ActivityIndicatorComponentView::MountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, @@ -83,6 +82,7 @@ void ActivityIndicatorComponentView::FinalizeUpdates( } else { m_ActivityIndicatorVisual.Size(radiusLarge * m_layoutMetrics.pointScaleFactor); } + base_type::FinalizeUpdates(updateMask); } void ActivityIndicatorComponentView::updateState( diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp index 6e38462ac2f..360a3161c14 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ComponentViewRegistry.cpp @@ -124,5 +124,8 @@ void ComponentViewRegistry::enqueueComponentViewWithComponentHandle( ->prepareForRecycle(); m_registry.erase(tag); + + winrt::get_self(componentViewDescriptor.view) + ->onDestroying(); } } // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp index e0c8ed3bd64..c05afdf02fc 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.cpp @@ -10,8 +10,13 @@ namespace winrt::Microsoft::ReactNative::Composition::Input::implementation { -KeyRoutedEventArgs::KeyRoutedEventArgs(facebook::react::Tag tag, uint32_t msg, uint64_t wParam, int64_t lParam) - : m_tag(tag) { +KeyRoutedEventArgs::KeyRoutedEventArgs( + facebook::react::Tag tag, + uint32_t msg, + uint64_t wParam, + int64_t lParam, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source) + : m_tag(tag), m_source(source) { bool fUp = (msg == WM_KEYUP || msg == WM_SYSKEYUP); bool fSysKey = (msg == WM_SYSKEYUP || msg == WM_SYSKEYDOWN); @@ -35,8 +40,11 @@ KeyRoutedEventArgs::KeyRoutedEventArgs(facebook::react::Tag tag, uint32_t msg, u } #ifdef USE_WINUI3 -KeyRoutedEventArgs::KeyRoutedEventArgs(facebook::react::Tag tag, winrt::Microsoft::UI::Input::KeyEventArgs const &args) - : m_tag(tag) { +KeyRoutedEventArgs::KeyRoutedEventArgs( + facebook::react::Tag tag, + winrt::Microsoft::UI::Input::KeyEventArgs const &args, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source) + : m_tag(tag), m_source(source) { auto keyStatus = args.KeyStatus(); m_keyStatus.RepeatCount = keyStatus.RepeatCount; m_keyStatus.ScanCode = keyStatus.ScanCode; @@ -75,12 +83,17 @@ winrt::Microsoft::UI::Input::PhysicalKeyStatus KeyRoutedEventArgs::KeyStatus() n winrt::Windows::System::VirtualKey KeyRoutedEventArgs::OriginalKey() noexcept { return m_key; } +winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource KeyRoutedEventArgs::KeyboardSource() const noexcept { + return m_source; +} CharacterReceivedRoutedEventArgs::CharacterReceivedRoutedEventArgs( facebook::react::Tag tag, uint32_t msg, uint64_t wParam, - int64_t lParam) { + int64_t lParam, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source) + : m_source(source) { m_keycode = static_cast(wParam); m_keyStatus.RepeatCount = (lParam & 0x0000FFFF); // bits 0-15 m_keyStatus.ScanCode = (lParam & 0x00FF0000) >> 16; // bits 16-23 @@ -93,8 +106,9 @@ CharacterReceivedRoutedEventArgs::CharacterReceivedRoutedEventArgs( #ifdef USE_WINUI3 CharacterReceivedRoutedEventArgs::CharacterReceivedRoutedEventArgs( facebook::react::Tag tag, - winrt::Microsoft::UI::Input::CharacterReceivedEventArgs const &args) - : m_tag(tag) { + winrt::Microsoft::UI::Input::CharacterReceivedEventArgs const &args, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source) + : m_tag(tag), m_source(source) { auto keyStatus = args.KeyStatus(); m_keyStatus.RepeatCount = keyStatus.RepeatCount; m_keyStatus.ScanCode = keyStatus.ScanCode; @@ -121,6 +135,10 @@ int32_t CharacterReceivedRoutedEventArgs::KeyCode() noexcept { winrt::Microsoft::UI::Input::PhysicalKeyStatus CharacterReceivedRoutedEventArgs::KeyStatus() noexcept { return m_keyStatus; } +winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource CharacterReceivedRoutedEventArgs::KeyboardSource() + const noexcept { + return m_source; +} Pointer::Pointer(winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType type, uint32_t id) : m_type(type), m_id(id) {} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h index 09c125b03eb..444750d1ddf 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Composition.Input.h @@ -21,9 +21,17 @@ struct KeyRoutedEventArgs : winrt::implements< KeyRoutedEventArgs, 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); + KeyRoutedEventArgs( + facebook::react::Tag tag, + uint32_t msg, + uint64_t wParam, + int64_t lParam, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source); #ifdef USE_WINUI3 - KeyRoutedEventArgs(facebook::react::Tag tag, winrt::Microsoft::UI::Input::KeyEventArgs const &args); + KeyRoutedEventArgs( + facebook::react::Tag tag, + winrt::Microsoft::UI::Input::KeyEventArgs const &args, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source); #endif int32_t OriginalSource() noexcept; @@ -33,12 +41,14 @@ struct KeyRoutedEventArgs : winrt::implements< winrt::Windows::System::VirtualKey Key() noexcept; winrt::Microsoft::UI::Input::PhysicalKeyStatus KeyStatus() noexcept; winrt::Windows::System::VirtualKey OriginalKey() noexcept; + winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource KeyboardSource() const noexcept; private: facebook::react::Tag m_tag{-1}; bool m_handled{false}; winrt::Windows::System::VirtualKey m_key; winrt::Microsoft::UI::Input::PhysicalKeyStatus m_keyStatus; + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource m_source; }; struct CharacterReceivedRoutedEventArgs @@ -46,11 +56,17 @@ struct CharacterReceivedRoutedEventArgs CharacterReceivedRoutedEventArgs, winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs, winrt::Microsoft::ReactNative::Composition::Input::RoutedEventArgs> { - CharacterReceivedRoutedEventArgs(facebook::react::Tag tag, uint32_t msg, uint64_t wParam, int64_t lParam); + CharacterReceivedRoutedEventArgs( + facebook::react::Tag tag, + uint32_t msg, + uint64_t wParam, + int64_t lParam, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source); #ifdef USE_WINUI3 CharacterReceivedRoutedEventArgs( facebook::react::Tag tag, - winrt::Microsoft::UI::Input::CharacterReceivedEventArgs const &args); + winrt::Microsoft::UI::Input::CharacterReceivedEventArgs const &args, + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source); #endif int32_t OriginalSource() noexcept; @@ -58,12 +74,14 @@ struct CharacterReceivedRoutedEventArgs void Handled(bool value) noexcept; int32_t KeyCode() noexcept; winrt::Microsoft::UI::Input::PhysicalKeyStatus KeyStatus() noexcept; + winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource KeyboardSource() const noexcept; private: facebook::react::Tag m_tag{-1}; bool m_handled{false}; int32_t m_keycode; winrt::Microsoft::UI::Input::PhysicalKeyStatus m_keyStatus; + const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource m_source; }; struct Pointer : PointerT { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp index 899fda372a4..886e2cc2d75 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionDynamicAutomationProvider.cpp @@ -313,6 +313,21 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPropertyValue(PROPERT hr = pRetVal->bstrVal != nullptr ? S_OK : E_OUTOFMEMORY; break; } + case UIA_PositionInSetPropertyId: { + pRetVal->vt = VT_I4; + pRetVal->lVal = props->accessibilityPosInSet; + break; + } + case UIA_SizeOfSetPropertyId: { + pRetVal->vt = VT_I4; + pRetVal->lVal = props->accessibilitySetSize; + break; + } + case UIA_LiveSettingPropertyId: { + pRetVal->vt = VT_I4; + pRetVal->lVal = GetLiveSetting(props->accessibilityLiveRegion); + break; + } } return hr; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp index deff7bb87ca..3acf0840507 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.cpp @@ -140,149 +140,170 @@ struct CompositionInputKeyboardSource : winrt::implements< CompositionEventHandler::CompositionEventHandler( const winrt::Microsoft::ReactNative::ReactContext &context, const winrt::Microsoft::ReactNative::ReactNativeIsland &reactNativeIsland) - : m_context(context), m_wkRootView(reactNativeIsland) { + : m_context(context), m_wkRootView(reactNativeIsland) {} + +void CompositionEventHandler::Initialize() noexcept { #ifdef USE_WINUI3 - if (auto island = reactNativeIsland.Island()) { + if (auto island = m_wkRootView.get().Island()) { auto pointerSource = winrt::Microsoft::UI::Input::InputPointerSource::GetForIsland(island); m_pointerPressedToken = - pointerSource.PointerPressed([this]( + pointerSource.PointerPressed([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto pp = winrt::make( - args.CurrentPoint(), strongRootView.ScaleFactor()); - onPointerPressed(pp, args.KeyModifiers()); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + strongThis->onPointerPressed(pp, args.KeyModifiers()); + } } }); m_pointerReleasedToken = - pointerSource.PointerReleased([this]( + pointerSource.PointerReleased([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto pp = winrt::make( - args.CurrentPoint(), strongRootView.ScaleFactor()); - onPointerReleased(pp, args.KeyModifiers()); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + strongThis->onPointerReleased(pp, args.KeyModifiers()); + } } }); - m_pointerMovedToken = pointerSource.PointerMoved([this]( + m_pointerMovedToken = pointerSource.PointerMoved([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto pp = winrt::make( - args.CurrentPoint(), strongRootView.ScaleFactor()); - onPointerMoved(pp, args.KeyModifiers()); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + strongThis->onPointerMoved(pp, args.KeyModifiers()); + } } }); m_pointerCaptureLostToken = - pointerSource.PointerCaptureLost([this]( + pointerSource.PointerCaptureLost([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto pp = winrt::make( - args.CurrentPoint(), strongRootView.ScaleFactor()); - onPointerCaptureLost(pp, args.KeyModifiers()); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + strongThis->onPointerCaptureLost(pp, args.KeyModifiers()); + } } }); m_pointerWheelChangedToken = - pointerSource.PointerWheelChanged([this]( + pointerSource.PointerWheelChanged([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputPointerSource const &, winrt::Microsoft::UI::Input::PointerEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto pp = winrt::make( - args.CurrentPoint(), strongRootView.ScaleFactor()); - onPointerWheelChanged(pp, args.KeyModifiers()); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto pp = winrt::make( + args.CurrentPoint(), strongRootView.ScaleFactor()); + strongThis->onPointerWheelChanged(pp, args.KeyModifiers()); + } } }); auto keyboardSource = winrt::Microsoft::UI::Input::InputKeyboardSource::GetForIsland(island); - m_keyDownToken = keyboardSource.KeyDown([this]( + m_keyDownToken = keyboardSource.KeyDown([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputKeyboardSource const &source, winrt::Microsoft::UI::Input::KeyEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto keyArgs = - winrt::make( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self( - strongRootView) - ->RootTag()), - args); - auto keyboardSource = winrt::make(source); - onKeyDown(keyboardSource, keyArgs); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent(); + auto keyboardSource = winrt::make(source); + auto keyArgs = + winrt::make( + focusedComponent + ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->RootTag()), + args, + keyboardSource); + strongThis->onKeyDown(keyArgs); + winrt::get_self(keyboardSource)->Disconnect(); + } } }); - m_keyUpToken = keyboardSource.KeyUp([this]( + m_keyUpToken = keyboardSource.KeyUp([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputKeyboardSource const &source, winrt::Microsoft::UI::Input::KeyEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto keyArgs = - winrt::make( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self( - strongRootView) - ->RootTag()), - args); - auto keyboardSource = winrt::make(source); - onKeyUp(keyboardSource, keyArgs); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent(); + auto keyboardSource = winrt::make(source); + auto keyArgs = + winrt::make( + focusedComponent + ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->RootTag()), + args, + keyboardSource); + strongThis->onKeyUp(keyArgs); + winrt::get_self(keyboardSource)->Disconnect(); + } } }); m_characterReceivedToken = - keyboardSource.CharacterReceived([this]( + keyboardSource.CharacterReceived([wkThis = weak_from_this()]( winrt::Microsoft::UI::Input::InputKeyboardSource const &source, winrt::Microsoft::UI::Input::CharacterReceivedEventArgs const &args) { - if (auto strongRootView = m_wkRootView.get()) { - if (SurfaceId() == -1) - return; - - auto focusedComponent = RootComponentView().GetFocusedComponent(); - auto charArgs = winrt::make< - winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( - focusedComponent - ? focusedComponent.Tag() - : static_cast( - winrt::get_self( - strongRootView) - ->RootTag()), - args); - auto keyboardSource = winrt::make(source); - onCharacterReceived(keyboardSource, charArgs); - winrt::get_self(keyboardSource)->Disconnect(); + if (auto strongThis = wkThis.lock()) { + if (auto strongRootView = strongThis->m_wkRootView.get()) { + if (strongThis->SurfaceId() == -1) + return; + + auto focusedComponent = strongThis->RootComponentView().GetFocusedComponent(); + auto keyboardSource = winrt::make(source); + auto charArgs = winrt::make< + winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( + focusedComponent + ? focusedComponent.Tag() + : static_cast( + winrt::get_self( + strongRootView) + ->RootTag()), + args, + keyboardSource); + strongThis->onCharacterReceived(charArgs); + winrt::get_self(keyboardSource)->Disconnect(); + } } }); } @@ -344,7 +365,8 @@ void CompositionEventHandler::onPointerWheelChanged( auto args = winrt::make( m_context, tag, pointerPoint, keyModifiers); - targetComponentView.OnPointerWheelChanged(args); + winrt::get_self(targetComponentView) + ->OnPointerWheelChanged(args); } } @@ -441,6 +463,7 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w case WM_SYSCHAR: { if (auto strongRootView = m_wkRootView.get()) { auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto keyboardSource = winrt::make(this); auto args = winrt::make< winrt::Microsoft::ReactNative::Composition::Input::implementation::CharacterReceivedRoutedEventArgs>( focusedComponent @@ -450,9 +473,9 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w ->RootTag()), msg, wParam, - lParam); - auto keyboardSource = winrt::make(this); - onCharacterReceived(keyboardSource, args); + lParam, + keyboardSource); + onCharacterReceived(args); winrt::get_self(keyboardSource)->Disconnect(); } break; @@ -463,6 +486,7 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w case WM_SYSKEYUP: { if (auto strongRootView = m_wkRootView.get()) { auto focusedComponent = RootComponentView().GetFocusedComponent(); + auto keyboardSource = winrt::make(this); auto args = winrt::make( focusedComponent ? focusedComponent.Tag() @@ -471,12 +495,12 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w ->RootTag()), msg, wParam, - lParam); - auto keyboardSource = winrt::make(this); + lParam, + keyboardSource); if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) { - onKeyDown(keyboardSource, args); + onKeyDown(args); } else { - onKeyUp(keyboardSource, args); + onKeyUp(args); } winrt::get_self(keyboardSource)->Disconnect(); } @@ -488,20 +512,19 @@ int64_t CompositionEventHandler::SendMessage(HWND hwnd, uint32_t msg, uint64_t w } void CompositionEventHandler::onKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { if (auto focusedComponent = RootComponentView().GetFocusedComponent()) { - focusedComponent.OnKeyDown(source, args); + winrt::get_self(focusedComponent)->OnKeyDown(args); if (args.Handled()) return; } bool fShift = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Shift) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Shift) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fCtrl = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; if (fShift && fCtrl && args.Key() == static_cast(VkKeyScanA('d')) && @@ -522,10 +545,9 @@ void CompositionEventHandler::onKeyDown( } void CompositionEventHandler::onKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { if (auto focusedComponent = RootComponentView().GetFocusedComponent()) { - focusedComponent.OnKeyUp(source, args); + winrt::get_self(focusedComponent)->OnKeyUp(args); if (args.Handled()) return; @@ -533,10 +555,10 @@ void CompositionEventHandler::onKeyUp( } void CompositionEventHandler::onCharacterReceived( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept { if (auto focusedComponent = RootComponentView().GetFocusedComponent()) { - focusedComponent.OnCharacterReceived(source, args); + winrt::get_self(focusedComponent) + ->OnCharacterReceived(args); if (args.Handled()) return; @@ -624,7 +646,8 @@ void CompositionEventHandler::HandleIncomingPointerEvent( auto args = winrt::make( m_context, componentView.Tag(), pointerPoint, keyModifiers); - componentView.OnPointerEntered(args); + winrt::get_self(componentView) + ->OnPointerEntered(args); if (shouldEmitEvent) { const auto eventEmitter = @@ -702,7 +725,7 @@ void CompositionEventHandler::HandleIncomingPointerEvent( auto args = winrt::make( m_context, componentView.Tag(), pointerPoint, keyModifiers); - componentView.OnPointerExited(args); + winrt::get_self(componentView)->OnPointerExited(args); } for (auto itComponentView = viewsToEmitJSLeaveEventsTo.rbegin(); itComponentView != viewsToEmitJSLeaveEventsTo.rend(); @@ -853,7 +876,8 @@ void CompositionEventHandler::onPointerMoved( m_context, tag, pointerPoint, keyModifiers); auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view; - targetComponentView.OnPointerMoved(args); + winrt::get_self(targetComponentView) + ->OnPointerMoved(args); auto targetView = FindClosestFabricManagedTouchableView( fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view); @@ -907,7 +931,8 @@ void CompositionEventHandler::onPointerPressed( auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(tag).view; auto args = winrt::make( m_context, tag, pointerPoint, keyModifiers); - targetComponentView.OnPointerPressed(args); + winrt::get_self(targetComponentView) + ->OnPointerPressed(args); ActiveTouch activeTouch{0}; activeTouch.touchType = UITouchType::Mouse; @@ -968,7 +993,8 @@ void CompositionEventHandler::onPointerReleased( auto args = winrt::make( m_context, tag, pointerPoint, keyModifiers); - targetComponentView.OnPointerReleased(args); + winrt::get_self(targetComponentView) + ->OnPointerReleased(args); UpdateActiveTouch(activeTouch->second, ptScaled, ptLocal); DispatchTouchEvent(TouchEventType::End, pointerId, pointerPoint, keyModifiers); @@ -987,7 +1013,8 @@ bool CompositionEventHandler::CapturePointer( auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_pointerCapturingComponentTag).view; - targetComponentView.OnPointerCaptureLost(); + winrt::get_self(targetComponentView) + ->OnPointerCaptureLost(); } } @@ -1018,7 +1045,8 @@ bool CompositionEventHandler::releasePointerCapture(PointerId pointerId, faceboo auto targetComponentView = fabricuiManager->GetViewRegistry().componentViewDescriptorWithTag(m_pointerCapturingComponentTag).view; - targetComponentView.OnPointerCaptureLost(); + winrt::get_self(targetComponentView) + ->OnPointerCaptureLost(); } if (m_capturedPointers.size() == 0) { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h index 656fb8c03b9..44a1c12b129 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionEventHandler.h @@ -27,13 +27,14 @@ struct FabricUIManager; struct winrt::Microsoft::ReactNative::implementation::ComponentView; typedef int PointerId; -class CompositionEventHandler { +class CompositionEventHandler : public std::enable_shared_from_this { public: CompositionEventHandler( const winrt::Microsoft::ReactNative::ReactContext &context, const winrt::Microsoft::ReactNative::ReactNativeIsland &ReactNativeIsland); virtual ~CompositionEventHandler(); + void Initialize() noexcept; int64_t SendMessage(HWND hwnd, uint32_t msg, uint64_t wParam, int64_t lParam) noexcept; void RemoveTouchHandlers(); winrt::Microsoft::UI::Input::VirtualKeyStates GetKeyState(winrt::Windows::System::VirtualKey key) noexcept; @@ -62,14 +63,9 @@ class CompositionEventHandler { void onPointerCaptureLost( const winrt::Microsoft::ReactNative::Composition::Input::PointerPoint &pointerPoint, winrt::Windows::System::VirtualKeyModifiers keyModifiers) noexcept; - void onKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; - void onKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; + void onKeyDown(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; + void onKeyUp(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept; void onCharacterReceived( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept; void getTargetPointerArgs( diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp index 061a388b5e6..66b1de9c3ab 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.cpp @@ -31,46 +31,12 @@ namespace winrt::Microsoft::ReactNative::Composition::implementation { -CreateCompositionComponentViewArgs::CreateCompositionComponentViewArgs( - const winrt::Microsoft::ReactNative::IReactContext &reactContext, - facebook::react::Tag tag, - const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compositionContext) - : base_type(reactContext, tag), m_compositionContext(compositionContext){}; - -winrt::Microsoft::UI::Composition::Compositor CreateCompositionComponentViewArgs::Compositor() const noexcept { - return winrt::Microsoft::ReactNative::Composition::CompositionUIService::GetCompositor(ReactContext().Properties()); -} - -winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext -CreateCompositionComponentViewArgs::CompositionContext() const noexcept { - return m_compositionContext; -} - -ComponentViewFeatures CreateCompositionComponentViewArgs::Features() const noexcept { - return m_features; -} - -void CreateCompositionComponentViewArgs::Features(ComponentViewFeatures value) noexcept { - m_features = value; -} - -ComponentView::ComponentView(const winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs &args) - : ComponentView( - winrt::get_self< - winrt::Microsoft::ReactNative::Composition::implementation::CreateCompositionComponentViewArgs>(args) - ->CompositionContext(), - args.Tag(), - args.ReactContext(), - args.Features(), - true) {} - ComponentView::ComponentView( const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, facebook::react::Tag tag, winrt::Microsoft::ReactNative::ReactContext const &reactContext, - ComponentViewFeatures flags, - bool customControl) - : base_type(tag, reactContext, customControl), m_compContext(compContext), m_flags(flags) { + ComponentViewFeatures flags) + : base_type(tag, reactContext), m_compContext(compContext), m_flags(flags) { m_outerVisual = compContext.CreateSpriteVisual(); // TODO could be a raw ContainerVisual if we had a // CreateContainerVisual in ICompositionContext m_focusVisual = compContext.CreateFocusVisual(); @@ -105,14 +71,18 @@ void ComponentView::onThemeChanged() noexcept { base_type::onThemeChanged(); - if (m_customComponent) { - // Review is it expected that I need this cast to call overridden methods? - winrt::Microsoft::ReactNative::Composition::ComponentView outer(*this); - outer.OnThemeChanged(); + if (m_themeChangedEvent) { + m_themeChangedEvent(*this, *this); } } -void ComponentView::OnThemeChanged() noexcept {} +winrt::event_token ComponentView::ThemeChanged( + winrt::Windows::Foundation::EventHandler const &handler) noexcept { + return m_themeChangedEvent.add(handler); +} +void ComponentView::ThemeChanged(winrt::event_token const &token) noexcept { + m_themeChangedEvent.remove(token); +} void ComponentView::Theme(const winrt::Microsoft::ReactNative::Composition::Theme &value) noexcept { theme(winrt::get_self(value)); @@ -170,10 +140,8 @@ void ComponentView::updateLayoutMetrics( updateBorderLayoutMetrics(layoutMetrics, *viewProps()); } - m_layoutMetrics = layoutMetrics; - UpdateCenterPropertySet(); - base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics); + UpdateCenterPropertySet(); } const facebook::react::LayoutMetrics &ComponentView::layoutMetrics() const noexcept { @@ -277,6 +245,7 @@ void ComponentView::StartBringIntoView( void ComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept { m_eventEmitter = std::static_pointer_cast(eventEmitter); + base_type::updateEventEmitter(eventEmitter); } void ComponentView::HandleCommand( @@ -294,7 +263,8 @@ void ComponentView::HandleCommand( } return; } - assert(false); // Unhandled command + + base_type::HandleCommand(commandName, args); } bool ComponentView::CapturePointer(const winrt::Microsoft::ReactNative::Composition::Input::Pointer &pointer) noexcept { @@ -1323,6 +1293,21 @@ void ComponentView::updateAccessibilityProps( winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty( m_uiaProvider, UIA_HelpTextPropertyId, oldViewProps.accessibilityHint, newViewProps.accessibilityHint); + + winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty( + m_uiaProvider, + UIA_PositionInSetPropertyId, + oldViewProps.accessibilityPosInSet, + newViewProps.accessibilityPosInSet); + + winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty( + m_uiaProvider, UIA_SizeOfSetPropertyId, oldViewProps.accessibilitySetSize, newViewProps.accessibilitySetSize); + + winrt::Microsoft::ReactNative::implementation::UpdateUiaProperty( + m_uiaProvider, + UIA_LiveSettingPropertyId, + oldViewProps.accessibilityLiveRegion, + newViewProps.accessibilityLiveRegion); } std::optional ComponentView::getAcccessiblityValue() noexcept { @@ -1483,18 +1468,6 @@ std::string ComponentView::DefaultHelpText() const noexcept { return ""; } -ViewComponentView::ViewComponentView( - const winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs &args) - : ViewComponentView( - ViewComponentView::defaultProps(), - winrt::get_self< - winrt::Microsoft::ReactNative::Composition::implementation::CreateCompositionComponentViewArgs>(args) - ->CompositionContext(), - args.Tag(), - args.ReactContext(), - args.Features(), - true) {} - facebook::react::SharedViewProps ViewComponentView::defaultProps() noexcept { static auto const defaultViewProps = std::make_shared(); return defaultViewProps; @@ -1505,33 +1478,42 @@ ViewComponentView::ViewComponentView( const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, facebook::react::Tag tag, winrt::Microsoft::ReactNative::ReactContext const &reactContext, - ComponentViewFeatures flags, - bool customComponent) - : base_type(compContext, tag, reactContext, flags, customComponent), + ComponentViewFeatures flags) + : base_type(compContext, tag, reactContext, flags), m_props(defaultProps ? defaultProps : ViewComponentView::defaultProps()) {} winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ViewComponentView::createVisual() noexcept { return m_compContext.CreateSpriteVisual(); } -winrt::Microsoft::UI::Composition::Visual ViewComponentView::CreateVisual() noexcept { - return winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::InnerVisual( - createVisual()); +void ViewComponentView::CreateVisualHandler( + const winrt::Microsoft::ReactNative::Composition::CreateVisualDelegate &handler) { + m_createVisualHandler = handler; +} + +winrt::Microsoft::ReactNative::Composition::CreateVisualDelegate ViewComponentView::CreateVisualHandler() + const noexcept { + return m_createVisualHandler; +} + +void ViewComponentView::CreateInternalVisualHandler( + const winrt::Microsoft::ReactNative::Composition::Experimental::CreateInternalVisualDelegate &handler) { + m_createInternalVisualHandler = handler; +} + +winrt::Microsoft::ReactNative::Composition::Experimental::CreateInternalVisualDelegate +ViewComponentView::CreateInternalVisualHandler() const noexcept { + return m_createInternalVisualHandler; } void ViewComponentView::ensureVisual() noexcept { if (!m_visual) { - if (m_customComponent) { - // Review is it expected that I need this cast to call overridden methods? - winrt::Microsoft::ReactNative::Composition::ViewComponentView outer(*this); - winrt::Microsoft::ReactNative::Composition::Experimental::IInternalCreateVisual internalCreateVisual{nullptr}; - if (outer.try_as(internalCreateVisual)) { - m_visual = internalCreateVisual.CreateInternalVisual(); - } else { - m_visual = - winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::CreateVisual( - outer.CreateVisual()); - } + if (m_createInternalVisualHandler) { + m_visual = m_createInternalVisualHandler(); + } else if (m_createVisualHandler) { + m_visual = + winrt::Microsoft::ReactNative::Composition::Experimental::MicrosoftCompositionContextHelper::CreateVisual( + m_createVisualHandler()); } else { m_visual = createVisual(); } @@ -1544,7 +1526,7 @@ winrt::Microsoft::ReactNative::ComponentView ViewComponentView::Create( facebook::react::Tag tag, winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept { return winrt::make( - ViewComponentView::defaultProps(), compContext, tag, reactContext, ComponentViewFeatures::Default, false); + ViewComponentView::defaultProps(), compContext, tag, reactContext, ComponentViewFeatures::Default); } void ViewComponentView::MountChildComponentView( @@ -1555,10 +1537,20 @@ void ViewComponentView::MountChildComponentView( indexOffsetForBorder(index); ensureVisual(); - // TODO if we get mixed children of composition and non-composition ComponentViews the indexes will get mixed up - // We could offset the index based on non-composition children in m_children if (auto compositionChild = childComponentView.try_as()) { - Visual().InsertAt(compositionChild->OuterVisual(), index); + auto visualIndex = index; + // Most of the time child index will align with visual index. + // But if we have non-visual children, we need to account for that. + if (m_hasNonVisualChildren) { + for (uint32_t i = 0; i <= index; i++) { + if (!m_children.GetAt(i).try_as()) { + visualIndex--; + } + } + } + Visual().InsertAt(compositionChild->OuterVisual(), visualIndex); + } else { + m_hasNonVisualChildren = true; } } @@ -1597,7 +1589,6 @@ void ViewComponentView::updateProps( const winrt::Microsoft::ReactNative::IComponentProps ViewComponentView::userProps( facebook::react::Props::Shared const &props) noexcept { - assert(m_customComponent); const auto &abiViewProps = *std::static_pointer_cast(props); return abiViewProps.UserProps(); } @@ -1663,22 +1654,21 @@ std::string CodeFromVirtualKey( } void ViewComponentView::OnKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { - auto eventCode = CodeFromVirtualKey(source, args.Key()); + auto eventCode = CodeFromVirtualKey(args.KeyboardSource(), args.Key()); bool fShift = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Shift) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Shift) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fAlt = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Menu) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Menu) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fCtrl = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fMeta = - ((source.GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) & + ((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) || - ((source.GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) & + ((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down); if (args.OriginalSource() == Tag() && !args.Handled()) { @@ -1691,7 +1681,7 @@ void ViewComponentView::OnKeyDown( event.key = ::Microsoft::ReactNative::FromVirtualKey( args.Key(), event.shiftKey, - !!((source.GetKeyState(winrt::Windows::System::VirtualKey::CapitalLock) & + !!((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::CapitalLock) & winrt::Microsoft::UI::Input::VirtualKeyStates::Locked) == winrt::Microsoft::UI::Input::VirtualKeyStates::Locked)); event.code = eventCode; @@ -1707,26 +1697,25 @@ void ViewComponentView::OnKeyDown( } } - base_type::OnKeyDown(source, args); + base_type::OnKeyDown(args); } void ViewComponentView::OnKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { - auto eventCode = CodeFromVirtualKey(source, args.Key()); + auto eventCode = CodeFromVirtualKey(args.KeyboardSource(), args.Key()); bool fShift = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Shift) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Shift) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fAlt = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Menu) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Menu) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fCtrl = - (source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; bool fMeta = - ((source.GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) & + ((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) || - ((source.GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) & + ((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down); if (args.OriginalSource() == Tag()) { @@ -1739,7 +1728,7 @@ void ViewComponentView::OnKeyUp( event.key = ::Microsoft::ReactNative::FromVirtualKey( args.Key(), event.shiftKey, - !!((source.GetKeyState(winrt::Windows::System::VirtualKey::CapitalLock) & + !!((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::CapitalLock) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down)); event.code = eventCode; @@ -1755,7 +1744,7 @@ void ViewComponentView::OnKeyUp( } } - base_type::OnKeyUp(source, args); + base_type::OnKeyUp(args); } void ViewComponentView::updateLayoutMetrics( @@ -1767,10 +1756,9 @@ void ViewComponentView::updateLayoutMetrics( } ensureVisual(); base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics); -} - -void ViewComponentView::UpdateLayoutMetrics(const LayoutMetrics &metrics, const LayoutMetrics &oldMetrics) noexcept { - Visual().Size({metrics.Frame.Width * metrics.PointScaleFactor, metrics.Frame.Height * metrics.PointScaleFactor}); + Visual().Size( + {layoutMetrics.frame.size.width * layoutMetrics.pointScaleFactor, + layoutMetrics.frame.size.height * layoutMetrics.pointScaleFactor}); } void ViewComponentView::prepareForRecycle() noexcept {} diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h index ef9927b1f76..83a814320a5 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/CompositionViewComponentView.h @@ -11,7 +11,6 @@ #include "CompositionHelpers.h" #include "Composition.ComponentView.g.h" -#include "Composition.CreateCompositionComponentViewArgs.g.h" #include "Composition.ViewComponentView.g.h" namespace Microsoft::ReactNative { @@ -20,39 +19,17 @@ struct CompContext; namespace winrt::Microsoft::ReactNative::Composition::implementation { -struct CreateCompositionComponentViewArgs - : public CreateCompositionComponentViewArgsT< - CreateCompositionComponentViewArgs, - winrt::Microsoft::ReactNative::implementation::CreateComponentViewArgs, - winrt::Microsoft::ReactNative::Composition::Experimental::IInternalCreateComponentViewArgs> { - CreateCompositionComponentViewArgs( - const winrt::Microsoft::ReactNative::IReactContext &reactContext, - facebook::react::Tag tag, - const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compositionContext); - - winrt::Microsoft::UI::Composition::Compositor Compositor() const noexcept; - - winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext CompositionContext() const noexcept; - - ComponentViewFeatures Features() const noexcept; - void Features(ComponentViewFeatures value) noexcept; - - private: - ComponentViewFeatures m_features{ComponentViewFeatures::Default}; - winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compositionContext; -}; - -struct ComponentView - : public ComponentViewT { +struct ComponentView : public ComponentViewT< + ComponentView, + winrt::Microsoft::ReactNative::implementation::ComponentView, + winrt::Microsoft::ReactNative::Composition::Experimental::IInternalComponentView> { static constexpr size_t SpecialBorderLayerCount = 8; - ComponentView(winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs const &args); ComponentView( const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, facebook::react::Tag tag, winrt::Microsoft::ReactNative::ReactContext const &reactContext, - ComponentViewFeatures flags, - bool customControl); + ComponentViewFeatures flags); virtual winrt::Microsoft::ReactNative::Composition::Experimental::IVisual Visual() const noexcept { return nullptr; @@ -129,9 +106,10 @@ struct ComponentView winrt::Microsoft::UI::Composition::Compositor Compositor() const noexcept; winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext CompositionContext() const noexcept; - // Publicaly overridable APIs void FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask updateMask) noexcept override; - virtual void OnThemeChanged() noexcept; + winrt::event_token ThemeChanged( + winrt::Windows::Foundation::EventHandler const &handler) noexcept; + void ThemeChanged(winrt::event_token const &token) noexcept; protected: bool anyHitTestHelper( @@ -143,7 +121,6 @@ struct ComponentView winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext; comp::CompositionPropertySet m_centerPropSet{nullptr}; facebook::react::SharedViewEventEmitter m_eventEmitter; - facebook::react::LayoutMetrics m_layoutMetrics; bool m_needsBorderUpdate{false}; bool m_hasTransformMatrixFacade{false}; bool m_enableFocusVisual{false}; @@ -177,10 +154,13 @@ struct ComponentView void showFocusVisual(bool show) noexcept; winrt::Microsoft::ReactNative::Composition::Experimental::IFocusVisual m_focusVisual{nullptr}; winrt::Microsoft::ReactNative::Composition::Experimental::IVisual m_outerVisual{nullptr}; + winrt::event> m_themeChangedEvent; }; -struct ViewComponentView : public ViewComponentViewT { - ViewComponentView(winrt::Microsoft::ReactNative::Composition::CreateCompositionComponentViewArgs const &args); +struct ViewComponentView : public ViewComponentViewT< + ViewComponentView, + ComponentView, + winrt::Microsoft::ReactNative::Composition::Experimental::IInternalCreateVisual> { [[nodiscard]] static winrt::Microsoft::ReactNative::ComponentView Create( const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, facebook::react::Tag tag, @@ -200,12 +180,8 @@ struct ViewComponentView : public ViewComponentViewT {}; - -struct ViewComponentView : ViewComponentViewT {}; - -} // namespace winrt::Microsoft::ReactNative::Composition::factory_implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp new file mode 100644 index 00000000000..84b10002bd8 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.cpp @@ -0,0 +1,133 @@ + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include "ContentIslandComponentView.h" + +#include +#include +#include +#include +#include +#include +#include "CompositionContextHelper.h" +#include "RootComponentView.h" + +#include "Composition.ContentIslandComponentView.g.cpp" + +namespace winrt::Microsoft::ReactNative::Composition::implementation { + +ContentIslandComponentView::ContentIslandComponentView( + const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, + facebook::react::Tag tag, + winrt::Microsoft::ReactNative::ReactContext const &reactContext) + : base_type(ViewComponentView::defaultProps(), compContext, tag, reactContext, ComponentViewFeatures::Default) { + m_mountedToken = Mounted([](const winrt::IInspectable &, const winrt::Microsoft::ReactNative::ComponentView &view) { + view.as()->OnMounted(); + }); + m_unmountedToken = + Unmounted([](const winrt::IInspectable &, const winrt::Microsoft::ReactNative::ComponentView &view) { + view.as()->OnUnmounted(); + }); +} + +void ContentIslandComponentView::OnMounted() noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + m_childContentLink = winrt::Microsoft::UI::Content::ChildContentLink::Create( + rootComponentView()->parentContentIsland(), + winrt::Microsoft::ReactNative::Composition::Experimental::CompositionContextHelper::InnerVisual(Visual()) + .as()); + m_childContentLink.ActualSize({m_layoutMetrics.frame.size.width, m_layoutMetrics.frame.size.height}); + if (m_islandToConnect) { + m_childContentLink.Connect(m_islandToConnect); + m_islandToConnect = nullptr; + } + + ParentLayoutChanged(); + auto view = Parent(); + while (view) { + m_layoutMetricChangedRevokers.push_back(view.LayoutMetricsChanged( + winrt::auto_revoke, + [wkThis = get_weak()]( + const winrt::IInspectable &sender, const winrt::Microsoft::ReactNative::LayoutMetricsChangedArgs &args) { + if (auto strongThis = wkThis.get()) { + strongThis->ParentLayoutChanged(); + } + })); + view = view.Parent(); + } +#endif +} + +void ContentIslandComponentView::OnUnmounted() noexcept { + m_layoutMetricChangedRevokers.clear(); +} + +void ContentIslandComponentView::ParentLayoutChanged() noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_layoutChangePosted) + return; + + m_layoutChangePosted = true; + ReactContext().UIDispatcher().Post([wkThis = get_weak()]() { + if (auto strongThis = wkThis.get()) { + auto clientRect = strongThis->getClientRect(); + + strongThis->m_childContentLink.OffsetOverride( + {static_cast(clientRect.left), static_cast(clientRect.top)}); + strongThis->m_layoutChangePosted = false; + } + }); +#endif +} + +ContentIslandComponentView::~ContentIslandComponentView() noexcept { + if (m_islandToConnect) { + m_islandToConnect.Close(); + } +} + +void ContentIslandComponentView::MountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView &childComponentView, + uint32_t index) noexcept { + assert(false); + base_type::MountChildComponentView(childComponentView, index); +} + +void ContentIslandComponentView::UnmountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView &childComponentView, + uint32_t index) noexcept { + assert(false); + base_type::UnmountChildComponentView(childComponentView, index); +} + +void ContentIslandComponentView::updateLayoutMetrics( + facebook::react::LayoutMetrics const &layoutMetrics, + facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_childContentLink) { + m_childContentLink.ActualSize({layoutMetrics.frame.size.width, layoutMetrics.frame.size.height}); + ParentLayoutChanged(); + } +#endif + base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics); +} + +void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::ContentIsland &contentIsland) noexcept { +#ifdef USE_EXPERIMENTAL_WINUI3 + if (m_childContentLink) { + m_islandToConnect = nullptr; + m_childContentLink.Connect(contentIsland); + } else { + m_islandToConnect = contentIsland; + } +#endif +} + +void ContentIslandComponentView::prepareForRecycle() noexcept { + Super::prepareForRecycle(); +} + +} // namespace winrt::Microsoft::ReactNative::Composition::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h new file mode 100644 index 00000000000..a0788b6316a --- /dev/null +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ContentIslandComponentView.h @@ -0,0 +1,60 @@ + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include + +#include +#include +#include "CompositionHelpers.h" +#include "CompositionViewComponentView.h" + +#pragma warning(push) +#pragma warning(disable : 4244 4305) +#include +#pragma warning(pop) +#include "Composition.ContentIslandComponentView.g.h" + +namespace winrt::Microsoft::ReactNative::Composition::implementation { + +struct ContentIslandComponentView : ContentIslandComponentViewT { + using Super = ContentIslandComponentViewT; + + void MountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView &childComponentView, + uint32_t index) noexcept override; + void UnmountChildComponentView( + const winrt::Microsoft::ReactNative::ComponentView &childComponentView, + uint32_t index) noexcept override; + void Connect(const winrt::Microsoft::UI::Content::ContentIsland &contentIsland) noexcept; + + void updateLayoutMetrics( + facebook::react::LayoutMetrics const &layoutMetrics, + facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept; + + void prepareForRecycle() noexcept override; + + ContentIslandComponentView( + const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, + facebook::react::Tag tag, + winrt::Microsoft::ReactNative::ReactContext const &reactContext); + ~ContentIslandComponentView() noexcept; + + private: + void OnMounted() noexcept; + void OnUnmounted() noexcept; + void ParentLayoutChanged() noexcept; + + bool m_layoutChangePosted{false}; + winrt::Microsoft::UI::Content::ContentIsland m_islandToConnect{nullptr}; + winrt::event_token m_mountedToken; + winrt::event_token m_unmountedToken; + std::vector m_layoutMetricChangedRevokers; +#ifdef USE_EXPERIMENTAL_WINUI3 + winrt::Microsoft::UI::Content::ChildContentLink m_childContentLink{nullptr}; +#endif +}; + +} // namespace winrt::Microsoft::ReactNative::Composition::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp index 8b6f41d2551..604d05f448a 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/DebuggingOverlayComponentView.cpp @@ -21,8 +21,7 @@ DebuggingOverlayComponentView::DebuggingOverlayComponentView( reactContext, ComponentViewFeatures::Default & ~(ComponentViewFeatures::Background | ComponentViewFeatures::ShadowProps | - ComponentViewFeatures::NativeBorder), - false) {} + ComponentViewFeatures::NativeBorder)) {} void DebuggingOverlayComponentView::MountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp index 250a81bc3d4..8916338ebfc 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ImageComponentView.cpp @@ -70,8 +70,7 @@ ImageComponentView::ImageComponentView( compContext, tag, reactContext, - ComponentViewFeatures::Default & ~ComponentViewFeatures::Background, - false) {} + ComponentViewFeatures::Default & ~ComponentViewFeatures::Background) {} void ImageComponentView::MountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, @@ -185,7 +184,6 @@ void ImageComponentView::prepareForRecycle() noexcept { winrt::Microsoft::ReactNative::ImageProps ImageComponentView::ImageProps() noexcept { // We do not currently support custom ImageComponentView's // If we did we would need to create a AbiImageProps and possibly return them here - assert(!m_customComponent); return winrt::make(viewProps()); } @@ -196,7 +194,6 @@ winrt::Microsoft::ReactNative::ImageProps ImageComponentView::ViewProps() noexce winrt::Microsoft::ReactNative::ViewProps ImageComponentView::ViewPropsInner() noexcept { // We do not currently support custom ImageComponentView's // If we did we would need to create a AbiImageProps and possibly return them here - assert(!m_customComponent); return winrt::make(viewProps()); } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp index d4fa0493b79..7abb166e3e2 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/Modal/WindowsModalHostViewComponentView.cpp @@ -27,8 +27,7 @@ WindowsModalHostComponentView::WindowsModalHostComponentView( compContext, tag, reactContext, - ComponentViewFeatures::Default & ~ComponentViewFeatures::Background, - false) { + ComponentViewFeatures::Default & ~ComponentViewFeatures::Background) { m_context = reactContext; // save context } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp index dab5dae3aa5..0f90896ccd8 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.cpp @@ -27,8 +27,7 @@ ParagraphComponentView::ParagraphComponentView( compContext, tag, reactContext, - ComponentViewFeatures::Default & ~ComponentViewFeatures::Background, - false) {} + ComponentViewFeatures::Default & ~ComponentViewFeatures::Background) {} void ParagraphComponentView::MountChildComponentView( const winrt::Microsoft::ReactNative::ComponentView &childComponentView, @@ -68,10 +67,6 @@ void ParagraphComponentView::updateProps( Super::updateProps(props, oldProps); } -void ParagraphComponentView::updateEventEmitter(facebook::react::EventEmitter::Shared const &eventEmitter) noexcept { - Super::updateEventEmitter(eventEmitter); -} - void ParagraphComponentView::updateState( facebook::react::State::Shared const &state, facebook::react::State::Shared const &oldState) noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h index 4e74704fa32..648b6e41a04 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ParagraphComponentView.h @@ -33,7 +33,6 @@ struct ParagraphComponentView : ParagraphComponentViewT +#include #include #include "DynamicWriter.h" #include "ReactHost/MsoUtils.h" @@ -46,16 +47,74 @@ LayoutHandler ReactCompositionViewComponentBuilder::LayoutHandler() const noexce return m_layoutHandler; } -void ReactCompositionViewComponentBuilder::SetCreateComponentView(ComponentViewFactory impl) noexcept { - m_createComponentView = impl; - assert(!m_createView); // Only SetCreateComponentView OR SetCreateViewComponentView should be called +void ReactCompositionViewComponentBuilder::InitializeComponentView( + const winrt::Microsoft::ReactNative::ComponentView &view) noexcept { + auto self = winrt::get_self(view); + self->MarkAsCustomComponent(); + if (m_customCommandHandler) + self->CustomCommandHandler(m_customCommandHandler); + if (m_finalizeUpdateHandler) + self->FinalizeUpdateHandler(m_finalizeUpdateHandler); + if (m_updatePropsHandler) + self->UpdatePropsHandler(m_updatePropsHandler); + if (m_updateStateHandler) + self->UpdateStateHandler(m_updateStateHandler); + if (m_updateEventEmitterHandler) + self->UpdateEventEmitterHandler(m_updateEventEmitterHandler); + if (m_mountChildComponentViewHandler) + self->MountChildComponentViewHandler(m_mountChildComponentViewHandler); + if (m_unmountChildComponentViewHandler) + self->UnmountChildComponentViewHandler(m_unmountChildComponentViewHandler); + if (m_createVisualHandler) + view.as()->CreateVisualHandler( + m_createVisualHandler); +} + +void ReactCompositionViewComponentBuilder::SetComponentViewInitializer( + const ComponentViewInitializer &initializer) noexcept { + m_fnCreateView = + [initializer](const IReactContext &reactContext, int32_t tag, const Experimental::ICompositionContext &context) { + auto view = winrt::make(tag, reactContext); + initializer(view); + return view; + }; + m_descriptorConstructorFactory = []() { + return &facebook::react::concreteComponentDescriptorConstructor<::Microsoft::ReactNative::AbiComponentDescriptor>; + }; +} + +void ReactCompositionViewComponentBuilder::SetViewComponentViewInitializer( + const ViewComponentViewInitializer &initializer) noexcept { + m_fnCreateView = + [initializer](const IReactContext &reactContext, int32_t tag, const Experimental::ICompositionContext &context) { + auto view = winrt::Microsoft::ReactNative::Composition::implementation::ViewComponentView::Create( + context, tag, reactContext) + .as(); + initializer(view); + return view; + }; + m_descriptorConstructorFactory = []() { + return &facebook::react::concreteComponentDescriptorConstructor< + ::Microsoft::ReactNative::AbiViewComponentDescriptor>; + }; +} + +void ReactCompositionViewComponentBuilder::SetContentIslandComponentViewInitializer( + const ComponentIslandComponentViewInitializer &initializer) noexcept { + m_fnCreateView = [initializer]( + const IReactContext &reactContext, int32_t tag, const Experimental::ICompositionContext &context) + -> winrt::Microsoft::ReactNative::Composition::ContentIslandComponentView { + auto view = winrt::make( + context, tag, reactContext); + initializer(view); + return view; + }; + m_descriptorConstructorFactory = []() { + return &facebook::react::concreteComponentDescriptorConstructor< + ::Microsoft::ReactNative::AbiViewComponentDescriptor>; + }; } -void ReactCompositionViewComponentBuilder::SetCreateViewComponentView( - CompositionViewComponentViewFactory impl) noexcept { - m_createView = impl; - assert(!m_createComponentView); // Only SetCreateComponentView OR SetCreateViewComponentView should be called -} // (Object handle, Microsoft.ReactNative.IComponentState state) => void // void ReactCompositionViewComponentBuilder::SetStateUpdater(StateUpdater impl) noexcept { // m_stateUpdater = impl; @@ -93,22 +152,53 @@ void ReactCompositionViewComponentBuilder::SetLayoutHandler( m_layoutHandler = impl; } +void ReactCompositionViewComponentBuilder::SetCustomCommandHandler(HandleCommandDelegate impl) noexcept { + m_customCommandHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetFinalizeUpdateHandler(UpdateFinalizerDelegate impl) noexcept { + m_finalizeUpdateHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetUpdatePropsHandler(UpdatePropsDelegate impl) noexcept { + m_updatePropsHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetUpdateStateHandler(UpdateStateDelegate impl) noexcept { + m_updateStateHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetUpdateEventEmitterHandler(UpdateEventEmitterDelegate impl) noexcept { + m_updateEventEmitterHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetMountChildComponentViewHandler( + MountChildComponentViewDelegate impl) noexcept { + m_mountChildComponentViewHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetUnmountChildComponentViewHandler( + UnmountChildComponentViewDelegate impl) noexcept { + m_unmountChildComponentViewHandler = impl; +} + +void ReactCompositionViewComponentBuilder::SetCreateVisualHandler(CreateVisualDelegate impl) noexcept { + m_createVisualHandler = impl; +} + winrt::Microsoft::ReactNative::ComponentView ReactCompositionViewComponentBuilder::CreateView( const IReactContext &reactContext, int32_t tag, const Experimental::ICompositionContext &context) noexcept { - if (m_createView) { - auto args = winrt::make(reactContext, tag, context); - return m_createView(args); - } else { - assert(m_createComponentView); - auto args = winrt::make(reactContext, tag); - return m_createComponentView(args); - } + assert(m_fnCreateView); + auto view = m_fnCreateView(reactContext, tag, context); + InitializeComponentView(view); + return view; } -bool ReactCompositionViewComponentBuilder::IsViewComponent() const noexcept { - return m_createView != nullptr; +facebook::react::ComponentDescriptorConstructor *ReactCompositionViewComponentBuilder::GetComponentDescriptorProvider() + const noexcept { + return m_descriptorConstructorFactory(); } } // namespace winrt::Microsoft::ReactNative::Composition diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h index 7978958bb3a..14e1a21ff11 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactCompositionViewComponentBuilder.h @@ -2,6 +2,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +#include +#include +#include #include #include "winrt/Microsoft.ReactNative.Composition.Experimental.h" #include "winrt/Microsoft.ReactNative.Composition.h" @@ -16,7 +19,7 @@ struct ReactCompositionViewComponentBuilder : winrt::implements< ReactCompositionViewComponentBuilder() noexcept; public: // IReactViewComponentBuilder - void SetCreateComponentView(ComponentViewFactory impl) noexcept; + void SetComponentViewInitializer(const ComponentViewInitializer &initializer) noexcept; void SetCreateProps(ViewPropsFactory impl) noexcept; // (Object handle, Microsoft.ReactNative.IComponentState state) => void @@ -28,8 +31,19 @@ struct ReactCompositionViewComponentBuilder : winrt::implements< void SetMeasureContentHandler(MeasureContentHandler impl) noexcept; void SetLayoutHandler(LayoutHandler impl) noexcept; + void SetCustomCommandHandler(HandleCommandDelegate impl) noexcept; + void SetFinalizeUpdateHandler(UpdateFinalizerDelegate impl) noexcept; + void SetUpdatePropsHandler(UpdatePropsDelegate impl) noexcept; + void SetUpdateStateHandler(UpdateStateDelegate impl) noexcept; + void SetUpdateEventEmitterHandler(UpdateEventEmitterDelegate impl) noexcept; + void SetMountChildComponentViewHandler(MountChildComponentViewDelegate impl) noexcept; + void SetUnmountChildComponentViewHandler(UnmountChildComponentViewDelegate impl) noexcept; + public: // Composition::IReactCompositionViewComponentBuilder - void SetCreateViewComponentView(CompositionViewComponentViewFactory impl) noexcept; + void SetViewComponentViewInitializer(const ViewComponentViewInitializer &initializer) noexcept; + void SetContentIslandComponentViewInitializer(const ComponentIslandComponentViewInitializer &initializer) noexcept; + + void SetCreateVisualHandler(CreateVisualDelegate impl) noexcept; public: IComponentProps CreateProps(ViewProps props) noexcept; @@ -39,7 +53,7 @@ struct ReactCompositionViewComponentBuilder : winrt::implements< winrt::Microsoft::ReactNative::IComponentProps props) noexcept; MeasureContentHandler MeasureContentHandler() const noexcept; LayoutHandler LayoutHandler() const noexcept; - bool IsViewComponent() const noexcept; + facebook::react::ComponentDescriptorConstructor *GetComponentDescriptorProvider() const noexcept; winrt::Microsoft::ReactNative::ComponentView CreateView( const IReactContext &reactContext, @@ -47,15 +61,29 @@ struct ReactCompositionViewComponentBuilder : winrt::implements< const Experimental::ICompositionContext &context) noexcept; private: + void InitializeComponentView(const winrt::Microsoft::ReactNative::ComponentView &view) noexcept; + ViewPropsFactory m_propsFactory; ViewShadowNodeFactory m_shadowNodeFactory; ViewShadowNodeCloner m_shadowNodeCloner; InitialStateDataFactory m_initialStateDataFactory; winrt::Microsoft::ReactNative::MeasureContentHandler m_measureContent; winrt::Microsoft::ReactNative::LayoutHandler m_layoutHandler; + std::function + m_fnCreateView; + std::function m_descriptorConstructorFactory; + winrt::Microsoft::ReactNative::HandleCommandDelegate m_customCommandHandler; + winrt::Microsoft::ReactNative::UpdateFinalizerDelegate m_finalizeUpdateHandler; + winrt::Microsoft::ReactNative::UpdatePropsDelegate m_updatePropsHandler; + winrt::Microsoft::ReactNative::UpdateStateDelegate m_updateStateHandler; + winrt::Microsoft::ReactNative::UpdateEventEmitterDelegate m_updateEventEmitterHandler; + winrt::Microsoft::ReactNative::MountChildComponentViewDelegate m_mountChildComponentViewHandler; + winrt::Microsoft::ReactNative::UnmountChildComponentViewDelegate m_unmountChildComponentViewHandler; - ComponentViewFactory m_createComponentView{nullptr}; - CompositionViewComponentViewFactory m_createView{nullptr}; + winrt::Microsoft::ReactNative::Composition::CreateVisualDelegate m_createVisualHandler; }; } // namespace winrt::Microsoft::ReactNative::Composition diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp index 92408749a0d..370beca2328 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.cpp @@ -143,9 +143,13 @@ ReactNativeIsland::ReactNativeIsland(const winrt::Microsoft::UI::Composition::Co ReactNativeIsland::~ReactNativeIsland() noexcept { #ifdef USE_WINUI3 - if (m_island && m_island.IsConnected()) { + if (m_island) { m_island.AutomationProviderRequested(m_islandAutomationProviderRequestedToken); m_island.StateChanged(m_islandStateChangedToken); +#ifdef USE_EXPERIMENTAL_WINUI3 + m_island.Connected(m_islandConnectedToken); + m_island.Disconnected(m_islandDisconnectedToken); +#endif } #endif @@ -199,6 +203,12 @@ void ReactNativeIsland::AddRenderedVisual( assert(!m_hasRenderedVisual); InternalRootVisual().InsertAt(visual, 0); m_hasRenderedVisual = true; + + if (m_mounted) { + if (auto componentView = GetComponentView()) { + componentView->onMounted(); + } + } } void ReactNativeIsland::RemoveRenderedVisual( @@ -398,6 +408,7 @@ void ReactNativeIsland::InitRootView( m_context = winrt::Microsoft::ReactNative::ReactContext(std::move(context)); m_reactViewOptions = std::move(viewOptions); m_CompositionEventHandler = std::make_shared<::Microsoft::ReactNative::CompositionEventHandler>(m_context, *this); + m_CompositionEventHandler->Initialize(); UpdateRootViewInternal(); @@ -752,13 +763,58 @@ winrt::Microsoft::UI::Content::ContentIsland ReactNativeIsland::Island() { if (args.DidRasterizationScaleChange()) { pThis->ScaleFactor(island.RasterizationScale()); } +#ifndef USE_EXPERIMENTAL_WINUI3 // Use this in place of Connected/Disconnected events for now. -- Its not quite what we + // want, but it will do for now. + if (args.DidSiteVisibleChange()) { + if (island.IsSiteVisible()) { + pThis->OnMounted(); + } else { + pThis->OnUnmounted(); + } + } +#endif } }); +#ifdef USE_EXPERIMENTAL_WINUI3 + m_islandConnectedToken = m_island.Connected( + [weakThis = get_weak()]( + winrt::IInspectable const &, winrt::Microsoft::UI::Content::ContentIsland const &island) { + if (auto pThis = weakThis.get()) { + pThis->OnMounted(); + } + }); + + m_islandDisconnectedToken = m_island.Disconnected( + [weakThis = get_weak()]( + winrt::IInspectable const &, winrt::Microsoft::UI::Content::ContentIsland const &island) { + if (auto pThis = weakThis.get()) { + pThis->OnUnmounted(); + } + }); +#endif } return m_island; } #endif +void ReactNativeIsland::OnMounted() noexcept { + if (m_mounted) + return; + m_mounted = true; + if (auto componentView = GetComponentView()) { + componentView->onMounted(); + } +} + +void ReactNativeIsland::OnUnmounted() noexcept { + if (!m_mounted) + return; + m_mounted = false; + if (auto componentView = GetComponentView()) { + componentView->onUnmounted(); + } +} + winrt::Microsoft::ReactNative::Composition::implementation::RootComponentView * ReactNativeIsland::GetComponentView() noexcept { if (!m_context || m_context.Handle().LoadingState() != winrt::Microsoft::ReactNative::LoadingState::Loaded || diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h index e0605d5a107..28c0de9c6d2 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ReactNativeIsland.h @@ -61,6 +61,9 @@ struct ReactNativeIsland winrt::Microsoft::ReactNative::Composition::Experimental::IVisual InternalRootVisual() noexcept; void InternalRootVisual(winrt::Microsoft::ReactNative::Composition::Experimental::IVisual const &value) noexcept; + void OnMounted() noexcept; + void OnUnmounted() noexcept; + // property Size winrt::Windows::Foundation::Size Size() noexcept; void Size(winrt::Windows::Foundation::Size value) noexcept; @@ -126,6 +129,8 @@ struct ReactNativeIsland winrt::event_token m_islandFrameworkClosedToken; winrt::event_token m_islandAutomationProviderRequestedToken; winrt::event_token m_islandStateChangedToken; + winrt::event_token m_islandConnectedToken; + winrt::event_token m_islandDisconnectedToken; #endif HWND m_hwnd{0}; @@ -133,6 +138,7 @@ struct ReactNativeIsland bool m_isJSViewAttached{false}; bool m_hasRenderedVisual{false}; bool m_showingLoadingUI{false}; + bool m_mounted{false}; IReactDispatcher m_uiDispatcher{nullptr}; winrt::IInspectable m_uiaProvider{nullptr}; int64_t m_rootTag{-1}; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp index 52c7f47380d..8e01addc5b2 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.cpp @@ -24,8 +24,7 @@ RootComponentView::RootComponentView( reactContext, ComponentViewFeatures::Default & ~(ComponentViewFeatures::Background | ComponentViewFeatures::ShadowProps | - ComponentViewFeatures::NativeBorder), - false) {} + ComponentViewFeatures::NativeBorder)) {} RootComponentView::~RootComponentView() { if (auto rootView = m_wkRootView.get()) { @@ -203,6 +202,13 @@ winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixel return winrt::get_self(view)->EnsureUiaProvider(); } +winrt::Microsoft::UI::Content::ContentIsland RootComponentView::parentContentIsland() noexcept { + if (auto rootView = m_wkRootView.get()) { + return winrt::get_self(rootView)->Island(); + } + return nullptr; +} + winrt::Microsoft::ReactNative::implementation::ClipState RootComponentView::getClipState() noexcept { return winrt::Microsoft::ReactNative::implementation::ClipState::NoClip; } diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h index eca7ddfb8a2..dbd08609766 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/RootComponentView.h @@ -36,6 +36,8 @@ struct RootComponentView : RootComponentViewT(m_outer.Theme()) - ->InternalPlatformBrush(L"ScrollBarTrackFill")); + if (auto outer = m_wkOuter.get()) { + m_trackVisual.Brush( + winrt::get_self(outer.Theme()) + ->InternalPlatformBrush(L"ScrollBarTrackFill")); + } } void ContentSize(winrt::Windows::Foundation::Size contentSize) noexcept { @@ -293,19 +295,21 @@ struct ScrollBarComponent { } void OnPointerReleased(const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) { - if (!m_visible) - return; - auto pt = args.GetCurrentPoint(m_outer.Tag()); - if (m_nTrackInputOffset != -1 && - pt.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse && - pt.Properties().PointerUpdateKind() == - winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::LeftButtonReleased) { - handleMoveThumb(args); - stopTrackingThumb(); - m_outer.ReleasePointerCapture(args.Pointer()); - - auto reg = HitTest(pt.Position()); - updateShy(reg == ScrollbarHitRegion::Unknown); + if (auto outer = m_wkOuter.get()) { + if (!m_visible) + return; + auto pt = args.GetCurrentPoint(outer.Tag()); + if (m_nTrackInputOffset != -1 && + pt.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse && + pt.Properties().PointerUpdateKind() == + winrt::Microsoft::ReactNative::Composition::Input::PointerUpdateKind::LeftButtonReleased) { + handleMoveThumb(args); + stopTrackingThumb(); + outer.ReleasePointerCapture(args.Pointer()); + + auto reg = HitTest(pt.Position()); + updateShy(reg == ScrollbarHitRegion::Unknown); + } } } @@ -318,78 +322,85 @@ struct ScrollBarComponent { } void handleMoveThumb(const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) { - auto pt = args.GetCurrentPoint(m_outer.Tag()); - auto pos = pt.Position(); - - auto newTrackingPosition = static_cast((m_vertical ? pos.Y : pos.X) * m_scaleFactor) - m_nTrackInputOffset; - winrt::get_self(m_outer)->scrollTo( - m_vertical ? winrt::Windows::Foundation::Numerics:: - float3{m_offset.x, scrollOffsetFromThumbPos(newTrackingPosition), m_offset.z} - : winrt::Windows::Foundation::Numerics:: - float3{scrollOffsetFromThumbPos(newTrackingPosition), m_offset.y, m_offset.z}, - false); + if (auto outer = m_wkOuter.get()) { + auto pt = args.GetCurrentPoint(outer.Tag()); + auto pos = pt.Position(); + + auto newTrackingPosition = static_cast((m_vertical ? pos.Y : pos.X) * m_scaleFactor) - m_nTrackInputOffset; + winrt::get_self(outer)->scrollTo( + m_vertical ? winrt::Windows::Foundation::Numerics:: + float3{m_offset.x, scrollOffsetFromThumbPos(newTrackingPosition), m_offset.z} + : winrt::Windows::Foundation::Numerics:: + float3{scrollOffsetFromThumbPos(newTrackingPosition), m_offset.y, m_offset.z}, + false); + } args.Handled(true); } void OnPointerPressed(const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) { - if (!m_visible) - return; - auto pt = args.GetCurrentPoint(m_outer.Tag()); - if (pt.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) { - auto pos = pt.Position(); - auto reg = HitTest(pos); + if (auto outer = m_wkOuter.get()) { + if (!m_visible) + return; + auto pt = args.GetCurrentPoint(outer.Tag()); + if (pt.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) { + auto pos = pt.Position(); + auto reg = HitTest(pos); - switch (reg) { - case ScrollbarHitRegion::ArrowFirst: - if (m_vertical) { - winrt::get_self(m_outer)->lineUp(false); - } else { - winrt::get_self(m_outer)->lineLeft(false); - } - args.Handled(true); - break; - case ScrollbarHitRegion::ArrowLast: - if (m_vertical) { - winrt::get_self(m_outer)->lineDown(false); - } else { - winrt::get_self(m_outer)->lineRight(false); - } - args.Handled(true); - break; - case ScrollbarHitRegion::PageUp: - if (m_vertical) { - winrt::get_self(m_outer)->pageUp(false); - } - args.Handled(true); - break; - case ScrollbarHitRegion::PageDown: - if (m_vertical) { - winrt::get_self(m_outer)->pageDown(false); + switch (reg) { + case ScrollbarHitRegion::ArrowFirst: + if (m_vertical) { + winrt::get_self(outer)->lineUp(false); + } else { + winrt::get_self(outer)->lineLeft(false); + } + args.Handled(true); + break; + case ScrollbarHitRegion::ArrowLast: + if (m_vertical) { + winrt::get_self(outer)->lineDown(false); + } else { + winrt::get_self(outer)->lineRight(false); + } + args.Handled(true); + break; + case ScrollbarHitRegion::PageUp: + if (m_vertical) { + winrt::get_self(outer)->pageUp(false); + } + args.Handled(true); + break; + case ScrollbarHitRegion::PageDown: + if (m_vertical) { + winrt::get_self(outer)->pageDown(false); + } + args.Handled(true); + break; + case ScrollbarHitRegion::Thumb: { + outer.CapturePointer(args.Pointer()); + m_nTrackInputOffset = static_cast((m_vertical ? pos.Y : pos.X) * m_scaleFactor) - m_thumbPos; + m_thumbVisual.AnimationClass( + winrt::Microsoft::ReactNative::Composition::Experimental::AnimationClass::None); + handleMoveThumb(args); } - args.Handled(true); - break; - case ScrollbarHitRegion::Thumb: { - m_outer.CapturePointer(args.Pointer()); - m_nTrackInputOffset = static_cast((m_vertical ? pos.Y : pos.X) * m_scaleFactor) - m_thumbPos; - m_thumbVisual.AnimationClass(winrt::Microsoft::ReactNative::Composition::Experimental::AnimationClass::None); - handleMoveThumb(args); } } } } void OnPointerMoved(const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) { - if (!m_visible) - return; - auto pt = args.GetCurrentPoint(m_outer.Tag()); - if (pt.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) { - if (m_nTrackInputOffset != -1) { - handleMoveThumb(args); - } else { - auto pos = pt.Position(); - auto reg = HitTest(pos); - updateShy(reg == ScrollbarHitRegion::Unknown); - setHighlightedRegion(reg); + if (auto outer = m_wkOuter.get()) { + if (!m_visible) + return; + auto pt = args.GetCurrentPoint(outer.Tag()); + if (pt.PointerDeviceType() == winrt::Microsoft::ReactNative::Composition::Input::PointerDeviceType::Mouse) { + if (m_nTrackInputOffset != -1) { + handleMoveThumb(args); + } else { + auto pos = pt.Position(); + auto reg = HitTest(pos); + updateShy(reg == ScrollbarHitRegion::Unknown); + setHighlightedRegion(reg); + } } } } @@ -427,122 +438,126 @@ struct ScrollBarComponent { // Renders the text into our composition surface void drawArrow(ScrollbarHitRegion region, bool disabled, bool hovered) noexcept { - auto &drawingSurface = - (region == ScrollbarHitRegion::ArrowFirst) ? m_arrowFirstDrawingSurface : m_arrowLastDrawingSurface; - if (!drawingSurface) { - drawingSurface = m_compContext.CreateDrawingSurfaceBrush( - {m_arrowSize, m_arrowSize}, - winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, - winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); - } + if (auto outer = m_wkOuter.get()) { + auto &drawingSurface = + (region == ScrollbarHitRegion::ArrowFirst) ? m_arrowFirstDrawingSurface : m_arrowLastDrawingSurface; + if (!drawingSurface) { + drawingSurface = m_compContext.CreateDrawingSurfaceBrush( + {m_arrowSize, m_arrowSize}, + winrt::Windows::Graphics::DirectX::DirectXPixelFormat::B8G8R8A8UIntNormalized, + winrt::Windows::Graphics::DirectX::DirectXAlphaMode::Premultiplied); + } - if (winrt::get_self(m_outer)->theme()->IsEmpty()) { - return; - } + if (winrt::get_self(outer)->theme()->IsEmpty()) { + return; + } - winrt::com_ptr spTextFormat; - winrt::check_hresult(::Microsoft::ReactNative::DWriteFactory()->CreateTextFormat( - L"Segoe Fluent Icons", - nullptr, // Font collection (nullptr sets it to use the system font collection). - DWRITE_FONT_WEIGHT_REGULAR, - DWRITE_FONT_STYLE_NORMAL, - DWRITE_FONT_STRETCH_NORMAL, - 8, // Xaml resource: ScrollBarButtonArrowIconFontSize - L"", - spTextFormat.put())); - - winrt::check_hresult(spTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); - - winrt::com_ptr spTextLayout; - winrt::check_hresult(::Microsoft::ReactNative::DWriteFactory()->CreateTextLayout( - m_vertical ? ((region == ScrollbarHitRegion::ArrowFirst) ? L"\uEDDB" : L"\uEDDC") - : ((region == ScrollbarHitRegion::ArrowFirst) ? L"\uEDD9" : L"\uEDDA"), - 1, // The length of the string. - spTextFormat.get(), // The text format to apply to the string (contains font information, etc). - (m_arrowSize / m_scaleFactor), // The width of the layout box. - (m_arrowSize / m_scaleFactor), // The height of the layout box. - spTextLayout.put() // The IDWriteTextLayout interface pointer. - )); - - POINT offset; - { - ::Microsoft::ReactNative::Composition::AutoDrawDrawingSurface autoDraw(drawingSurface, m_scaleFactor, &offset); - if (auto d2dDeviceContext = autoDraw.GetRenderTarget()) { - d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); - assert(d2dDeviceContext->GetUnitMode() == D2D1_UNIT_MODE_DIPS); - - // Create a solid color brush for the text. A more sophisticated application might want - // to cache and reuse a brush across all text elements instead, taking care to recreate - // it in the event of device removed. - winrt::com_ptr brush; - - D2D1::ColorF color{0}; - if (disabled) { - color = winrt::get_self(m_outer)->theme()->D2DPlatformColor( - "ScrollBarButtonArrowForegroundDisabled"); - } else if (hovered) { - color = winrt::get_self(m_outer)->theme()->D2DPlatformColor( - "ScrollBarButtonArrowForegroundPointerOver"); - } else { - color = winrt::get_self(m_outer)->theme()->D2DPlatformColor( - "ScrollBarButtonArrowForeground"); - } - winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush(color, brush.put())); + winrt::com_ptr spTextFormat; + winrt::check_hresult(::Microsoft::ReactNative::DWriteFactory()->CreateTextFormat( + L"Segoe Fluent Icons", + nullptr, // Font collection (nullptr sets it to use the system font collection). + DWRITE_FONT_WEIGHT_REGULAR, + DWRITE_FONT_STYLE_NORMAL, + DWRITE_FONT_STRETCH_NORMAL, + 8, // Xaml resource: ScrollBarButtonArrowIconFontSize + L"", + spTextFormat.put())); + + winrt::check_hresult(spTextFormat->SetTextAlignment(DWRITE_TEXT_ALIGNMENT_CENTER)); + + winrt::com_ptr spTextLayout; + winrt::check_hresult(::Microsoft::ReactNative::DWriteFactory()->CreateTextLayout( + m_vertical ? ((region == ScrollbarHitRegion::ArrowFirst) ? L"\uEDDB" : L"\uEDDC") + : ((region == ScrollbarHitRegion::ArrowFirst) ? L"\uEDD9" : L"\uEDDA"), + 1, // The length of the string. + spTextFormat.get(), // The text format to apply to the string (contains font information, etc). + (m_arrowSize / m_scaleFactor), // The width of the layout box. + (m_arrowSize / m_scaleFactor), // The height of the layout box. + spTextLayout.put() // The IDWriteTextLayout interface pointer. + )); + + POINT offset; + { + ::Microsoft::ReactNative::Composition::AutoDrawDrawingSurface autoDraw(drawingSurface, m_scaleFactor, &offset); + if (auto d2dDeviceContext = autoDraw.GetRenderTarget()) { + d2dDeviceContext->Clear(D2D1::ColorF(D2D1::ColorF::Black, 0.0f)); + assert(d2dDeviceContext->GetUnitMode() == D2D1_UNIT_MODE_DIPS); + + // Create a solid color brush for the text. A more sophisticated application might want + // to cache and reuse a brush across all text elements instead, taking care to recreate + // it in the event of device removed. + winrt::com_ptr brush; + + D2D1::ColorF color{0}; + if (disabled) { + color = winrt::get_self(outer)->theme()->D2DPlatformColor( + "ScrollBarButtonArrowForegroundDisabled"); + } else if (hovered) { + color = winrt::get_self(outer)->theme()->D2DPlatformColor( + "ScrollBarButtonArrowForegroundPointerOver"); + } else { + color = winrt::get_self(outer)->theme()->D2DPlatformColor( + "ScrollBarButtonArrowForeground"); + } + winrt::check_hresult(d2dDeviceContext->CreateSolidColorBrush(color, brush.put())); - { - DWRITE_TEXT_METRICS dtm{}; - winrt::check_hresult(spTextLayout->GetMetrics(&dtm)); - offset.y += static_cast((m_arrowSize - dtm.height) / 2.0f); - } + { + DWRITE_TEXT_METRICS dtm{}; + winrt::check_hresult(spTextLayout->GetMetrics(&dtm)); + offset.y += static_cast((m_arrowSize - dtm.height) / 2.0f); + } - // Draw the line of text at the specified offset, which corresponds to the top-left - // corner of our drawing surface. Notice we don't call BeginDraw on the D2D device - // context; this has already been done for us by the composition API. - d2dDeviceContext->DrawTextLayout( - D2D1::Point2F( - static_cast((offset.x) / m_scaleFactor), static_cast((offset.y) / m_scaleFactor)), - spTextLayout.get(), - brush.get(), - D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT); + // Draw the line of text at the specified offset, which corresponds to the top-left + // corner of our drawing surface. Notice we don't call BeginDraw on the D2D device + // context; this has already been done for us by the composition API. + d2dDeviceContext->DrawTextLayout( + D2D1::Point2F( + static_cast((offset.x) / m_scaleFactor), static_cast((offset.y) / m_scaleFactor)), + spTextLayout.get(), + brush.get(), + D2D1_DRAW_TEXT_OPTIONS_ENABLE_COLOR_FONT); + } + } + if (drawingSurface) { + drawingSurface.HorizontalAlignmentRatio(0.0f); + drawingSurface.VerticalAlignmentRatio(0.0f); + drawingSurface.Stretch(winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::None); } - } - if (drawingSurface) { - drawingSurface.HorizontalAlignmentRatio(0.0f); - drawingSurface.VerticalAlignmentRatio(0.0f); - drawingSurface.Stretch(winrt::Microsoft::ReactNative::Composition::Experimental::CompositionStretch::None); - } - auto &arrowVisual = (region == ScrollbarHitRegion::ArrowFirst) ? m_arrowVisualFirst : m_arrowVisualLast; - arrowVisual.Brush(drawingSurface); + auto &arrowVisual = (region == ScrollbarHitRegion::ArrowFirst) ? m_arrowVisualFirst : m_arrowVisualLast; + arrowVisual.Brush(drawingSurface); + } } void updateHighlight(ScrollbarHitRegion region) noexcept { - switch (region) { - case ScrollbarHitRegion::ArrowFirst: - case ScrollbarHitRegion::ArrowLast: { - auto disabled = !std::static_pointer_cast( - winrt::get_self(m_outer)->viewProps()) - ->scrollEnabled; - drawArrow(region, disabled, m_highlightedRegion == region); - } - case ScrollbarHitRegion::Thumb: { - if (!std::static_pointer_cast( - winrt::get_self(m_outer)->viewProps()) - ->scrollEnabled) { - m_thumbVisual.Brush( - winrt::get_self(m_outer.Theme())->InternalPlatformBrush(L"ScrollBarThumbFillDisabled")); - } else if (m_highlightedRegion == region) { - m_thumbVisual.Brush( - winrt::get_self(m_outer.Theme())->InternalPlatformBrush(L"ScrollBarThumbFillPointerOver")); - } else { - m_thumbVisual.Brush(winrt::get_self(m_outer.Theme())->InternalPlatformBrush(L"ScrollBarThumbFill")); + if (auto outer = m_wkOuter.get()) { + switch (region) { + case ScrollbarHitRegion::ArrowFirst: + case ScrollbarHitRegion::ArrowLast: { + auto disabled = !std::static_pointer_cast( + winrt::get_self(outer)->viewProps()) + ->scrollEnabled; + drawArrow(region, disabled, m_highlightedRegion == region); + } + case ScrollbarHitRegion::Thumb: { + if (!std::static_pointer_cast( + winrt::get_self(outer)->viewProps()) + ->scrollEnabled) { + m_thumbVisual.Brush( + winrt::get_self(outer.Theme())->InternalPlatformBrush(L"ScrollBarThumbFillDisabled")); + } else if (m_highlightedRegion == region) { + m_thumbVisual.Brush( + winrt::get_self(outer.Theme())->InternalPlatformBrush(L"ScrollBarThumbFillPointerOver")); + } else { + m_thumbVisual.Brush(winrt::get_self(outer.Theme())->InternalPlatformBrush(L"ScrollBarThumbFill")); + } } } } } private: - winrt::Microsoft::ReactNative::Composition::ScrollViewComponentView m_outer; + winrt::weak_ref m_wkOuter; winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext m_compContext; winrt::Microsoft::ReactNative::ReactContext m_reactContext; const bool m_vertical; @@ -593,8 +608,7 @@ ScrollViewComponentView::ScrollViewComponentView( compContext, tag, reactContext, - ComponentViewFeatures::Default & ~ComponentViewFeatures::Background, - false) { + ComponentViewFeatures::Default & ~ComponentViewFeatures::Background) { // m_element.Content(m_contentPanel); /* @@ -895,7 +909,6 @@ void ScrollViewComponentView::OnPointerCaptureLost() noexcept { } void ScrollViewComponentView::OnKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { switch (args.Key()) { case winrt::Windows::System::VirtualKey::End: @@ -924,7 +937,7 @@ void ScrollViewComponentView::OnKeyDown( break; } - base_type::OnKeyDown(source, args); + base_type::OnKeyDown(args); } bool ScrollViewComponentView::scrollToEnd(bool animate) noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h index 15039a970dc..dc294a4d9c8 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/ScrollViewComponentView.h @@ -72,9 +72,7 @@ struct ScrollInteractionTrackerOwner : public winrt::implements< facebook::react::LayoutMetrics const &layoutMetrics, facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept override; void prepareForRecycle() noexcept override; - void OnKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override; + void OnKeyDown(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override; void HandleCommand(winrt::hstring commandName, const winrt::Microsoft::ReactNative::IJSValueReader &args) noexcept override; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp index 50498d641e4..98aa7d95776 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.cpp @@ -33,8 +33,7 @@ SwitchComponentView::SwitchComponentView( compContext, tag, reactContext, - ComponentViewFeatures::Default & ~ComponentViewFeatures::Background, - false) {} + ComponentViewFeatures::Default & ~ComponentViewFeatures::Background) {} winrt::Microsoft::ReactNative::ComponentView SwitchComponentView::Create( const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext, @@ -304,14 +303,13 @@ void SwitchComponentView::OnPointerExited( } void SwitchComponentView::OnKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { if (args.Key() == winrt::Windows::System::VirtualKey::Space) { if (toggle()) { args.Handled(true); } } - Super::OnKeyUp(source, args); + Super::OnKeyUp(args); } bool SwitchComponentView::toggle() noexcept { diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h index c5918626513..f696ff757b8 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/SwitchComponentView.h @@ -38,9 +38,7 @@ struct SwitchComponentView : SwitchComponentViewT(args.Key()); LPARAM lParam = 0; @@ -776,16 +774,15 @@ void WindowsTextInputComponentView::OnKeyDown( } } - Super::OnKeyDown(source, args); + Super::OnKeyDown(args); } void WindowsTextInputComponentView::OnKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept { // Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with WinUI // behavior We do forward Ctrl+Tab to the textinput. if (args.Key() != winrt::Windows::System::VirtualKey::Tab || - (source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down) { WPARAM wParam = static_cast(args.Key()); LPARAM lParam = 1; @@ -807,11 +804,10 @@ void WindowsTextInputComponentView::OnKeyUp( } } - Super::OnKeyUp(source, args); + Super::OnKeyUp(args); } bool WindowsTextInputComponentView::ShouldSubmit( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept { bool shouldSubmit = true; @@ -824,19 +820,19 @@ bool WindowsTextInputComponentView::ShouldSubmit( // If 'submitKeyEvents' are supplied, use them to determine whether to emit onSubmitEditing' for either // single-line or multi-line TextInput if (args.KeyCode() == '\r') { - bool shiftDown = (source.GetKeyState(winrt::Windows::System::VirtualKey::Shift) & + bool shiftDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Shift) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; - bool ctrlDown = (source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + bool ctrlDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; - bool altDown = (source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + bool altDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; - bool metaDown = (source.GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) & + bool metaDown = (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::LeftWindows) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down || - (source.GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) & + (args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::RightWindows) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) == winrt::Microsoft::UI::Input::VirtualKeyStates::Down; return (submitKeyEvent.shiftKey && shiftDown) || (submitKeyEvent.ctrlKey && ctrlDown) || @@ -854,18 +850,17 @@ bool WindowsTextInputComponentView::ShouldSubmit( } void WindowsTextInputComponentView::OnCharacterReceived( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept { // Do not forward tab keys into the TextInput, since we want that to do the tab loop instead. This aligns with WinUI // behavior We do forward Ctrl+Tab to the textinput. if ((args.KeyCode() == '\t') && - ((source.GetKeyState(winrt::Windows::System::VirtualKey::Control) & + ((args.KeyboardSource().GetKeyState(winrt::Windows::System::VirtualKey::Control) & winrt::Microsoft::UI::Input::VirtualKeyStates::Down) != winrt::Microsoft::UI::Input::VirtualKeyStates::Down)) { return; } // Logic for submit events - if (ShouldSubmit(source, args)) { + if (ShouldSubmit(args)) { // call onSubmitEditing event if (m_eventEmitter && !m_comingFromJS) { auto emitter = std::static_pointer_cast(m_eventEmitter); diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h index ee0aef6e434..a459e013684 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/TextInput/WindowsTextInputComponentView.h @@ -62,16 +62,10 @@ struct WindowsTextInputComponentView void OnPointerMoved( const winrt::Microsoft::ReactNative::Composition::Input::PointerRoutedEventArgs &args) noexcept override; - void OnKeyDown( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override; - void OnKeyUp( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override; - void OnCharacterReceived( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, - const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept - override; + void OnKeyDown(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override; + void OnKeyUp(const winrt::Microsoft::ReactNative::Composition::Input::KeyRoutedEventArgs &args) noexcept override; + void OnCharacterReceived(const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs + &args) noexcept override; std::optional getAcccessiblityValue() noexcept override; void setAcccessiblityValue(std::string &&value) noexcept override; @@ -106,7 +100,6 @@ struct WindowsTextInputComponentView const facebook::react::SharedColor &cursorColor, const facebook::react::SharedColor &foregroundColor) noexcept; bool ShouldSubmit( - const winrt::Microsoft::ReactNative::Composition::Input::KeyboardSource &source, const winrt::Microsoft::ReactNative::Composition::Input::CharacterReceivedRoutedEventArgs &args) noexcept; winrt::Windows::UI::Composition::CompositionSurfaceBrush m_brush{nullptr}; diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp index c2187cf114a..ed4a6248da7 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.cpp @@ -162,4 +162,23 @@ void UpdateUiaProperty( spProviderSimple.get(), propId, CComVariant(oldValue.c_str()), CComVariant(newValue.c_str())); } +long GetLiveSetting(const std::string &liveRegion) noexcept { + if (liveRegion == "polite") { + return LiveSetting::Polite; + } else if (liveRegion == "assertive") { + return LiveSetting::Assertive; + } + return LiveSetting::Off; +} + +std::string extractAccessibilityValue(const facebook::react::AccessibilityValue &value) noexcept { + if (value.now.has_value()) { + return std::to_string(value.now.value()); + } else if (value.text.has_value()) { + return value.text.value(); + } else { + return ""; + } +} + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h index 2350b164bee..c5a5a109f80 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UiaHelpers.h @@ -29,4 +29,8 @@ void UpdateUiaProperty( const std::string &oldValue, const std::string &newValue) noexcept; +long GetLiveSetting(const std::string &liveRegion) noexcept; + +std::string extractAccessibilityValue(const facebook::react::AccessibilityValue &value) noexcept; + } // namespace winrt::Microsoft::ReactNative::implementation diff --git a/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp b/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp index 254810b964c..40d6c47f887 100644 --- a/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/Composition/UnimplementedNativeViewComponentView.cpp @@ -24,8 +24,7 @@ UnimplementedNativeViewComponentView::UnimplementedNativeViewComponentView( reactContext, ComponentViewFeatures::Default & ~(ComponentViewFeatures::Background | ComponentViewFeatures::ShadowProps | - ComponentViewFeatures::NativeBorder), - false) { + ComponentViewFeatures::NativeBorder)) { m_labelVisual = compContext.CreateSpriteVisual(); OuterVisual().InsertAt(m_labelVisual, 1); } diff --git a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp index 41fb4f29baa..12b58e90a9b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/FabricUIManagerModule.cpp @@ -257,9 +257,10 @@ void FabricUIManager::RCTPerformMountInstructions( newChildComponentView->updateEventEmitter(newChildShadowView.eventEmitter); newChildComponentView->updateState(newChildShadowView.state, oldChildShadowView.state); newChildComponentView->updateLayoutMetrics(newChildShadowView.layoutMetrics, oldChildShadowView.layoutMetrics); - newChildViewDescriptor.view.FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask::All); + newChildComponentView->FinalizeUpdates(winrt::Microsoft::ReactNative::ComponentViewUpdateMask::All); - parentViewDescriptor.view.MountChildComponentView(*newChildComponentView, mutation.index); + winrt::get_self(parentViewDescriptor.view) + ->MountChildComponentView(*newChildComponentView, mutation.index); break; } @@ -268,7 +269,8 @@ void FabricUIManager::RCTPerformMountInstructions( auto &parentShadowView = mutation.parentShadowView; auto &oldChildViewDescriptor = m_registry.componentViewDescriptorWithTag(oldChildShadowView.tag); auto &parentViewDescriptor = m_registry.componentViewDescriptorWithTag(parentShadowView.tag); - parentViewDescriptor.view.UnmountChildComponentView(oldChildViewDescriptor.view, mutation.index); + winrt::get_self(parentViewDescriptor.view) + ->UnmountChildComponentView(oldChildViewDescriptor.view, mutation.index); break; } @@ -302,7 +304,8 @@ void FabricUIManager::RCTPerformMountInstructions( } if (mask != winrt::Microsoft::ReactNative::ComponentViewUpdateMask::None) { - newChildViewDescriptor.view.FinalizeUpdates(mask); + winrt::get_self(newChildViewDescriptor.view) + ->FinalizeUpdates(mask); } break; @@ -387,15 +390,15 @@ void FabricUIManager::schedulerDidDispatchCommand( folly::dynamic const &arg) { if (m_context.UIDispatcher().HasThreadAccess()) { auto descriptor = m_registry.componentViewDescriptorWithTag(shadowView.tag); - descriptor.view.HandleCommand( - winrt::to_hstring(commandName), winrt::make(arg)); + winrt::get_self(descriptor.view) + ->HandleCommand(winrt::to_hstring(commandName), winrt::make(arg)); } else { m_context.UIDispatcher().Post( [wkThis = weak_from_this(), commandName, tag = shadowView.tag, args = folly::dynamic(arg)]() { if (auto pThis = wkThis.lock()) { auto view = pThis->m_registry.findComponentViewWithTag(tag); if (view) { - view.HandleCommand( + winrt::get_self(view)->HandleCommand( winrt::to_hstring(commandName), winrt::make(args)); } } diff --git a/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp b/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp index d377d6e828d..c7947ac85d6 100644 --- a/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/WindowsComponentDescriptorRegistry.cpp @@ -74,9 +74,7 @@ void WindowsComponentDescriptorRegistry::Add( m_descriptorFlavors.back()->c_str(), std::static_pointer_cast(m_descriptorFlavors.back()), winrt::get_self(builder) - ->IsViewComponent() - ? &facebook::react::concreteComponentDescriptorConstructor - : &facebook::react::concreteComponentDescriptorConstructor}); + ->GetComponentDescriptorProvider()}); } winrt::Microsoft::ReactNative::IReactViewComponentBuilder WindowsComponentDescriptorRegistry::GetDescriptor( diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp index 0f7cc3b3aae..7e4638224cc 100644 --- a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp +++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.cpp @@ -26,7 +26,23 @@ HostPlatformViewProps::HostPlatformViewProps( focusable( CoreFeatures::enablePropIteratorSetter ? sourceProps.focusable - : convertRawProp(context, rawProps, "focusable", sourceProps.focusable, {})) {} + : convertRawProp(context, rawProps, "focusable", sourceProps.focusable, {})), + accessibilityPosInSet( + CoreFeatures::enablePropIteratorSetter + ? sourceProps.accessibilityPosInSet + : convertRawProp(context, rawProps, "accessibilityPosInSet", sourceProps.accessibilityPosInSet, 0)), + accessibilitySetSize( + CoreFeatures::enablePropIteratorSetter + ? sourceProps.accessibilitySetSize + : convertRawProp(context, rawProps, "accessibilitySetSize", sourceProps.accessibilitySetSize, 0)), + accessibilityLiveRegion( + CoreFeatures::enablePropIteratorSetter ? sourceProps.accessibilityLiveRegion + : convertRawProp( + context, + rawProps, + "accessibilityLiveRegion", + sourceProps.accessibilityLiveRegion, + "none")) {} #define WINDOWS_VIEW_EVENT_CASE(eventType) \ case CONSTEXPR_RAW_PROPS_KEY_HASH("on" #eventType): { \ @@ -61,6 +77,9 @@ void HostPlatformViewProps::setProp( WINDOWS_VIEW_EVENT_CASE(MouseLeave); RAW_SET_PROP_SWITCH_CASE_BASIC(enableFocusRing); RAW_SET_PROP_SWITCH_CASE_BASIC(focusable); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityPosInSet); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilitySetSize); + RAW_SET_PROP_SWITCH_CASE_BASIC(accessibilityLiveRegion); RAW_SET_PROP_SWITCH_CASE_BASIC(keyDownEvents); RAW_SET_PROP_SWITCH_CASE_BASIC(keyUpEvents); } diff --git a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h index aa0e59ca697..b2fce99065b 100644 --- a/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h +++ b/vnext/Microsoft.ReactNative/Fabric/platform/react/renderer/components/view/HostPlatformViewProps.h @@ -24,6 +24,9 @@ class HostPlatformViewProps : public BaseViewProps { WindowsViewEvents windowsEvents{}; bool enableFocusRing{true}; bool focusable{false}; + int accessibilityPosInSet{0}; + int accessibilitySetSize{0}; + std::string accessibilityLiveRegion{"none"}; // std::optional overflowAnchor{}; // std::optional tooltip{}; diff --git a/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl b/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl index 242a04af8b2..fbb406bbf6b 100644 --- a/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl +++ b/vnext/Microsoft.ReactNative/IReactCompositionViewComponentBuilder.idl @@ -13,16 +13,26 @@ import "CompositionComponentView.idl"; namespace Microsoft.ReactNative.Composition { + [webhosthidden] + [experimental] + delegate void ViewComponentViewInitializer(ViewComponentView view); + + [webhosthidden] [experimental] - DOC_STRING("Provides a factory method to create an instance of a ViewComponentView. See @IReactCompositionViewComponentBuilder.SetCreateViewComponentView") - delegate Microsoft.ReactNative.Composition.ViewComponentView CompositionViewComponentViewFactory(CreateCompositionComponentViewArgs args); + delegate void ComponentIslandComponentViewInitializer(ContentIslandComponentView view); + + [experimental] + [webhosthidden] + delegate Microsoft.UI.Composition.Visual CreateVisualDelegate(); [webhosthidden] [experimental] DOC_STRING(".") interface IReactCompositionViewComponentBuilder { - void SetCreateViewComponentView(CompositionViewComponentViewFactory impl); + void SetViewComponentViewInitializer(ViewComponentViewInitializer initializer); + void SetContentIslandComponentViewInitializer(ComponentIslandComponentViewInitializer initializer); + void SetCreateVisualHandler(CreateVisualDelegate impl); }; } // namespace Microsoft.ReactNative diff --git a/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl b/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl index a55fd561995..33617e4eec5 100644 --- a/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl +++ b/vnext/Microsoft.ReactNative/IReactViewComponentBuilder.idl @@ -3,6 +3,7 @@ import "ViewProps.idl"; import "ComponentView.idl"; +import "IJSValueWriter.idl"; #include "DocString.h" @@ -34,6 +35,18 @@ namespace Microsoft.ReactNative LayoutDirection LayoutDirection; }; + [experimental] + runtimeclass MountChildComponentViewArgs { + ComponentView Child { get; }; + UInt32 Index { get; }; + }; + + [experimental] + runtimeclass UnmountChildComponentViewArgs { + ComponentView Child { get; }; + UInt32 Index { get; }; + }; + [experimental] DOC_STRING("A delegate that creates a @IComponentProps object for an instance of @ViewProps. See @IReactViewComponentBuilder.SetCreateProps") delegate IComponentProps ViewPropsFactory(ViewProps props); @@ -54,15 +67,39 @@ namespace Microsoft.ReactNative delegate Object InitialStateDataFactory(IComponentProps props); [experimental] - DOC_STRING("Provides a factory method to create an instance of a ComponentView. See @IReactViewComponentBuilder.SetCreateView") - delegate ComponentView ComponentViewFactory(CreateComponentViewArgs args); + DOC_STRING("Provides a method to initialize an instance of a ComponentView. See @IReactViewComponentBuilder.SetComponentViewInitializer") + delegate void ComponentViewInitializer(ComponentView view); + + [experimental] + delegate void HandleCommandDelegate(ComponentView source, String commandName, IJSValueReader args); + + [experimental] + delegate void UpdateFinalizerDelegate(ComponentView source, ComponentViewUpdateMask updateMask); + + [experimental] + delegate void UpdatePropsDelegate(ComponentView source, IComponentProps newProps, IComponentProps oldProps); + + [experimental] + delegate void UpdateStateDelegate(ComponentView source, IComponentState newState); + + [experimental] + delegate void MountChildComponentViewDelegate(ComponentView source, MountChildComponentViewArgs args); + + [experimental] + delegate void UnmountChildComponentViewDelegate(ComponentView source, UnmountChildComponentViewArgs args); + + [experimental] + runtimeclass EventEmitter { + void DispatchEvent(String eventName, JSValueArgWriter args); + }; + + [experimental] + delegate void UpdateEventEmitterDelegate(ComponentView source, EventEmitter eventEmitter); [webhosthidden] [experimental] interface IReactViewComponentBuilder { - void SetCreateComponentView(ComponentViewFactory impl); - DOC_STRING("Create an implementation of your custom Props type that will be passed to your components @Composition.ICompositionViewComponent.UpdateProps method.") void SetCreateProps(ViewPropsFactory impl); @@ -71,9 +108,24 @@ namespace Microsoft.ReactNative void SetInitialStateDataFactory(InitialStateDataFactory impl); void SetMeasureContentHandler(MeasureContentHandler impl); void SetLayoutHandler(LayoutHandler impl); + + void SetComponentViewInitializer(ComponentViewInitializer initializer); + void SetCustomCommandHandler(HandleCommandDelegate impl); + void SetFinalizeUpdateHandler(UpdateFinalizerDelegate impl); + void SetUpdatePropsHandler(UpdatePropsDelegate impl); + void SetUpdateStateHandler(UpdateStateDelegate impl); + void SetUpdateEventEmitterHandler(UpdateEventEmitterDelegate impl); + void SetMountChildComponentViewHandler(MountChildComponentViewDelegate impl); + void SetUnmountChildComponentViewHandler(UnmountChildComponentViewDelegate impl); }; + // [exclusiveto(ShadowNode)] + // [uuid(BF2A2A64-AB8B-47FC-BE69-E31DE6FC29A4)] + // interface IShadowNodeFactory + // { + // } + // [composable(IShadowNodeFactory, protected)] [webhosthidden] [experimental] unsealed runtimeclass ShadowNode @@ -81,6 +133,7 @@ namespace Microsoft.ReactNative void EnsureUnsealed(); Object Tag { get; set; }; Object StateData{ get; set; }; + EventEmitter EventEmitter { get; }; }; [webhosthidden] diff --git a/vnext/Microsoft.ReactNative/ReactNativeIsland.idl b/vnext/Microsoft.ReactNative/ReactNativeIsland.idl index 023d7211593..4df3ecd227d 100644 --- a/vnext/Microsoft.ReactNative/ReactNativeIsland.idl +++ b/vnext/Microsoft.ReactNative/ReactNativeIsland.idl @@ -59,6 +59,9 @@ namespace Microsoft.ReactNative DOC_STRING("Forward input to the RootView. Only required when not using ContentIslands") Int64 SendMessage(UInt32 Msg, UInt64 WParam, Int64 LParam); + + void OnMounted(); + void OnUnmounted(); } } diff --git a/vnext/PropertySheets/WinUI.props b/vnext/PropertySheets/WinUI.props index e2132d41d65..6901ffd984b 100644 --- a/vnext/PropertySheets/WinUI.props +++ b/vnext/PropertySheets/WinUI.props @@ -7,7 +7,7 @@ For local testing of internal versions, modify the WinUI3Version, and comment out the addition nuget source in NuGet.Config --> - 1.5.240124002-experimental2 + 1.6.240701003-experimental2 1.5.240227000 diff --git a/vnext/Shared/Shared.vcxitems b/vnext/Shared/Shared.vcxitems index b8eb0c76fe4..0b6c29f9333 100644 --- a/vnext/Shared/Shared.vcxitems +++ b/vnext/Shared/Shared.vcxitems @@ -89,6 +89,10 @@ $(ReactNativeWindowsDir)Microsoft.ReactNative\UriImageManager.idl Code + + true + $(ReactNativeWindowsDir)Microsoft.ReactNative\CompositionComponentView.idl + true @@ -155,6 +159,9 @@ true + + true + true From 63144db49353091ef632ad1e1ce2faea0cc8c007 Mon Sep 17 00:00:00 2001 From: Andrew <30809111+acoates-ms@users.noreply.github.com> Date: Fri, 30 Aug 2024 14:58:48 -0700 Subject: [PATCH 2/2] snapshots --- .../__snapshots__/HomeUIADump.test.ts.snap | 1 + .../TextInputComponentTest.test.ts.snap | 99 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/HomeUIADump.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/HomeUIADump.test.ts.snap index 883c9172cac..d91de9186e5 100644 --- a/packages/e2e-test-app-fabric/test/__snapshots__/HomeUIADump.test.ts.snap +++ b/packages/e2e-test-app-fabric/test/__snapshots__/HomeUIADump.test.ts.snap @@ -9287,6 +9287,7 @@ exports[`Home UIA Tree Dump Search Bar 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Search...", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", diff --git a/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap b/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap index 85cbfdbf6aa..f3799944536 100644 --- a/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap +++ b/packages/e2e-test-app-fabric/test/__snapshots__/TextInputComponentTest.test.ts.snap @@ -7,6 +7,9 @@ exports[`TextInput Tests Multi-line TextInputs can enable text selection (Impera "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "multiline text selection +can also be changed imperatively", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -82,6 +85,9 @@ exports[`TextInput Tests Multi-line TextInputs can enable text selection 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "multiline text selection +can also be changed", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -157,6 +163,8 @@ exports[`TextInput Tests Single-line TextInputs can enable text selection (Imper "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "text selection can be changed imperatively", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -232,6 +240,8 @@ exports[`TextInput Tests Single-line TextInputs can enable text selection 1`] = "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "text selection can be changed", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -309,6 +319,8 @@ exports[`TextInput Tests Text have cursorColor 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "cursorColor={"green"}", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Hello World", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -344,6 +356,7 @@ exports[`TextInput Tests TextInputs can autocapitalize: Autocapitalize Character "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -419,6 +432,7 @@ exports[`TextInput Tests TextInputs can autocapitalize: Autocapitalize Sentences "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -494,6 +508,7 @@ exports[`TextInput Tests TextInputs can autocapitalize: Autocapitalize Turned Of "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -569,6 +584,7 @@ exports[`TextInput Tests TextInputs can autocapitalize: Autocapitalize Words 1`] "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -646,6 +662,7 @@ exports[`TextInput Tests TextInputs can autocomplete, address country 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "postal-address-country", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -723,6 +740,7 @@ exports[`TextInput Tests TextInputs can autocomplete, country 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "country", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -800,6 +818,7 @@ exports[`TextInput Tests TextInputs can autocomplete, one-time-code 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "one-time-code", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -877,6 +896,7 @@ exports[`TextInput Tests TextInputs can autocomplete, sms-otp 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "sms-otp", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -952,6 +972,8 @@ exports[`TextInput Tests TextInputs can autogrow 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "small small small small small small", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -989,6 +1011,7 @@ exports[`TextInput Tests TextInputs can be editable 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "editable text input using editable prop", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1064,6 +1087,8 @@ exports[`TextInput Tests TextInputs can be multiline, bottomright alignment 1`] "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "multiline with children, aligned bottom-right", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1101,6 +1126,7 @@ exports[`TextInput Tests TextInputs can be multiline, center alignment 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "multiline, aligned center", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1138,6 +1164,7 @@ exports[`TextInput Tests TextInputs can be multiline, topleft alignment 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "multiline, aligned top-left", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1173,6 +1200,7 @@ exports[`TextInput Tests TextInputs can be set to not editable 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.Value": "Can't touch this! (>'-')> ^(' - ')^ <('-'<) (>'-')> ^(' - ')^", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1285,6 +1313,7 @@ exports[`TextInput Tests TextInputs can clear on submit 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1320,6 +1349,7 @@ exports[`TextInput Tests TextInputs can clear on submit with custom submit key e "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1355,6 +1385,7 @@ exports[`TextInput Tests TextInputs can clear on submit with custom submit key e "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1390,6 +1421,7 @@ exports[`TextInput Tests TextInputs can customize its padding 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1427,6 +1459,7 @@ exports[`TextInput Tests TextInputs can enable spellcheck 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Type text to test spell check functionality.", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1462,6 +1495,8 @@ exports[`TextInput Tests TextInputs can have a background color 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1537,6 +1572,8 @@ exports[`TextInput Tests TextInputs can have a color 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1612,6 +1649,8 @@ exports[`TextInput Tests TextInputs can have a font family 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1687,6 +1726,8 @@ exports[`TextInput Tests TextInputs can have a font size 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1762,6 +1803,8 @@ exports[`TextInput Tests TextInputs can have a font style 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1837,6 +1880,8 @@ exports[`TextInput Tests TextInputs can have a font weight 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1912,6 +1957,8 @@ exports[`TextInput Tests TextInputs can have attributed text 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Hello", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -1989,6 +2036,8 @@ exports[`TextInput Tests TextInputs can have caretHidden 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "caretHidden={true}", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Hello World", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2026,6 +2075,7 @@ exports[`TextInput Tests TextInputs can have custom return key label, Compile 1` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyLabel: Compile", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2063,6 +2113,7 @@ exports[`TextInput Tests TextInputs can have custom return key label, React Nati "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyLabel: React Native", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2100,6 +2151,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, done 1`] = "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: done", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2137,6 +2189,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, go 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: go", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2174,6 +2227,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, next 1`] = "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: next", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2211,6 +2265,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, none 1`] = "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: none", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2248,6 +2303,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, previous 1` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: previous", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2285,6 +2341,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, search 1`] "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: search", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2322,6 +2379,7 @@ exports[`TextInput Tests TextInputs can have custom return key type, send 1`] = "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "returnKeyType: send", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2359,6 +2417,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=-1 "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "letterSpacing = -1", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2396,6 +2455,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=0 "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "letterSpacing = 0", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2433,6 +2493,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=2 "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "letterSpacing = 2", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2470,6 +2531,7 @@ exports[`TextInput Tests TextInputs can have customer letter spacing, spacing=9 "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "letterSpacing = 9", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2601,6 +2663,8 @@ exports[`TextInput Tests TextInputs can have customized letter spacing 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2676,6 +2740,8 @@ exports[`TextInput Tests TextInputs can have customized line height 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Hel", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2753,6 +2819,7 @@ exports[`TextInput Tests TextInputs can have inline images 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "This has drawableLeft set", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2790,6 +2857,7 @@ exports[`TextInput Tests TextInputs can have inline images, drawable props not s "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "This does not have drawable props set", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2827,6 +2895,7 @@ exports[`TextInput Tests TextInputs can have inline images, drawableLeft and dra "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "This has drawableLeft and drawablePadding set", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2864,6 +2933,7 @@ exports[`TextInput Tests TextInputs can have shadows 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "shadowColor: purple", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2899,6 +2969,8 @@ exports[`TextInput Tests TextInputs can have text decoration lines 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -2974,6 +3046,8 @@ exports[`TextInput Tests TextInputs can have text shadows 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "He", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3051,6 +3125,7 @@ exports[`TextInput Tests TextInputs can propagate events 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Click inside the box to observe events being fired.", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3088,6 +3163,7 @@ exports[`TextInput Tests TextInputs can register press events 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Click inside the box to observe events being fired.", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3123,6 +3199,7 @@ exports[`TextInput Tests TextInputs can rewrite characters: Replace Space with C "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3198,6 +3275,7 @@ exports[`TextInput Tests TextInputs can rewrite characters: Replace Space with N "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3273,6 +3351,7 @@ exports[`TextInput Tests TextInputs can rewrite characters: Replace Space with U "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3350,6 +3429,7 @@ exports[`TextInput Tests TextInputs can set their readOnly prop to false 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "editable text input using readOnly prop", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3502,6 +3582,7 @@ exports[`TextInput Tests TextInputs can submit with custom key, multilined and s "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3537,6 +3618,8 @@ exports[`TextInput Tests TextInputs have a custom background color 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Same BackgroundColor as View ", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3572,6 +3655,8 @@ exports[`TextInput Tests TextInputs have a custom highlight color 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Selection Color is red", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3609,6 +3694,7 @@ exports[`TextInput Tests TextInputs have a custom placeholder text color 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Red placeholder text color", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3644,6 +3730,8 @@ exports[`TextInput Tests TextInputs have a custom text color 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Green Text", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3681,6 +3769,7 @@ exports[`TextInput Tests TextInputs have a custom underline color 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Blue underline color", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3718,6 +3807,7 @@ exports[`TextInput Tests TextInputs have a default placeholder text color 1`] = "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Default placeholder text color", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3753,6 +3843,8 @@ exports[`TextInput Tests TextInputs have a default text color 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Default color text", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3790,6 +3882,7 @@ exports[`TextInput Tests TextInputs have a default underline color 1`] = ` "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "Default underline color", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3825,6 +3918,8 @@ exports[`TextInput Tests TextInputs support secure entry 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "iloveturtles", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3862,6 +3957,7 @@ exports[`TextInput Tests TextInputs support secure entry, with placeholder text "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "color is supported too", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3899,6 +3995,7 @@ exports[`TextInput Tests TextInputs with set height and padding from theme 1`] = "IsKeyboardFocusable": true, "LocalizedControlType": "edit", "Name": "If you set height, beware of padding set from themes", + "ValuePattern.IsReadOnly": false, }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView", @@ -3934,6 +4031,8 @@ exports[`TextInput Tests Uncontrolled TextInput 1`] = ` "ControlType": 50004, "IsKeyboardFocusable": true, "LocalizedControlType": "edit", + "ValuePattern.IsReadOnly": false, + "ValuePattern.Value": "Hello World!", }, "Component Tree": { "Type": "Microsoft.ReactNative.Composition.WindowsTextInputComponentView",