Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions packages/sample-app-fabric/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
*/

import React from 'react';
import {SafeAreaView, StatusBar, useColorScheme} from 'react-native';
import {View, SafeAreaView, StatusBar, useColorScheme, ScrollView} from 'react-native';
import {NewAppScreen} from '@react-native/new-app-screen';
import XamlHost from 'react-native-windows/Libraries/Components/Xaml/XamlHost';

function App(): React.JSX.Element {
const isDarkMode = useColorScheme() === 'dark';
Expand All @@ -18,8 +19,9 @@ function App(): React.JSX.Element {
barStyle={isDarkMode ? 'light-content' : 'dark-content'}
backgroundColor="transparent"
translucent
/>
<NewAppScreen />
/>
<XamlHost label="sharath3" style={{ height:200, borderWidth: 2, borderColor: 'yellow' }} />
<NewAppScreen />
</SafeAreaView>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@

#include "NativeModules.h"

#include "../../../../vnext/Microsoft.ReactNative/XamlHost.h"


// A PackageProvider containing any turbo modules you define within this app project
struct CompReactPackageProvider
: winrt::implements<CompReactPackageProvider, winrt::Microsoft::ReactNative::IReactPackageProvider> {
public: // IReactPackageProvider
void CreatePackage(winrt::Microsoft::ReactNative::IReactPackageBuilder const &packageBuilder) noexcept {
AddAttributedModules(packageBuilder, true);
RegisterXamlHostComponentView(packageBuilder);
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@
<DisableSpecificWarnings>4453;28204</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>shell32.lib;user32.lib;windowsapp.lib;%(AdditionalDependenices)</AdditionalDependencies>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>shell32.lib;user32.lib;windowsapp.lib;Microsoft.ReactNative.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\..\..\vnext\target\x64\Debug\Microsoft.ReactNative\</AdditionalLibraryDirectories>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
Expand Down
6 changes: 6 additions & 0 deletions vnext/Microsoft.ReactNative/App.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Application
x:Class="Microsoft.ReactNative.XamlApplication"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Microsoft.ReactNative">
</Application>
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,19 @@ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
NavigateDirection direction,
IRawElementProviderFragment **pRetVal) {

OutputDebugString(L"[UIA] Navigate returning provider instance: ");
OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(this)).c_str());
OutputDebugString(L"\n");

if (pRetVal == nullptr)
return E_POINTER;

if (m_childSiteLink) {
if (direction == NavigateDirection_FirstChild || direction == NavigateDirection_LastChild) {
OutputDebugString(L"[UIA] Navigate returning provider instance - 2: ");
OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(this)).c_str());
OutputDebugString(L"\n");
auto fragment = m_childSiteLink.AutomationProvider().try_as<IRawElementProviderFragment>();
*pRetVal = fragment.detach();
return S_OK;
Expand Down Expand Up @@ -115,6 +123,18 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::get_BoundingRectangle(Ui
if (pRetVal == nullptr)
return E_POINTER;

OutputDebugString(L"[UIA] get_BoundingRectangle called: ");

if (m_childSiteLink == nullptr) {
OutputDebugString(L"[UIA] m_childSiteLink is nullptr — using fallback RNW bounds\n");
OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(this)).c_str());
OutputDebugString(L"\n");
} else {
OutputDebugString(L"[UIA] m_childSiteLink is valid — expected to use XamlIsland bounds\n");
OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(this)).c_str());
OutputDebugString(L"\n");
}

auto hr = UiaGetBoundingRectangleHelper(m_view, *pRetVal);
if (FAILED(hr))
return hr;
Expand Down Expand Up @@ -161,6 +181,13 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::SetFocus(void) {
return UiaSetFocusHelper(m_view);
}

winrt::IUnknown CompositionDynamicAutomationProvider::TryGetChildSiteLinkAutomationProvider() {
if (m_childSiteLink) {
return m_childSiteLink.AutomationProvider().as<winrt::IUnknown>();
}
return nullptr;
}

