Skip to content

Commit

Permalink
Adds TextPropertyChangedParentVisitor for Text tree traversals when t…
Browse files Browse the repository at this point in the history
…ext or highlights are updated
  • Loading branch information
rozele committed Aug 23, 2021
1 parent f0a67e3 commit 6c4ca2d
Show file tree
Hide file tree
Showing 17 changed files with 165 additions and 129 deletions.
2 changes: 2 additions & 0 deletions vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,7 @@
<ClInclude Include="Views\TextViewManager.h" />
<ClInclude Include="Views\Text\TextHighlighterVisitor.h" />
<ClInclude Include="Views\Text\TextParentVisitor.h" />
<ClInclude Include="Views\Text\TextPropertyChangedParentVisitor.h" />
<ClInclude Include="Views\Text\TextTransformParentVisitor.h" />
<ClInclude Include="Views\Text\TextTransformVisitor.h" />
<ClInclude Include="Views\Text\TextVisitorScope.h" />
Expand Down Expand Up @@ -655,6 +656,7 @@
<ClCompile Include="Views\TextViewManager.cpp" />
<ClCompile Include="Views\Text\TextHighlighterVisitor.cpp" />
<ClCompile Include="Views\Text\TextParentVisitor.cpp" />
<ClCompile Include="Views\Text\TextPropertyChangedParentVisitor.cpp" />
<ClCompile Include="Views\Text\TextTransformParentVisitor.cpp" />
<ClCompile Include="Views\Text\TextTransformVisitor.cpp" />
<ClCompile Include="Views\Text\TextVisitor.cpp" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,9 @@
<ClCompile Include="Views\Text\TextParentVisitor.cpp">
<Filter>Views\Text</Filter>
</ClCompile>
<ClCompile Include="Views\Text\TextPropertyChangedParentVisitor.cpp">
<Filter>Views\Text</Filter>
</ClCompile>
<ClCompile Include="Views\Text\TextTransformParentVisitor.cpp">
<Filter>Views\Text</Filter>
</ClCompile>
Expand Down Expand Up @@ -693,6 +696,9 @@
<ClInclude Include="Views\Text\TextParentVisitor.h">
<Filter>Views\Text</Filter>
</ClInclude>
<ClInclude Include="Views\Text\TextPropertyChangedParentVisitor.h">
<Filter>Views\Text</Filter>
</ClInclude>
<ClInclude Include="Views\Text\TextTransformParentVisitor.h">
<Filter>Views\Text</Filter>
</ClInclude>
Expand Down
35 changes: 1 addition & 34 deletions vnext/Microsoft.ReactNative/Views/RawTextViewManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,46 +54,13 @@ bool RawTextViewManager::UpdateProperty(
run.Text(asHstring(propertyValue));
static_cast<RawTextShadowNode *>(nodeToUpdate)->originalText = winrt::hstring{};
ApplyTextTransformToChild(nodeToUpdate);
NotifyAncestorsTextChanged(nodeToUpdate);
NotifyAncestorsTextPropertyChanged(nodeToUpdate, PropertyChangeType::Text);
} else {
return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue);
}
return true;
}

void RawTextViewManager::NotifyAncestorsTextChanged(ShadowNodeBase *nodeToUpdate) {
if (auto uiManager = GetNativeUIManager(GetReactContext()).lock()) {
auto host = uiManager->getHost();
ShadowNodeBase *parent = static_cast<ShadowNodeBase *>(host->FindShadowNodeForTag(nodeToUpdate->GetParent()));
auto isNested = false;
while (parent) {
auto viewManager = parent->GetViewManager();
const auto nodeType = viewManager->GetName();
if (IsTextShadowNode(parent)) {
const auto textViewManager = static_cast<TextViewManager *>(viewManager);
if (!isNested && parent->m_children.size() == 1) {
auto view = parent->GetView();
auto textBlock = view.try_as<winrt::TextBlock>();
if (textBlock != nullptr) {
const auto run = nodeToUpdate->GetView().try_as<winrt::Run>();
if (run != nullptr) {
textBlock.Text(run.Text());
}
}
}

(static_cast<TextViewManager *>(viewManager))->OnDescendantTextPropertyChanged(parent);

// We have reached the parent TextBlock, so there're no more parent <Text> elements in this tree.
break;
}

parent = static_cast<ShadowNodeBase *>(host->FindShadowNodeForTag(parent->GetParent()));
isNested = true;
}
}
}

