Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "ITextProvider Implementation",
"packageName": "react-native-windows",
"email": "34109996+chiaramooney@users.noreply.github.com",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ exports[`Text Tests Padding can be added to Text 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text is indented by 10px padding on all sides.",
"TextRangePattern.GetText": "This text is indented by 10px padding on all sides.",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -30,6 +31,7 @@ exports[`Text Tests Text can be restricted to one line 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Maximum of one line no matter now much I write here. If I keep writing it'll just truncate after one line",
"TextRangePattern.GetText": "Maximum of one line no matter now much I write here. If I keep writing it'll just truncate after one line",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -53,6 +55,7 @@ exports[`Text Tests Text can be selectable 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text is selectable if you click-and-hold, and will offer the native Android selection menus.",
"TextRangePattern.GetText": "This text is selectable if you click-and-hold, and will offer the native Android selection menus.",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -76,6 +79,7 @@ exports[`Text Tests Text can have a color 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Red color",
"TextRangePattern.GetText": "Red color",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -99,6 +103,7 @@ exports[`Text Tests Text can have a customized selection color 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text will have a orange highlight on selection.",
"TextRangePattern.GetText": "This text will have a orange highlight on selection.",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -122,6 +127,7 @@ exports[`Text Tests Text can have a size 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Size 23",
"TextRangePattern.GetText": "Size 23",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand Down Expand Up @@ -151,18 +157,21 @@ exports[`Text Tests Text can have advanced borders 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text has customized borders.",
"TextRangePattern.GetText": "This text has customized borders.",
},
{
"AutomationId": "",
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text has customized borders.",
"TextRangePattern.GetText": "This text has customized borders.",
},
{
"AutomationId": "",
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text has customized borders.",
"TextRangePattern.GetText": "This text has customized borders.",
},
],
},
Expand Down Expand Up @@ -463,6 +472,7 @@ exports[`Text Tests Text can have an outer color 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "(Normal text,(R)red(G)green(B)blue(C)cyan(M)magenta(Y)yellow(K)black(and bold(and tiny bold italic blue(and tiny normal blue))))",
"TextRangePattern.GetText": "(Normal text,(R)red(G)green(B)blue(C)cyan(M)magenta(Y)yellow(K)black(and bold(and tiny bold italic blue(and tiny normal blue))))",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand Down Expand Up @@ -492,20 +502,25 @@ exports[`Text Tests Text can have borders 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Sample bordered text with default styling.",
"TextRangePattern.GetText": "Sample bordered text with default styling.",
},
{
"AutomationId": "",
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Some more bordered text + a tad of CSS.
1st nested - border specifcied but ignored.
2nd Inside text!",
"TextRangePattern.GetText": "Some more bordered text + a tad of CSS.
1st nested - border specifcied but ignored.
2nd Inside text!",
},
{
"AutomationId": "",
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "This text is outlined and laid out within the normal text run, so will wrap etc as normal text.",
"TextRangePattern.GetText": "This text is outlined and laid out within the normal text run, so will wrap etc as normal text.",
},
],
},
Expand Down Expand Up @@ -668,6 +683,7 @@ exports[`Text Tests Text can have decoration lines: Solid Line Through 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Solid line-through",
"TextRangePattern.GetText": "Solid line-through",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -691,6 +707,7 @@ exports[`Text Tests Text can have decoration lines: Underline 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Solid underline",
"TextRangePattern.GetText": "Solid underline",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -714,6 +731,7 @@ exports[`Text Tests Text can have shadows 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "Demo text shadow",
"TextRangePattern.GetText": "Demo text shadow",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand All @@ -737,6 +755,7 @@ exports[`Text Tests Text can wrap 1`] = `
"ControlType": 50020,
"LocalizedControlType": "text",
"Name": "The text should wrap if it goes on multiple lines. See, this is going to the next line.",
"TextRangePattern.GetText": "The text should wrap if it goes on multiple lines. See, this is going to the next line.",
},
"Component Tree": {
"Type": "Microsoft.ReactNative.Composition.ParagraphComponentView",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,7 @@ void DumpUIAPatternInfo(IUIAutomationElement *pTarget, const winrt::Windows::Dat
BOOL isSelected;
BOOL multipleSelection;
BOOL selectionRequired;
BSTR text = nullptr;

// Dump IValueProvider Information
IValueProvider *valuePattern;
Expand Down Expand Up @@ -451,6 +452,21 @@ void DumpUIAPatternInfo(IUIAutomationElement *pTarget, const winrt::Windows::Dat
selectionPattern->Release();
}

// Dump ITextRangeProvider Information
ITextProvider *textPattern;
Comment thread
chiaramooney marked this conversation as resolved.
Outdated
hr = pTarget->GetCurrentPattern(UIA_TextPatternId, reinterpret_cast<IUnknown **>(&textPattern));
if (SUCCEEDED(hr) && textPattern) {
ITextRangeProvider *textRangePattern;
hr = textPattern->get_DocumentRange(&textRangePattern);
if (SUCCEEDED(hr) && textRangePattern) {
textRangePattern->GetText(20, &text);
Comment thread
chiaramooney marked this conversation as resolved.
if (SUCCEEDED(hr)) {
InsertStringValueIfNotEmpty(result, L"TextRangePattern.GetText", text);
}
}
}

::SysFreeString(text);
::SysFreeString(value);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#include "pch.h"
#include "CompositionDynamicAutomationProvider.h"
#include <Fabric/ComponentView.h>
#include <Fabric/Composition/CompositionTextRangeProvider.h>
#include <Fabric/Composition/ParagraphComponentView.h>
#include <Fabric/Composition/SwitchComponentView.h>
#include <Fabric/Composition/TextInput/WindowsTextInputComponentView.h>
#include <Fabric/platform/react/renderer/graphics/HostPlatformColor.h>
#include <Unicode.h>
#include "RootComponentView.h"
#include "UiaHelpers.h"
Expand All @@ -25,6 +28,13 @@ CompositionDynamicAutomationProvider::CompositionDynamicAutomationProvider(
if (props->accessibilityState.has_value() && props->accessibilityState->selected.has_value()) {
AddSelectionItemsToContainer(this);
}

if (strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::WindowsTextInputComponentView>() ||
Comment thread
chiaramooney marked this conversation as resolved.
strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::ParagraphComponentView>()) {
m_textProvider = winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionTextProvider>(
strongView.as<winrt::Microsoft::ReactNative::Composition::ComponentView>(), this)
.try_as<ITextProvider2>();
}
}

HRESULT __stdcall CompositionDynamicAutomationProvider::Navigate(
Expand Down Expand Up @@ -242,6 +252,21 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetPatternProvider(PATTE
AddRef();
}

if (patternId == UIA_TextPatternId &&
(strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::WindowsTextInputComponentView>() ||
strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::ParagraphComponentView>())) {
auto textProvider = m_textProvider.as<ITextProvider>().get();
*pRetVal = textProvider;
Comment thread
chiaramooney marked this conversation as resolved.
Outdated
textProvider->AddRef();
}

if (patternId == UIA_TextPattern2Id &&
strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::WindowsTextInputComponentView>()) {
auto textProvider = m_textProvider.get();
*pRetVal = textProvider;
textProvider->AddRef();
}

return S_OK;
}

Expand Down Expand Up @@ -741,6 +766,7 @@ HRESULT __stdcall CompositionDynamicAutomationProvider::GetSelection(SAFEARRAY *
auto pos = static_cast<long>(i);
SafeArrayPutElement(*pRetVal, &pos, m_selectionItems.at(selectedItems.at(i)).get());
}

return S_OK;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ class CompositionDynamicAutomationProvider : public winrt::implements<
virtual HRESULT __stdcall get_CanSelectMultiple(BOOL *pRetVal) override;
virtual HRESULT __stdcall get_IsSelectionRequired(BOOL *pRetVal) override;
virtual HRESULT __stdcall GetSelection(SAFEARRAY **pRetVal) override;

// inherited via ISelectionItemProvider
virtual HRESULT __stdcall AddToSelection() override;
virtual HRESULT __stdcall get_IsSelected(BOOL *pRetVal) override;
Expand All @@ -85,6 +84,7 @@ class CompositionDynamicAutomationProvider : public winrt::implements<

private:
::Microsoft::ReactNative::ReactTaggedView m_view;
winrt::com_ptr<ITextProvider2> m_textProvider;
std::vector<winrt::com_ptr<IRawElementProviderSimple>> m_selectionItems;
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "pch.h"
#include <Fabric/ComponentView.h>
#include <Fabric/Composition/ParagraphComponentView.h>
#include <Fabric/Composition/TextInput/WindowsTextInputComponentView.h>
#include <Fabric/platform/react/renderer/graphics/HostPlatformColor.h>
#include <Unicode.h>
#include "CompositionTextRangeProvider.h"
#include "RootComponentView.h"
#include "UiaHelpers.h"

namespace winrt::Microsoft::ReactNative::implementation {

CompositionTextProvider::CompositionTextProvider(
const winrt::Microsoft::ReactNative::Composition::ComponentView &componentView,
CompositionDynamicAutomationProvider *parentProvider) noexcept
: m_view{componentView} {
m_parentProvider.copy_from(parentProvider);
}
void CompositionTextProvider::EnsureTextRangeProvider() {
auto strongView = m_view.view();

if (!strongView)
return;

if (!m_textRangeProvider) {
m_textRangeProvider =
winrt::make<winrt::Microsoft::ReactNative::implementation::CompositionTextRangeProvider>(
strongView.as<winrt::Microsoft::ReactNative::Composition::ComponentView>(), m_parentProvider.get())
.try_as<ITextRangeProvider>();
m_parentProvider->AddRef();
Comment thread
chiaramooney marked this conversation as resolved.
Outdated
}
}

HRESULT __stdcall CompositionTextProvider::get_DocumentRange(ITextRangeProvider **pRetVal) {
if (pRetVal == nullptr)
return E_POINTER;
auto strongView = m_view.view();

if (!strongView)
return UIA_E_ELEMENTNOTAVAILABLE;
EnsureTextRangeProvider();

if (m_textRangeProvider == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
m_textRangeProvider.copy_to(pRetVal);
return S_OK;
}

HRESULT __stdcall CompositionTextProvider::get_SupportedTextSelection(SupportedTextSelection *pRetVal) {
if (pRetVal == nullptr)
return E_POINTER;
auto strongView = m_view.view();

if (!strongView)
return UIA_E_ELEMENTNOTAVAILABLE;

if (strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::WindowsTextInputComponentView>()) {
*pRetVal = SupportedTextSelection_Single;
} else if (
auto textView =
strongView.try_as<winrt::Microsoft::ReactNative::Composition::implementation::ParagraphComponentView>()) {
auto props = std::static_pointer_cast<const facebook::react::ParagraphProps>(textView->props());
if (props == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
*pRetVal = props->isSelectable ? SupportedTextSelection_Single : SupportedTextSelection_None;
} else {
*pRetVal = SupportedTextSelection_None;
}

return S_OK;
}

HRESULT __stdcall CompositionTextProvider::GetSelection(SAFEARRAY **pRetVal) {
*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 0);
return S_OK;
}

HRESULT __stdcall CompositionTextProvider::GetVisibleRanges(SAFEARRAY **pRetVal) {
*pRetVal = SafeArrayCreateVector(VT_UNKNOWN, 0, 1);
if (m_textRangeProvider == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
LONG pos = 0;
SafeArrayPutElement(*pRetVal, &pos, m_textRangeProvider.get());
return S_OK;
}

HRESULT __stdcall CompositionTextProvider::RangeFromChild(
IRawElementProviderSimple *childElement,
ITextRangeProvider **pRetVal) {
// no-op
*pRetVal = nullptr;
return S_OK;
}

HRESULT __stdcall CompositionTextProvider::RangeFromPoint(UiaPoint point, ITextRangeProvider **pRetVal) {
// no-op
if (m_textRangeProvider == nullptr)
return UIA_E_ELEMENTNOTAVAILABLE;
m_textRangeProvider.copy_to(pRetVal);
return S_OK;
}
HRESULT __stdcall CompositionTextProvider::GetCaretRange(BOOL *isActive, ITextRangeProvider **pRetVal) {
// no-op
*pRetVal = nullptr;
return S_OK;
}

HRESULT __stdcall CompositionTextProvider::RangeFromAnnotation(
IRawElementProviderSimple *annotationElement,
ITextRangeProvider **pRetVal) {
// no-op
*pRetVal = nullptr;
return S_OK;
}
} // namespace winrt::Microsoft::ReactNative::implementation
Loading