HRESULT __stdcall CompositionDynamicAutomationProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal) {
if (pRetVal == nullptr)
return E_POINTER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
void AddToSelectionItems(winrt::com_ptr<IRawElementProviderSimple> &item);
void RemoveFromSelectionItems(winrt::com_ptr<IRawElementProviderSimple> &item);

void SetChildSiteLink(winrt::Microsoft::UI::Content::ChildSiteLink childSiteLink) {
m_childSiteLink = childSiteLink;
}

// If this object is for a ChildSiteLink, returns the ChildSiteLink's automation provider.
// This will be a provider object from the hosted framework (for example, WinUI).
winrt::IUnknown TryGetChildSiteLinkAutomationProvider();

private:
::Microsoft::ReactNative::ReactTaggedView m_view;
winrt::com_ptr<ITextProvider2> m_textProvider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,8 @@ HRESULT __stdcall CompositionRootAutomationProvider::ElementProviderFromPoint(
auto local = rootView->ConvertScreenToLocal({static_cast<float>(x), static_cast<float>(y)});
auto provider = rootView->UiaProviderFromPoint(
{static_cast<LONG>(local.X * rootView->LayoutMetrics().PointScaleFactor),
static_cast<LONG>(local.Y * rootView->LayoutMetrics().PointScaleFactor)});
static_cast<LONG>(local.Y * rootView->LayoutMetrics().PointScaleFactor)},
{static_cast<LONG>(x), static_cast<LONG>(y)});
auto spFragment = provider.try_as<IRawElementProviderFragment>();
if (spFragment) {
*pRetVal = spFragment.detach();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1003,11 +1003,11 @@ std::string ComponentView::DefaultControlType() const noexcept {
}

std::string ComponentView::DefaultAccessibleName() const noexcept {
return "";
return "DefaultAccessString";
}

std::string ComponentView::DefaultHelpText() const noexcept {
return "";
return "DefaultHelpString";
}

facebook::react::SharedViewProps ViewComponentView::defaultProps() noexcept {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,11 @@ void ContentIslandComponentView::OnMounted() noexcept {
// We configure automation even if there's no UIA client at this point, because it's possible the first UIA
// request we'll get will be for a child of this island calling upward in the UIA tree.
ConfigureChildSiteLinkAutomation();
OutputDebugString(L"Configure childsite Connected");

if (m_islandToConnect) {
m_childSiteLink.Connect(m_islandToConnect);
OutputDebugString(L"ChildSiteLinK Connected");
m_islandToConnect = nullptr;
}

Expand All @@ -85,6 +87,25 @@ void ContentIslandComponentView::OnMounted() noexcept {
}
}

facebook::react::Tag ContentIslandComponentView::hitTest(
facebook::react::Point pt,
facebook::react::Point &localPt,
bool ignorePointerEvents) const noexcept {
facebook::react::Point ptLocal{pt.x - m_layoutMetrics.frame.origin.x, pt.y - m_layoutMetrics.frame.origin.y};

// This is similar to ViewComponentView::hitTest, but we don't want to hit test the children of this node,
// because the child ComponentView is kind of a dummy representation of the XamlElement and doesn't do anything.
// So, we just hit test the ContentIsland itself to make the UIA ElementProviderFromPoint call work.
// TODO: Will this cause a problem -- does this function need to do something different for non-UIA scenarios?
if (ptLocal.x >= 0 && ptLocal.x <= m_layoutMetrics.frame.size.width && ptLocal.y >= 0 &&
ptLocal.y <= m_layoutMetrics.frame.size.height) {
localPt = ptLocal;
return Tag();
}

return -1;
}

void ContentIslandComponentView::OnUnmounted() noexcept {
m_layoutMetricChangedRevokers.clear();
if (m_navigationHostDepartFocusRequestedToken && m_navigationHost) {
Expand Down Expand Up @@ -115,6 +136,7 @@ winrt::IInspectable ContentIslandComponentView::EnsureUiaProvider() noexcept {
if (m_uiaProvider == nullptr) {
m_uiaProvider = winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionDynamicAutomationProvider>(
*get_strong(), m_childSiteLink);
OutputDebugString(L"[EnsureUiaProvider] Created CompositionDynamicAutomationProvider\n");
}
return m_uiaProvider;
}
Expand Down Expand Up @@ -178,14 +200,14 @@ ContentIslandComponentView::~ContentIslandComponentView() noexcept {
void ContentIslandComponentView::MountChildComponentView(
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
uint32_t index) noexcept {
assert(false);
//assert(false);
base_type::MountChildComponentView(childComponentView, index);
}

void ContentIslandComponentView::UnmountChildComponentView(
const winrt::Microsoft::ReactNative::ComponentView &childComponentView,
uint32_t index) noexcept {
assert(false);
//assert(false);
base_type::UnmountChildComponentView(childComponentView, index);
}

Expand All @@ -196,15 +218,18 @@ void ContentIslandComponentView::updateLayoutMetrics(
m_childSiteLink.ActualSize({layoutMetrics.frame.size.width, layoutMetrics.frame.size.height});
ParentLayoutChanged();
}

base_type::updateLayoutMetrics(layoutMetrics, oldLayoutMetrics);
}

void ContentIslandComponentView::Connect(const winrt::Microsoft::UI::Content::ContentIsland &contentIsland) noexcept {
if (m_childSiteLink) {
m_islandToConnect = nullptr;
m_childSiteLink.Connect(contentIsland);
OutputDebugString(L"1-ChildSiteLink connected to XamlIsland\n");
} else {
m_islandToConnect = contentIsland;
OutputDebugString(L"2-ChildSiteLink connected to XamlIsland\n");
}
}

Expand All @@ -217,7 +242,7 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
// It puts the child content into a mode where it won't own its own framework root. Instead, the child island's
// automation peers will use the same framework root as the automation peer of this ContentIslandComponentView.
m_childSiteLink.AutomationOption(winrt::Microsoft::UI::Content::ContentAutomationOptions::FragmentBased);

//OutputDebugString(L"[ContentIsland] AutomationOption set to FragmentBased\n");
// These events are raised in response to the child ContentIsland asking for providers.
// For example, the ContentIsland.FragmentRootAutomationProvider property will return
// the provider we provide here in FragmentRootAutomationProviderRequested.
Expand All @@ -231,37 +256,63 @@ void ContentIslandComponentView::ConfigureChildSiteLinkAutomation() noexcept {
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
// The child island's fragment tree doesn't have its own fragment root.
// Here's how we can provide the correct fragment root to the child's UIA logic.
// OutputDebugString(L"[ContentIsland] FragmentRootAutomationProviderRequested entered\n");
winrt::com_ptr<IRawElementProviderFragmentRoot> fragmentRoot{nullptr};
auto uiaProvider = this->EnsureUiaProvider();
// OutputDebugString(L"[UIA] FragmentRootAutomationProviderRequested uiaprovider: ");

OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(winrt::get_abi(uiaProvider))).c_str());
OutputDebugString(L"\n");

uiaProvider.as<IRawElementProviderFragment>()->get_FragmentRoot(fragmentRoot.put());
OutputDebugString(L"[UIA] FragmentRoot pointer: ");
OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(fragmentRoot.get())).c_str());
OutputDebugString(L"\n");
args.AutomationProvider(fragmentRoot.as<IInspectable>());
OutputDebugString(L"[ContentIsland] FragmentRootAutomationProviderRequested handled\n");
args.Handled(true);
});

