forked from microsoft/react-native-windows
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Introduces TextVisitor to consolidate text traversal algorithms (micr…
…osoft#8454) * Initial implementation of TextVisitor We have 3 different features that require a DFS traversal of the text tree: 1. The textTransform algorithm needs to recurse the undefined paths of the Text sub-tree where the prop value changes to update. 2. The backgroundColor algorithm needs to recurse the entire text tree to re-calculate highlighters any time the text changes or backgroundColor / foregroundColor props change within the tree. 3. The WIP hit test algorithm needs to recurse the text tree to identify which span is pressed. Additionally, we have 4 "upward" traversal algorithms: 1. When a RCTRawText changes it's text prop, we need to send an accessibility notification 2. When a RCTRawText changes, we need to re-apply text transforms. 3. When a background or foregroundColor changes, we need to notify each parent that it may require a highlighter for an optimization that skips calculating highlighters for text trees without foreground or background colors. 4. In the WIP hit test algorithm, we need to notify parent nodes that a nested Text component is pressable for the optimization that selectively skips traversal of non-pressable sub-trees Each of these algorithms follow the same pattern, so reduce the potential for bugs, I'd like to consolidate these algorithms to use a visitor pattern. * Change files * Move highlighter logic to visitor pattern * Fixes a couple bugs and adds VisitChildren method to share logic * Split text visitors into individual files * Fixes issue with inherited text transforms Previously, if the child of a virtual text node with undefined textTransform was appended, and that virtual text node had a parent with a defined textTransform, the textTransform value would not inherit properly. Similarly, if a virtual text node switched from defined to undefined for textTransform prop, it would not inherit the correct value and update children accordingly either. Fixes microsoft#8460 * Adds TextPropertyChangedParentVisitor for Text tree traversals when text or highlights are updated * Removes irrelevant headers and fixes namespaces * Refactor TextVisitor to eliminate need for `VisitExtensionText'
- Loading branch information
1 parent
1671258
commit 34356c0
Showing
24 changed files
with
658 additions
and
278 deletions.
There are no files selected for viewing
7 changes: 7 additions & 0 deletions
7
change/react-native-windows-f7be41bd-666d-4885-aa40-4c801dad1528.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
{ | ||
"type": "prerelease", | ||
"comment": "Initial implementation of TextVisitor", | ||
"packageName": "react-native-windows", | ||
"email": "[email protected]", | ||
"dependentChangeType": "patch" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
vnext/Microsoft.ReactNative/Views/Text/TextHighlighterVisitor.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#include "TextHighlighterVisitor.h" | ||
#include <Utils/ValueUtils.h> | ||
#include <Views/RawTextViewManager.h> | ||
#include <Views/TextViewManager.h> | ||
#include <Views/VirtualTextViewManager.h> | ||
#include "TextVisitorScope.h" | ||
|
||
namespace winrt { | ||
using namespace xaml::Documents; | ||
} // namespace winrt | ||
|
||
namespace Microsoft::ReactNative { | ||
|
||
void TextHighlighterVisitor::VisitRawText(ShadowNodeBase *node) { | ||
const auto textNode = static_cast<RawTextShadowNode *>(node); | ||
m_startIndex += textNode->GetView().as<winrt::Run>().Text().size(); | ||
} | ||
|
||
void TextHighlighterVisitor::VisitVirtualText(ShadowNodeBase *node) { | ||
const auto textNode = static_cast<VirtualTextShadowNode *>(node); | ||
const auto foregroundColor = textNode->foregroundColor; | ||
const auto backgroundColor = textNode->backgroundColor; | ||
const auto needsHighlighter = RequiresTextHighlighter(foregroundColor, backgroundColor); | ||
TextVisitorScope<Color> foregroundScope{m_foregroundColors, foregroundColor}; | ||
TextVisitorScope<Color> backgroundScope{m_backgroundColors, backgroundColor}; | ||
|
||
const auto startIndex = m_startIndex; | ||
const auto initialHighlighterCount = highlighters.size(); | ||
Super::VisitVirtualText(node); | ||
|
||
if (needsHighlighter) { | ||
winrt::TextHighlighter highlighter; | ||
highlighter.Background(SolidBrushFromColor(m_backgroundColors.top().value())); | ||
const auto inheritedForegroundColor = m_foregroundColors.top(); | ||
if (inheritedForegroundColor.has_value()) { | ||
highlighter.Foreground(SolidBrushFromColor(inheritedForegroundColor.value())); | ||
} | ||
highlighter.Ranges().Append({startIndex, m_startIndex - startIndex}); | ||
highlighters.push_back(highlighter); | ||
} else if (highlighters.size() == initialHighlighterCount) { | ||
textNode->hasDescendantTextHighlighter = false; | ||
} | ||
} | ||
|
||
bool TextHighlighterVisitor::RequiresTextHighlighter(Color foregroundColor, Color backgroundColor) { | ||
return backgroundColor || m_backgroundColors.top() && foregroundColor; | ||
} | ||
|
||
} // namespace Microsoft::ReactNative |
37 changes: 37 additions & 0 deletions
37
vnext/Microsoft.ReactNative/Views/Text/TextHighlighterVisitor.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#pragma once | ||
|
||
#include <Utils/TextTransform.h> | ||
#include <stack> | ||
#include "TextVisitor.h" | ||
|
||
namespace Microsoft::ReactNative { | ||
|
||
class TextHighlighterVisitor : public TextVisitor { | ||
using Super = TextVisitor; | ||
using Color = std::optional<winrt::Windows::UI::Color>; | ||
|
||
public: | ||
TextHighlighterVisitor(Color foregroundColor, Color backgroundColor) : Super() { | ||
m_foregroundColors.push(foregroundColor); | ||
m_backgroundColors.push(backgroundColor); | ||
} | ||
|
||
std::vector<xaml::Documents::TextHighlighter> highlighters{}; | ||
|
||
protected: | ||
void VisitRawText(ShadowNodeBase *node) override; | ||
|
||
void VisitVirtualText(ShadowNodeBase *node) override; | ||
|
||
private: | ||
int m_startIndex{0}; | ||
std::stack<Color> m_foregroundColors; | ||
std::stack<Color> m_backgroundColors; | ||
|
||
bool RequiresTextHighlighter(Color foregroundColor, Color backgroundColor); | ||
}; | ||
|
||
} // namespace Microsoft::ReactNative |
12 changes: 12 additions & 0 deletions
12
vnext/Microsoft.ReactNative/Views/Text/TextParentVisitor.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#include "TextParentVisitor.h" | ||
|
||
namespace Microsoft::ReactNative { | ||
|
||
void TextParentVisitor::VisitCore(ShadowNodeBase *node) { | ||
Visit(GetShadowNode(node->m_parent)); | ||
} | ||
|
||
} // namespace Microsoft::ReactNative |
19 changes: 19 additions & 0 deletions
19
vnext/Microsoft.ReactNative/Views/Text/TextParentVisitor.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#pragma once | ||
|
||
#include "TextVisitor.h" | ||
|
||
namespace Microsoft::ReactNative { | ||
|
||
class TextParentVisitor : public TextVisitor { | ||
using Super = TextVisitor; | ||
|
||
protected: | ||
void VisitCore(ShadowNodeBase *node) override; | ||
|
||
void VisitText(ShadowNodeBase *node) override{}; | ||
}; | ||
|
||
} // namespace Microsoft::ReactNative |
69 changes: 69 additions & 0 deletions
69
vnext/Microsoft.ReactNative/Views/Text/TextPropertyChangedParentVisitor.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
#include "TextPropertyChangedParentVisitor.h" | ||
#include <UI.Xaml.Automation.Peers.h> | ||
#include <UI.Xaml.Automation.h> | ||
#include <UI.Xaml.Controls.h> | ||
#include <UI.Xaml.Documents.h> | ||
#include <Utils/ShadowNodeTypeUtils.h> | ||
#include <Views/TextViewManager.h> | ||
#include <Views/VirtualTextViewManager.h> | ||
|
||
namespace winrt { | ||
using namespace xaml::Automation; | ||
using namespace xaml::Automation::Peers; | ||
using namespace xaml::Documents; | ||
} // namespace winrt | ||
|
||
namespace Microsoft::ReactNative { | ||
void TextPropertyChangedParentVisitor::VisitCore(ShadowNodeBase *node) { | ||
// Update nested flag fast text updates | ||
m_isNested = !IsRawTextShadowNode(node); | ||
Super::VisitCore(node); | ||
} | ||
|
||
void TextPropertyChangedParentVisitor::VisitText(ShadowNodeBase *node) { | ||
// Raise LiveRegionChanged event | ||
const auto isTextUpdate = HasPropertyChangeType(PropertyChangeType::Text); | ||
if (isTextUpdate) { | ||
const auto element = node->GetView().as<xaml::Controls::TextBlock>(); | ||
|
||
// If name is set, it's controlled by accessibilityLabel, and it's already | ||
// handled in FrameworkElementViewManager. Here it only handles when name is | ||
// not set. | ||
if (xaml::Automation::AutomationProperties::GetLiveSetting(element) != winrt::AutomationLiveSetting::Off && | ||
xaml::Automation::AutomationProperties::GetName(element).empty() && | ||
xaml::Automation::AutomationProperties::GetAccessibilityView(element) != winrt::AccessibilityView::Raw) { | ||
if (auto peer = xaml::Automation::Peers::FrameworkElementAutomationPeer::FromElement(element)) { | ||
peer.RaiseAutomationEvent(winrt::AutomationEvents::LiveRegionChanged); | ||
} | ||
} | ||
|
||
// Update fast text content | ||
if (!m_isNested && node->m_children.size() == 1) { | ||
if (const auto childNode = GetShadowNode(node->m_children[0])) { | ||
const auto run = static_cast<ShadowNodeBase *>(childNode)->GetView().as<winrt::Run>(); | ||
element.Text(run.Text()); | ||
} | ||
} | ||
} | ||
|
||
// Refresh text highlighters | ||
const auto isHighlightAdded = HasPropertyChangeType(PropertyChangeType::AddHighlight); | ||
const auto isHighlightRemoved = HasPropertyChangeType(PropertyChangeType::RemoveHighlight); | ||
if (isTextUpdate || isHighlightAdded || isHighlightRemoved) { | ||
TextViewManager::UpdateTextHighlighters(node, isHighlightAdded); | ||
} | ||
} | ||
|
||
void TextPropertyChangedParentVisitor::VisitVirtualText(ShadowNodeBase *node) { | ||
// Update descendant text highlight flag | ||
if (HasPropertyChangeType(PropertyChangeType::AddHighlight)) { | ||
static_cast<VirtualTextShadowNode *>(node)->hasDescendantTextHighlighter = true; | ||
} | ||
|
||
Super::VisitVirtualText(node); | ||
} | ||
|
||
} // namespace Microsoft::ReactNative |
Oops, something went wrong.