void RawTextViewManager::SetLayoutProps(
ShadowNodeBase & /*nodeToUpdate*/,
const XamlView & /*viewToUpdate*/,
Expand Down
3 changes: 0 additions & 3 deletions vnext/Microsoft.ReactNative/Views/RawTextViewManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,6 @@ class RawTextViewManager : public ViewManagerBase {
const winrt::Microsoft::ReactNative::JSValue &propertyValue) override;

XamlView CreateViewCore(int64_t tag, const winrt::Microsoft::ReactNative::JSValueObject &) override;

private:
void NotifyAncestorsTextChanged(ShadowNodeBase *nodeToUpdate);
};

} // namespace Microsoft::ReactNative
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ void TextHighlighterVisitor::VisitRawText(ShadowNodeBase *node) {

void TextHighlighterVisitor::VisitVirtualText(ShadowNodeBase *node) {
const auto textNode = static_cast<VirtualTextShadowNode *>(node);
const auto foregroundColor = textNode->m_foregroundColor;
const auto backgroundColor = textNode->m_backgroundColor;
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};
Expand All @@ -41,7 +41,7 @@ void TextHighlighterVisitor::VisitVirtualText(ShadowNodeBase *node) {
highlighter.Ranges().Append({startIndex, m_startIndex - startIndex});
highlighters.push_back(highlighter);
} else if (highlighters.size() == initialHighlighterCount) {
textNode->m_hasDescendantBackgroundColor = false;
textNode->hasDescendantTextHighlighter = false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void TextParentVisitor::VisitVirtualText(ShadowNodeBase *node) {
}

void TextParentVisitor::VisitParent(ShadowNodeBase *node) {
Visit(m_uiManager->getHost()->FindShadowNodeForTag(node->m_parent));
Visit(GetShadowNode(node->m_parent));
}

} // namespace Microsoft::ReactNative
2 changes: 1 addition & 1 deletion vnext/Microsoft.ReactNative/Views/Text/TextParentVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class TextParentVisitor : public TextVisitor {

void VisitRawText(ShadowNodeBase *node) override;

void VisitText(ShadowNodeBase *node) override {};
void VisitText(ShadowNodeBase *node) override{};

void VisitVirtualText(ShadowNodeBase *node) override;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#include "TextPropertyChangedParentVisitor.h"
#include <UI.Xaml.Automation.Peers.h>
#include <UI.Xaml.Automation.h>
#include <Views/TextViewManager.h>
#include <Views/VirtualTextViewManager.h>

namespace winrt {
using namespace xaml::Automation;
using namespace xaml::Automation::Peers;
} // namespace winrt

namespace Microsoft::ReactNative {
void TextPropertyChangedParentVisitor::VisitExtensionText(ShadowNodeBase *node) {
// Update nested flag for fast text updates
m_isNested = true;
Super::VisitExtensionText(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::Peers::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);
}

Super::VisitText(node);
}

void TextPropertyChangedParentVisitor::VisitVirtualText(ShadowNodeBase *node) {
// Update nested flag for fast text updates
m_isNested = true;

// Update descendant text highlight flag
if (HasPropertyChangeType(PropertyChangeType::AddHighlight)) {
static_cast<VirtualTextShadowNode *>(node)->hasDescendantTextHighlighter = true;
}

Super::VisitVirtualText(node);
}

} // namespace Microsoft::ReactNative
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

#pragma once

#include <Views/TextViewManager.h>
#include "TextParentVisitor.h"

namespace Microsoft::ReactNative {

enum class PropertyChangeType : std::uint_fast8_t {
None = 0,
Text = 1 << 0,
AddHighlight = 1 << 1,
RemoveHighlight = 1 << 2,
};

DEFINE_ENUM_FLAG_OPERATORS(PropertyChangeType);

class TextPropertyChangedParentVisitor : public TextParentVisitor {
using Super = TextParentVisitor;

public:
TextPropertyChangedParentVisitor(PropertyChangeType type) : m_propertyChangeType{type} {}

protected:
void VisitExtensionText(ShadowNodeBase *node) override;

void VisitText(ShadowNodeBase *node) override;

void VisitVirtualText(ShadowNodeBase *node) override;

private:
PropertyChangeType m_propertyChangeType;
bool m_isNested{false};

bool HasPropertyChangeType(PropertyChangeType type) {
return (m_propertyChangeType & type) == type;
}
};

} // namespace Microsoft::ReactNative
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class TextTransformParentVisitor : public TextParentVisitor {

public:
TextTransform textTransform;

protected:
void VisitText(ShadowNodeBase *node) override;

Expand Down
6 changes: 5 additions & 1 deletion vnext/Microsoft.ReactNative/Views/Text/TextVisitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,13 @@ void TextVisitor::VisitVirtualText(ShadowNodeBase *node) {
VisitChildren(node);
}

ShadowNode *TextVisitor::GetShadowNode(int64_t tag) {
return m_uiManager->getHost()->FindShadowNodeForTag(tag);
}

void TextVisitor::VisitChildren(ShadowNodeBase *node) {
for (auto childTag : node->m_children) {
Visit(m_uiManager->getHost()->FindShadowNodeForTag(childTag));
Visit(GetShadowNode(childTag));
}
}

Expand Down
6 changes: 4 additions & 2 deletions vnext/Microsoft.ReactNative/Views/Text/TextVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ class TextVisitor {
void Visit(ShadowNode *node);

protected:
std::shared_ptr<NativeUIManager> m_uiManager;

virtual void VisitExtensionText(ShadowNodeBase *node);

virtual void VisitRawText(ShadowNodeBase *node) {}
Expand All @@ -25,7 +23,11 @@ class TextVisitor {

virtual void VisitVirtualText(ShadowNodeBase *node);

ShadowNode *GetShadowNode(int64_t tag);

private:
std::shared_ptr<NativeUIManager> m_uiManager;

void VisitChildren(ShadowNodeBase *node);
std::shared_ptr<NativeUIManager> EnsureNativeUIManager(ShadowNode *node);
};
Expand Down
8 changes: 7 additions & 1 deletion vnext/Microsoft.ReactNative/Views/Text/TextVisitors.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#pragma once

#include "TextHighlighterVisitor.h"
#include "TextPropertyChangedParentVisitor.h"
#include "TextTransformParentVisitor.h"
#include "TextTransformVisitor.h"

Expand All @@ -24,11 +25,16 @@ static inline void ApplyTextTransformToChild(ShadowNode *node) {
visitor.Visit(node);
}

static inline void UpdateTextTransformForChildren(ShadowNode* node) {
static inline void UpdateTextTransformForChildren(ShadowNode *node) {
TextTransformParentVisitor parentVisitor;
parentVisitor.Visit(node);
TextTransformVisitor visitor{parentVisitor.textTransform, true};
visitor.Visit(node);
}

static inline void NotifyAncestorsTextPropertyChanged(ShadowNode *node, PropertyChangeType type) {
TextPropertyChangedParentVisitor visitor{type};
visitor.Visit(node);
}

} // namespace Microsoft::ReactNative
Loading

0 comments on commit 6c4ca2d

Please sign in to comment.