m_parentAutomationProviderRequestedToken = m_childSiteLink.ParentAutomationProviderRequested(
[this](
const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
OutputDebugString(L"[ContentIsland] ParentAutomationProviderRequested entered\n");
auto uiaProvider = this->EnsureUiaProvider();
OutputDebugString(L"[UIA] ParentAutomationProviderRequested uiaprovider: ");

OutputDebugString(std::to_wstring(reinterpret_cast<uintptr_t>(winrt::get_abi(uiaProvider))).c_str());
OutputDebugString(L"\n");
args.AutomationProvider(uiaProvider);
OutputDebugString(L"[ContentIsland] ParentAutomationProviderRequested handled\n");
args.Handled(true);
});

m_nextSiblingAutomationProviderRequestedToken = m_childSiteLink.NextSiblingAutomationProviderRequested(
[](const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
// The ContentIsland will always be the one and only child of this node, so it won't have siblings.
OutputDebugString(L"[ContentIsland] NextSiblingAutomationProviderRequested entered\n");
args.AutomationProvider(nullptr);
OutputDebugString(L"[ContentIsland] m_nextSiblingAutomationProviderRequestedToken handled\n");
args.Handled(true);
});

m_previousSiblingAutomationProviderRequestedToken = m_childSiteLink.PreviousSiblingAutomationProviderRequested(
[](const winrt::Microsoft::UI::Content::IContentSiteAutomation &,
const winrt::Microsoft::UI::Content::ContentSiteAutomationProviderRequestedEventArgs &args) {
// The ContentIsland will always be the one and only child of this node, so it won't have siblings.
OutputDebugString(L"[ContentIsland] PreviousSiblingAutomationProviderRequested entered\n");
args.AutomationProvider(nullptr);
OutputDebugString(L"[ContentIsland] m_previousSiblingAutomationProviderRequestedToken handled\n");
args.Handled(true);
});

if (m_uiaProvider) {
auto providerImpl =
m_uiaProvider.as<winrt::Microsoft::ReactNative::implementation::CompositionDynamicAutomationProvider>();
providerImpl->SetChildSiteLink(m_childSiteLink);
}
}

} // namespace winrt::Microsoft::ReactNative::Composition::implementation
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ struct ContentIslandComponentView : ContentIslandComponentViewT<ContentIslandCom
ReactCompositionViewComponentBuilder *builder);
~ContentIslandComponentView() noexcept;

virtual facebook::react::Tag hitTest(
facebook::react::Point pt,
facebook::react::Point &localPt,
bool ignorePointerEvents) const noexcept override;
private:
void OnMounted() noexcept;
void OnUnmounted() noexcept;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <Fabric/FabricUIManagerModule.h>
#include <winrt/Microsoft.UI.Input.h>
#include "CompositionRootAutomationProvider.h"
#include "CompositionDynamicAutomationProvider.h"
#include "ReactNativeIsland.h"
#include "Theme.h"

Expand Down Expand Up @@ -275,7 +276,7 @@ facebook::react::Point RootComponentView::getClientOffset() const noexcept {
return {};
}

winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixels) noexcept {
winrt::IUnknown RootComponentView::UiaProviderFromPoint(const POINT &ptPixels, const POINT &ptScreen) noexcept {
facebook::react::Point ptDips{
static_cast<facebook::react::Float>(ptPixels.x) / m_layoutMetrics.pointScaleFactor,
static_cast<facebook::react::Float>(ptPixels.y) / m_layoutMetrics.pointScaleFactor};
Expand All @@ -295,7 +296,38 @@ winrt::IInspectable RootComponentView::UiaProviderFromPoint(const POINT &ptPixel
if (view == nullptr)
return nullptr;

return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(view)->EnsureUiaProvider();
// return winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(view)->EnsureUiaProvider();
auto uiaProvider =
winrt::get_self<winrt::Microsoft::ReactNative::implementation::ComponentView>(view)->EnsureUiaProvider();

// HACKHACK: It's ugly to have the RootComponentView know about the CompositionDynamicAutomationProvider type.
// HACKHACK: Can we clean this up?
auto dynamicProvider =
uiaProvider.try_as<winrt::Microsoft::ReactNative::implementation::CompositionDynamicAutomationProvider>();
if (dynamicProvider) {
if (auto childProvider = dynamicProvider->TryGetChildSiteLinkAutomationProvider()) {
// childProvider is the the automation provider from the ChildSiteLink. In the case of WinUI, this
// is a pointer to WinUI's internal CUIAHostWindow object.
// It seems odd, but even though this node doesn't behave as a fragment root in our case (the real fragment root
// is the RootComponentView's UIA provider), we still use its IRawElementProviderFragmentRoot -- just so
// we can do the ElementProviderFromPoint call. (this was recommended by the team who did the initial
// architecture work).
if (auto fragmentRoot = childProvider.try_as<IRawElementProviderFragmentRoot>()) {
com_ptr<IRawElementProviderFragment> frag;
fragmentRoot->ElementProviderFromPoint(
ptScreen.x, // Note since we're going through IRawElementProviderFragment the coordinates are in screen space.
ptScreen.y,
frag.put());
// In the case of WinUI, frag is now the UIA provider for the specific WinUI element that was hit.
// (A Microsoft_UI_Xaml!CUIAWrapper object)
if (frag) {
return frag.as<winrt::IUnknown>();
}
}
}
}

return uiaProvider;
}

float RootComponentView::FontSizeMultiplier() const noexcept {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ struct RootComponentView : RootComponentViewT<RootComponentView, ViewComponentVi
facebook::react::LayoutMetrics const &layoutMetrics,
facebook::react::LayoutMetrics const &oldLayoutMetrics) noexcept override;

winrt::Windows::Foundation::IInspectable UiaProviderFromPoint(const POINT &ptPixels) noexcept;
winrt::Windows::Foundation::IUnknown UiaProviderFromPoint(const POINT &ptPixels, const POINT &ptScreen) noexcept;

RootComponentView(
const winrt::Microsoft::ReactNative::Composition::Experimental::ICompositionContext &compContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
</ImportGroup>
<ImportGroup Label="Shared" />
<Import Project="..\Chakra\Chakra.vcxitems" Label="Shared" />
<Import Project="..\Shared\Shared.vcxitems" Label="Shared" />
<Import Project="..\Shared\Shared.vcxitems" Label="Shared" /><Import Project="..\Shared\Shared.vcxitems" Label="Shared" />
<Import Project="..\Mso\Mso.vcxitems" Label="Shared" />
<Import Project="..\Microsoft.ReactNative.Cxx\Microsoft.ReactNative.Cxx.vcxitems" Label="Shared" />
<ImportGroup Label="PropertySheets">
Expand Down
Loading
Loading