diff --git a/change/react-native-windows-2020-04-11-01-26-56-unimplNotify.json b/change/react-native-windows-2020-04-11-01-26-56-unimplNotify.json new file mode 100644 index 00000000000..52b5d38224f --- /dev/null +++ b/change/react-native-windows-2020-04-11-01-26-56-unimplNotify.json @@ -0,0 +1,8 @@ +{ + "type": "prerelease", + "comment": "Refactor View managers to detect unimplemented props", + "packageName": "react-native-windows", + "email": "asklar@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-04-11T08:26:56.395Z" +} \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index 1fdf5719fdc..0029edaedf3 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -87,6 +87,9 @@ + + $(GeneratedFilesDir);$(IncludePath) + Use @@ -535,7 +538,7 @@ - + RNW_PKG_VERSION_STR=$(RNW_PKG_VERSION_STR); RNW_PKG_VERSION_MAJOR=$(RNW_PKG_VERSION_MAJOR); diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index c0062f0dcdd..ebbec354228 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -741,4 +741,7 @@ {b0b76275-4a16-4939-87ee-f777cb189114} + + + \ No newline at end of file diff --git a/vnext/Microsoft.ReactNative/RedBox.cpp b/vnext/Microsoft.ReactNative/RedBox.cpp index 61257fdf86e..30e1f259c44 100644 --- a/vnext/Microsoft.ReactNative/RedBox.cpp +++ b/vnext/Microsoft.ReactNative/RedBox.cpp @@ -8,13 +8,18 @@ #include #include #include +#include #include #include #include +#include #include #include "Unicode.h" +namespace xaml = winrt::Windows::UI::Xaml; +using namespace winrt::Windows::Foundation; + namespace Mso::React { struct RedBox : public std::enable_shared_from_this { @@ -47,7 +52,7 @@ struct RedBox : public std::enable_shared_from_this { void ShowNewJSError() noexcept { m_showing = true; - m_popup = winrt::Windows::UI::Xaml::Controls::Primitives::Popup{}; + m_popup = xaml::Controls::Primitives::Popup{}; const winrt::hstring xamlString = L" { L" " L" " L" " - L" " + L" " L" " L" " L" " @@ -76,30 +81,27 @@ struct RedBox : public std::enable_shared_from_this { L" " L""; - m_redboxContent = winrt::unbox_value( - winrt::Windows::UI::Xaml::Markup::XamlReader::Load(xamlString)); - m_errorMessageText = - m_redboxContent.FindName(L"ErrorMessageText").as(); - m_errorStackText = m_redboxContent.FindName(L"ErrorStackText").as(); - m_stackPanel = m_redboxContent.FindName(L"StackPanel").as(); - m_dismissButton = m_redboxContent.FindName(L"DismissButton").as(); - m_reloadButton = m_redboxContent.FindName(L"ReloadButton").as(); + m_redboxContent = winrt::unbox_value(xaml::Markup::XamlReader::Load(xamlString)); + m_errorMessageText = m_redboxContent.FindName(L"ErrorMessageText").as(); + m_errorStackText = m_redboxContent.FindName(L"ErrorStackText").as(); + m_stackPanel = m_redboxContent.FindName(L"StackPanel").as(); + m_stackPanelUpper = m_redboxContent.FindName(L"StackPanelUpper").as(); + m_dismissButton = m_redboxContent.FindName(L"DismissButton").as(); + m_reloadButton = m_redboxContent.FindName(L"ReloadButton").as(); m_tokenDismiss = m_dismissButton.Click([&]( - winrt::Windows::Foundation::IInspectable const & /*sender*/, - winrt::Windows::UI::Xaml::RoutedEventArgs const & /*args*/) noexcept { Dismiss(); }); + IInspectable const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept { Dismiss(); }); - m_tokenReload = m_reloadButton.Click([&]( - auto const & /*sender*/, winrt::Windows::UI::Xaml::RoutedEventArgs const & /*args*/) noexcept { + m_tokenReload = m_reloadButton.Click([&](auto const & /*sender*/, xaml::RoutedEventArgs const & /*args*/) noexcept { Dismiss(); Reload(); }); PopulateFrameStackUI(); - UpdateErorrMessageUI(); + UpdateErrorMessageUI(); // Do some kind of sizing logic... - auto window = winrt::Windows::UI::Xaml::Window::Current(); + auto window = xaml::Window::Current(); auto windowBounds = window.Bounds(); m_redboxContent.MaxHeight(windowBounds.Height); m_redboxContent.Height(windowBounds.Height); @@ -111,7 +113,7 @@ struct RedBox : public std::enable_shared_from_this { }); m_tokenClosed = m_popup.Closed([wkThis = std::weak_ptr(shared_from_this())]( - auto const & /*sender*/, winrt::Windows::Foundation::IInspectable const & /*args*/) noexcept { + auto const & /*sender*/, IInspectable const & /*args*/) noexcept { if (auto pthis = wkThis.lock()) { pthis->OnPopupClosed(); } @@ -132,7 +134,7 @@ struct RedBox : public std::enable_shared_from_this { m_showing = false; m_dismissButton.Click(m_tokenDismiss); m_reloadButton.Click(m_tokenReload); - winrt::Windows::UI::Xaml::Window::Current().SizeChanged(m_tokenSizeChanged); + xaml::Window::Current().SizeChanged(m_tokenSizeChanged); m_popup.Closed(m_tokenClosed); m_onClosedCallback(GetId()); } @@ -142,7 +144,20 @@ struct RedBox : public std::enable_shared_from_this { } private: - void UpdateErorrMessageUI() noexcept { + static bool IsMetroBundlerError(const std::string &message, const std::string &type) { + // This string must be kept in sync with the one in formatBundlingError in + // node_modules\metro\src\lib\formatBundlingError.js + if (message.find_first_of("Metro Bundler has encountered an error") != message.npos) { + return true; + } + return false; + } + +#define METRO_TROUBLESHOOTING_URL "http://aka.ms/RNWTroubleshootMetro" +#define _MAKE_WIDE_STR(x) L##x +#define MAKE_WIDE_STR(x) _MAKE_WIDE_STR(x) + + void UpdateErrorMessageUI() noexcept { try { auto json = folly::parseJson(m_errorInfo.Message); if (json.count("name") && json["name"] == "Error") { @@ -151,8 +166,83 @@ struct RedBox : public std::enable_shared_from_this { m_errorMessageText.Text(Microsoft::Common::Unicode::Utf8ToUtf16(message)); m_errorStackText.Text(Microsoft::Common::Unicode::Utf8ToUtf16(stack)); return; + } else if (json.count("type") && json["type"] == "InternalError") { + auto message = json["message"].asString(); + m_errorMessageText.Text(Microsoft::Common::Unicode::Utf8ToUtf16(message)); + + if (IsMetroBundlerError(message, json["type"].asString())) { + xaml::Documents::Hyperlink link; + link.NavigateUri(Uri(MAKE_WIDE_STR(METRO_TROUBLESHOOTING_URL))); + xaml::Documents::Run linkRun; + + linkRun.Text(Microsoft::Common::Unicode::Utf8ToUtf16(METRO_TROUBLESHOOTING_URL)); + link.Foreground( + xaml::Media::SolidColorBrush(winrt::Windows::UI::ColorHelper::FromArgb(0xff, 0xff, 0xff, 0xff))); + link.Inlines().Append(linkRun); + xaml::Documents::Run normalRun; + normalRun.Text(Microsoft::Common::Unicode::Utf8ToUtf16(json["type"].asString() + (" ─ See "))); + m_errorStackText.Inlines().Append(normalRun); + m_errorStackText.Inlines().Append(link); + } else { + m_errorStackText.Text(Microsoft::Common::Unicode::Utf8ToUtf16(json["type"].asString())); + } + return; } } catch (...) { + std::string doctype = ""; + if (boost::istarts_with(m_errorInfo.Message, doctype)) { + auto webView = xaml::Controls::WebView(xaml::Controls::WebViewExecutionMode::SameThread); + + winrt::hstring content( + Microsoft::Common::Unicode::Utf8ToUtf16(m_errorInfo.Message.substr(doctype.length()).c_str())); + + webView.HorizontalAlignment(xaml::HorizontalAlignment::Stretch); + webView.VerticalAlignment(xaml::VerticalAlignment::Stretch); + webView.MinWidth(400); + + m_stackPanel.Children().Clear(); + m_stackPanel.Children().Append(webView); + + auto dispatcher = winrt::Windows::System::DispatcherQueue::GetForCurrentThread(); + // XAML doesn't currently provide a way to measure a WebView control, + // So we're going to tell the WebView to measure itself by running some javascript, + // and then we'll post a task back to XAML to set the XAML WebView minimum height. + // The HTML we get from Metro doesn't have any styling, so we'll take advantage of + // the fact that we're running javascript in the WebView, to set the + // foreground/background to match the rest of the RedBox. + // setProperty returns undefined so we continue the first expression with a comma + // whereas the height expression gets executed because of the || + // (since the setProperty calls resulted in undefined). + // Finally, it's important to note that JS expressions of that are not of string type + // need to be manually converted to string for them to get marshaled properly back here. + webView.NavigationCompleted( + [=](IInspectable const &, xaml::Controls::WebViewNavigationCompletedEventArgs const &) { + // #eecc0000 ARGB is #be0000 RGB (background doesn't seem to allow alpha channel in WebView) + std::vector args{ + L"(document.body.style.setProperty('color', 'white'), " + L"document.body.style.setProperty('background', '#be0000')) " + L"|| document.documentElement.scrollHeight.toString()"}; + auto async = webView.InvokeScriptAsync(L"eval", std::move(args)); + + async.Completed([=](IAsyncOperation const &op, auto &&state) { + auto result = op.GetResults(); + int documentHeight = _wtoi(result.c_str()); + dispatcher.TryEnqueue([=]() { + // Ensure the webview has a min height of the content it wants to show, + // and that the horizontal scrollbar that might exist, doesn't occlude the webview. + constexpr int horizontalScrollbarHeight = 12; + webView.MinHeight(documentHeight + horizontalScrollbarHeight); + }); + }); + }); + + webView.NavigateToString(content); + + m_stackPanel.Margin(xaml::ThicknessHelper::FromUniformLength(0)); + m_stackPanelUpper.Visibility(xaml::Visibility::Collapsed); + + return; + } } // fall back to displaying the raw message string const std::regex colorsRegex( @@ -173,16 +263,15 @@ struct RedBox : public std::enable_shared_from_this { L" " L" " L""; - auto frameContent = winrt::unbox_value( - winrt::Windows::UI::Xaml::Markup::XamlReader::Load(xamlFrameString)); - auto methodText = frameContent.FindName(L"MethodText").as(); - auto frameText = frameContent.FindName(L"FrameText").as(); + auto frameContent = + winrt::unbox_value(xaml::Markup::XamlReader::Load(xamlFrameString)); + auto methodText = frameContent.FindName(L"MethodText").as(); + auto frameText = frameContent.FindName(L"FrameText").as(); methodText.Text(Microsoft::Common::Unicode::Utf8ToUtf16(frame.Method)); // When the user taps on a stack frame, tell the bundler to load that source in the users editor of choice frameContent.Tapped([weakReactHost = m_weakReactHost, f = frame]( - winrt::Windows::Foundation::IInspectable const & /*sender*/, - winrt::Windows::UI::Xaml::Input::TappedRoutedEventArgs const & /*e*/) { + IInspectable const & /*sender*/, xaml::Input::TappedRoutedEventArgs const & /*e*/) { if (auto reactHost = weakReactHost.GetStrongPtr()) { auto devSettings = reactHost->Options().DeveloperSettings; std::string stackFrameUri = "http://"; @@ -191,7 +280,7 @@ struct RedBox : public std::enable_shared_from_this { stackFrameUri.append(devSettings.SourceBundlePath.empty() ? "8081" : devSettings.SourceBundlePort); stackFrameUri.append("/open-stack-frame"); - winrt::Windows::Foundation::Uri uri{Microsoft::Common::Unicode::Utf8ToUtf16(stackFrameUri)}; + Uri uri{Microsoft::Common::Unicode::Utf8ToUtf16(stackFrameUri)}; winrt::Windows::Web::Http::HttpClient httpClient{}; winrt::Windows::Data::Json::JsonObject payload{}; @@ -226,13 +315,14 @@ struct RedBox : public std::enable_shared_from_this { } } - winrt::Windows::UI::Xaml::Controls::StackPanel m_stackPanel{nullptr}; - winrt::Windows::UI::Xaml::Controls::Primitives::Popup m_popup{nullptr}; - winrt::Windows::UI::Xaml::Controls::Grid m_redboxContent{nullptr}; - winrt::Windows::UI::Xaml::Controls::Button m_dismissButton{nullptr}; - winrt::Windows::UI::Xaml::Controls::Button m_reloadButton{nullptr}; - winrt::Windows::UI::Xaml::Controls::TextBlock m_errorMessageText{nullptr}; - winrt::Windows::UI::Xaml::Controls::TextBlock m_errorStackText{nullptr}; + xaml::Controls::StackPanel m_stackPanelUpper{nullptr}; + xaml::Controls::StackPanel m_stackPanel{nullptr}; + xaml::Controls::Primitives::Popup m_popup{nullptr}; + xaml::Controls::Grid m_redboxContent{nullptr}; + xaml::Controls::Button m_dismissButton{nullptr}; + xaml::Controls::Button m_reloadButton{nullptr}; + xaml::Controls::TextBlock m_errorMessageText{nullptr}; + xaml::Controls::TextBlock m_errorStackText{nullptr}; bool m_showing = false; Mso::Functor m_onClosedCallback; diff --git a/vnext/ReactUWP/Polyester/ButtonViewManager.cpp b/vnext/ReactUWP/Polyester/ButtonViewManager.cpp index 903485902b6..573ac85bda4 100644 --- a/vnext/ReactUWP/Polyester/ButtonViewManager.cpp +++ b/vnext/ReactUWP/Polyester/ButtonViewManager.cpp @@ -73,25 +73,23 @@ XamlView ButtonViewManager::CreateViewCore(int64_t /*tag*/) { return button; } -void ButtonViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool ButtonViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto button = nodeToUpdate->GetView().as(); if (button == nullptr) - return; + return true; - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "disabled") { - if (propertyValue.isBool()) - button.IsEnabled(!propertyValue.asBool()); - } - - continue; + if (propertyName == "disabled") { + if (propertyValue.isBool()) + button.IsEnabled(!propertyValue.asBool()); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } + } // namespace polyester } // namespace uwp } // namespace react diff --git a/vnext/ReactUWP/Polyester/ButtonViewManager.h b/vnext/ReactUWP/Polyester/ButtonViewManager.h index ec3d2e32a13..d3a3f28c714 100644 --- a/vnext/ReactUWP/Polyester/ButtonViewManager.h +++ b/vnext/ReactUWP/Polyester/ButtonViewManager.h @@ -24,9 +24,12 @@ class ButtonViewManager : public ContentControlViewManager { folly::dynamic GetExportedCustomDirectEventTypeConstants() const override; facebook::react::ShadowNode *createShadow() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; - protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; }; diff --git a/vnext/ReactUWP/Polyester/HyperlinkViewManager.cpp b/vnext/ReactUWP/Polyester/HyperlinkViewManager.cpp index da1abda7099..3754b765de2 100644 --- a/vnext/ReactUWP/Polyester/HyperlinkViewManager.cpp +++ b/vnext/ReactUWP/Polyester/HyperlinkViewManager.cpp @@ -60,31 +60,31 @@ folly::dynamic HyperlinkViewManager::GetExportedCustomDirectEventTypeConstants() return directEvents; } -void HyperlinkViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool HyperlinkViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto button = nodeToUpdate->GetView().as(); if (button == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "disabled") { - if (propertyValue.isBool()) - button.IsEnabled(!propertyValue.asBool()); - } else if (propertyName == "tooltip") { - if (propertyValue.isString()) { - winrt::TextBlock tooltip = winrt::TextBlock(); - tooltip.Text(asHstring(propertyValue)); - winrt::ToolTipService::SetToolTip(button, tooltip); - } - } else if (propertyName == "url") { - winrt::Uri myUri(asHstring(propertyValue)); - button.NavigateUri(myUri); + return true; + + if (propertyName == "disabled") { + if (propertyValue.isBool()) + button.IsEnabled(!propertyValue.asBool()); + } else if (propertyName == "tooltip") { + if (propertyValue.isString()) { + winrt::TextBlock tooltip = winrt::TextBlock(); + tooltip.Text(asHstring(propertyValue)); + winrt::ToolTipService::SetToolTip(button, tooltip); } + } else if (propertyName == "url") { + winrt::Uri myUri(asHstring(propertyValue)); + button.NavigateUri(myUri); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } } // namespace polyester diff --git a/vnext/ReactUWP/Polyester/HyperlinkViewManager.h b/vnext/ReactUWP/Polyester/HyperlinkViewManager.h index 4e2c016151a..d276c8a746d 100644 --- a/vnext/ReactUWP/Polyester/HyperlinkViewManager.h +++ b/vnext/ReactUWP/Polyester/HyperlinkViewManager.h @@ -19,9 +19,12 @@ class HyperlinkViewManager : public ContentControlViewManager { folly::dynamic GetNativeProps() const override; folly::dynamic GetExportedCustomDirectEventTypeConstants() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; - protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; }; diff --git a/vnext/ReactUWP/Views/ActivityIndicatorViewManager.cpp b/vnext/ReactUWP/Views/ActivityIndicatorViewManager.cpp index ef4337efc34..1e4dfcd711b 100644 --- a/vnext/ReactUWP/Views/ActivityIndicatorViewManager.cpp +++ b/vnext/ReactUWP/Views/ActivityIndicatorViewManager.cpp @@ -31,24 +31,23 @@ XamlView ActivityIndicatorViewManager::CreateViewCore(int64_t /*tag*/) { return progressRing; } -void ActivityIndicatorViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool ActivityIndicatorViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto progressRing = nodeToUpdate->GetView().as(); if (progressRing == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "animating") { - if (propertyValue.isBool()) - progressRing.IsActive(propertyValue.asBool()); - else if (pair.second.isNull()) - progressRing.ClearValue(winrt::ProgressRing::IsActiveProperty()); - } + return true; + + if (propertyName == "animating") { + if (propertyValue.isBool()) + progressRing.IsActive(propertyValue.asBool()); + else if (propertyValue.isNull()) + progressRing.ClearValue(winrt::ProgressRing::IsActiveProperty()); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyName); } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } } // namespace uwp diff --git a/vnext/ReactUWP/Views/ActivityIndicatorViewManager.h b/vnext/ReactUWP/Views/ActivityIndicatorViewManager.h index 73443158b6f..90e63997811 100644 --- a/vnext/ReactUWP/Views/ActivityIndicatorViewManager.h +++ b/vnext/ReactUWP/Views/ActivityIndicatorViewManager.h @@ -17,9 +17,12 @@ class ActivityIndicatorViewManager : public ControlViewManager { const char *GetName() const override; folly::dynamic GetNativeProps() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; - protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; }; diff --git a/vnext/ReactUWP/Views/CheckboxViewManager.cpp b/vnext/ReactUWP/Views/CheckboxViewManager.cpp index 78ebd313123..7710229b8ff 100644 --- a/vnext/ReactUWP/Views/CheckboxViewManager.cpp +++ b/vnext/ReactUWP/Views/CheckboxViewManager.cpp @@ -85,29 +85,28 @@ XamlView CheckBoxViewManager::CreateViewCore(int64_t /*tag*/) { return checkbox; } -void CheckBoxViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool CheckBoxViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto checkbox = nodeToUpdate->GetView().as(); if (checkbox == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "disabled") { - if (propertyValue.isBool()) - checkbox.IsEnabled(!propertyValue.asBool()); - else if (pair.second.isNull()) - checkbox.ClearValue(winrt::Control::IsEnabledProperty()); - } else if (propertyName == "value") { - if (propertyValue.isBool()) - checkbox.IsChecked(propertyValue.asBool()); - else if (pair.second.isNull()) - checkbox.ClearValue(winrt::ToggleButton::IsCheckedProperty()); - } + return true; + + if (propertyName == "disabled") { + if (propertyValue.isBool()) + checkbox.IsEnabled(!propertyValue.asBool()); + else if (propertyValue.isNull()) + checkbox.ClearValue(winrt::Control::IsEnabledProperty()); + } else if (propertyName == "value") { + if (propertyValue.isBool()) + checkbox.IsChecked(propertyValue.asBool()); + else if (propertyValue.isNull()) + checkbox.ClearValue(winrt::ToggleButton::IsCheckedProperty()); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } } // namespace uwp diff --git a/vnext/ReactUWP/Views/CheckboxViewManager.h b/vnext/ReactUWP/Views/CheckboxViewManager.h index 82be7007828..581aa2f8c01 100644 --- a/vnext/ReactUWP/Views/CheckboxViewManager.h +++ b/vnext/ReactUWP/Views/CheckboxViewManager.h @@ -19,9 +19,12 @@ class CheckBoxViewManager : public ControlViewManager { facebook::react::ShadowNode *createShadow() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; - protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; friend class CheckBoxShadowNode; diff --git a/vnext/ReactUWP/Views/ControlViewManager.cpp b/vnext/ReactUWP/Views/ControlViewManager.cpp index e0bbc4fc1e4..3e359854e09 100644 --- a/vnext/ReactUWP/Views/ControlViewManager.cpp +++ b/vnext/ReactUWP/Views/ControlViewManager.cpp @@ -39,53 +39,48 @@ void ControlViewManager::TransferProperties(XamlView oldView, XamlView newView) Super::TransferProperties(oldView, newView); } -void ControlViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool ControlViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto control(nodeToUpdate->GetView().as()); bool implementsPadding = nodeToUpdate->ImplementsPadding(); bool finalizeBorderRadius{false}; + bool ret = true; if (control != nullptr) { - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (TryUpdateBackgroundBrush(control, propertyName, propertyValue)) { - continue; - } else if (TryUpdateBorderProperties(nodeToUpdate, control, propertyName, propertyValue)) { - continue; - } else if (TryUpdateForeground(control, propertyName, propertyValue)) { - continue; - } else if (TryUpdateCornerRadiusOnNode(nodeToUpdate, control, propertyName, propertyValue)) { - finalizeBorderRadius = true; - continue; - } else if (implementsPadding && TryUpdatePadding(nodeToUpdate, control, propertyName, propertyValue)) { - continue; - } else if (propertyName == "tabIndex") { - if (propertyValue.isNumber()) { - auto tabIndex = propertyValue.asDouble(); - if (tabIndex == static_cast(tabIndex)) { - if (tabIndex < 0) { - control.IsTabStop(false); - control.ClearValue(winrt::Control::TabIndexProperty()); - } else { - control.IsTabStop(true); - control.TabIndex(static_cast(tabIndex)); - } + if (TryUpdateBackgroundBrush(control, propertyName, propertyValue)) { + } else if (TryUpdateBorderProperties(nodeToUpdate, control, propertyName, propertyValue)) { + } else if (TryUpdateForeground(control, propertyName, propertyValue)) { + } else if (TryUpdateCornerRadiusOnNode(nodeToUpdate, control, propertyName, propertyValue)) { + finalizeBorderRadius = true; + } else if (implementsPadding && TryUpdatePadding(nodeToUpdate, control, propertyName, propertyValue)) { + } else if (propertyName == "tabIndex") { + if (propertyValue.isNumber()) { + auto tabIndex = propertyValue.asDouble(); + if (tabIndex == static_cast(tabIndex)) { + if (tabIndex < 0) { + control.IsTabStop(false); + control.ClearValue(winrt::Control::TabIndexProperty()); + } else { + control.IsTabStop(true); + control.TabIndex(static_cast(tabIndex)); } - } else if (propertyValue.isNull()) { - control.ClearValue(winrt::Control::TabIndexProperty()); } + } else if (propertyValue.isNull()) { + control.ClearValue(winrt::Control::TabIndexProperty()); } + } else { + ret = Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } } - Super::UpdateProperties(nodeToUpdate, reactDiffMap); - if (finalizeBorderRadius && control.try_as()) { // Control.CornerRadius is only supported on >= RS5, setting borderRadius on Controls have no effect < RS5 UpdateCornerRadiusOnElement(nodeToUpdate, control); } + return ret; } void ControlViewManager::OnViewCreated(XamlView view) { diff --git a/vnext/ReactUWP/Views/FrameworkElementViewManager.cpp b/vnext/ReactUWP/Views/FrameworkElementViewManager.cpp index 6146329cc0c..f1c223c7632 100644 --- a/vnext/ReactUWP/Views/FrameworkElementViewManager.cpp +++ b/vnext/ReactUWP/Views/FrameworkElementViewManager.cpp @@ -150,326 +150,313 @@ folly::dynamic FrameworkElementViewManager::GetNativeProps() const { return props; } -void FrameworkElementViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool FrameworkElementViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto element(nodeToUpdate->GetView().as()); if (element != nullptr) { - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "opacity") { - if (propertyValue.isNumber()) { - double opacity = propertyValue.asDouble(); - if (opacity >= 0 && opacity <= 1) - element.Opacity(opacity); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::UIElement::OpacityProperty()); - continue; - } - } else if (propertyName == "transform") { - if (element.try_as()) // Works on 19H1+ - { - if (propertyValue.isArray()) { - assert(propertyValue.size() == 16); - winrt::Windows::Foundation::Numerics::float4x4 transformMatrix; - transformMatrix.m11 = static_cast(propertyValue[0].asDouble()); - transformMatrix.m12 = static_cast(propertyValue[1].asDouble()); - transformMatrix.m13 = static_cast(propertyValue[2].asDouble()); - transformMatrix.m14 = static_cast(propertyValue[3].asDouble()); - transformMatrix.m21 = static_cast(propertyValue[4].asDouble()); - transformMatrix.m22 = static_cast(propertyValue[5].asDouble()); - transformMatrix.m23 = static_cast(propertyValue[6].asDouble()); - transformMatrix.m24 = static_cast(propertyValue[7].asDouble()); - transformMatrix.m31 = static_cast(propertyValue[8].asDouble()); - transformMatrix.m32 = static_cast(propertyValue[9].asDouble()); - transformMatrix.m33 = static_cast(propertyValue[10].asDouble()); - transformMatrix.m34 = static_cast(propertyValue[11].asDouble()); - transformMatrix.m41 = static_cast(propertyValue[12].asDouble()); - transformMatrix.m42 = static_cast(propertyValue[13].asDouble()); - transformMatrix.m43 = static_cast(propertyValue[14].asDouble()); - transformMatrix.m44 = static_cast(propertyValue[15].asDouble()); - - ApplyTransformMatrix(element, nodeToUpdate, transformMatrix); - } else if (propertyValue.isNull()) { - element.TransformMatrix(winrt::Windows::Foundation::Numerics::float4x4::identity()); - } - } - } else if (propertyName == "width") { - if (propertyValue.isNumber()) { - double width = propertyValue.asDouble(); - if (width >= 0) - element.Width(width); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::FrameworkElement::WidthProperty()); - continue; - } - - } else if (propertyName == "height") { - if (propertyValue.isNumber()) { - double height = propertyValue.asDouble(); - if (height >= 0) - element.Height(height); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::FrameworkElement::HeightProperty()); - continue; - } - } else if (propertyName == "minWidth") { - if (propertyValue.isNumber()) { - double minWidth = propertyValue.asDouble(); - if (minWidth >= 0) - element.MinWidth(minWidth); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::FrameworkElement::MinWidthProperty()); - continue; - } - } else if (propertyName == "maxWidth") { - if (propertyValue.isNumber()) { - double maxWidth = propertyValue.asDouble(); - if (maxWidth >= 0) - element.MaxWidth(maxWidth); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::FrameworkElement::MaxWidthProperty()); - continue; - } - - } else if (propertyName == "minHeight") { - if (propertyValue.isNumber()) { - double minHeight = propertyValue.asDouble(); - if (minHeight >= 0) - element.MinHeight(minHeight); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::FrameworkElement::MinHeightProperty()); - continue; - } - } else if (propertyName == "maxHeight") { - if (propertyValue.isNumber()) { - double maxHeight = propertyValue.asDouble(); - if (maxHeight >= 0) - element.MaxHeight(maxHeight); - // else - // TODO report error - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::FrameworkElement::MaxHeightProperty()); - continue; - } - - } else if (propertyName == "accessibilityHint") { - if (propertyValue.isString()) { - auto value = react::uwp::asHstring(propertyValue); - auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateString(value); - - element.SetValue(winrt::AutomationProperties::HelpTextProperty(), boxedValue); - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::AutomationProperties::HelpTextProperty()); - } - } else if (propertyName == "accessibilityLabel") { - if (propertyValue.isString()) { - auto value = react::uwp::asHstring(propertyValue); - auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateString(value); - - element.SetValue(winrt::AutomationProperties::NameProperty(), boxedValue); + if (propertyName == "opacity") { + if (propertyValue.isNumber()) { + double opacity = propertyValue.asDouble(); + if (opacity >= 0 && opacity <= 1) + element.Opacity(opacity); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::UIElement::OpacityProperty()); + } + } else if (propertyName == "transform") { + if (element.try_as()) // Works on 19H1+ + { + if (propertyValue.isArray()) { + assert(propertyValue.size() == 16); + winrt::Windows::Foundation::Numerics::float4x4 transformMatrix; + transformMatrix.m11 = static_cast(propertyValue[0].asDouble()); + transformMatrix.m12 = static_cast(propertyValue[1].asDouble()); + transformMatrix.m13 = static_cast(propertyValue[2].asDouble()); + transformMatrix.m14 = static_cast(propertyValue[3].asDouble()); + transformMatrix.m21 = static_cast(propertyValue[4].asDouble()); + transformMatrix.m22 = static_cast(propertyValue[5].asDouble()); + transformMatrix.m23 = static_cast(propertyValue[6].asDouble()); + transformMatrix.m24 = static_cast(propertyValue[7].asDouble()); + transformMatrix.m31 = static_cast(propertyValue[8].asDouble()); + transformMatrix.m32 = static_cast(propertyValue[9].asDouble()); + transformMatrix.m33 = static_cast(propertyValue[10].asDouble()); + transformMatrix.m34 = static_cast(propertyValue[11].asDouble()); + transformMatrix.m41 = static_cast(propertyValue[12].asDouble()); + transformMatrix.m42 = static_cast(propertyValue[13].asDouble()); + transformMatrix.m43 = static_cast(propertyValue[14].asDouble()); + transformMatrix.m44 = static_cast(propertyValue[15].asDouble()); + + ApplyTransformMatrix(element, nodeToUpdate, transformMatrix); } else if (propertyValue.isNull()) { - element.ClearValue(winrt::AutomationProperties::NameProperty()); - } - AnnounceLiveRegionChangedIfNeeded(element); - } else if (propertyName == "accessible") { - if (propertyValue.isBool()) { - if (!propertyValue.asBool()) - winrt::AutomationProperties::SetAccessibilityView(element, winrt::Peers::AccessibilityView::Raw); + element.TransformMatrix(winrt::Windows::Foundation::Numerics::float4x4::identity()); } - } else if (propertyName == "accessibilityLiveRegion") { - if (propertyValue.isString()) { - auto value = propertyValue.getString(); + } + } else if (propertyName == "width") { + if (propertyValue.isNumber()) { + double width = propertyValue.asDouble(); + if (width >= 0) + element.Width(width); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::FrameworkElement::WidthProperty()); + } - auto liveSetting = winrt::AutomationLiveSetting::Off; + } else if (propertyName == "height") { + if (propertyValue.isNumber()) { + double height = propertyValue.asDouble(); + if (height >= 0) + element.Height(height); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::FrameworkElement::HeightProperty()); + } + } else if (propertyName == "minWidth") { + if (propertyValue.isNumber()) { + double minWidth = propertyValue.asDouble(); + if (minWidth >= 0) + element.MinWidth(minWidth); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::FrameworkElement::MinWidthProperty()); + } + } else if (propertyName == "maxWidth") { + if (propertyValue.isNumber()) { + double maxWidth = propertyValue.asDouble(); + if (maxWidth >= 0) + element.MaxWidth(maxWidth); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::FrameworkElement::MaxWidthProperty()); + } - if (value == "polite") { - liveSetting = winrt::AutomationLiveSetting::Polite; - } else if (value == "assertive") { - liveSetting = winrt::AutomationLiveSetting::Assertive; - } + } else if (propertyName == "minHeight") { + if (propertyValue.isNumber()) { + double minHeight = propertyValue.asDouble(); + if (minHeight >= 0) + element.MinHeight(minHeight); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::FrameworkElement::MinHeightProperty()); + } + } else if (propertyName == "maxHeight") { + if (propertyValue.isNumber()) { + double maxHeight = propertyValue.asDouble(); + if (maxHeight >= 0) + element.MaxHeight(maxHeight); + // else + // TODO report error + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::FrameworkElement::MaxHeightProperty()); + } - element.SetValue(winrt::AutomationProperties::LiveSettingProperty(), winrt::box_value(liveSetting)); - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::AutomationProperties::LiveSettingProperty()); - } - AnnounceLiveRegionChangedIfNeeded(element); - } else if (propertyName == "accessibilityPosInSet") { - if (propertyValue.isNumber()) { - auto value = static_cast(propertyValue.asDouble()); - auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateInt32(value); + } else if (propertyName == "accessibilityHint") { + if (propertyValue.isString()) { + auto value = react::uwp::asHstring(propertyValue); + auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateString(value); - element.SetValue(winrt::AutomationProperties::PositionInSetProperty(), boxedValue); - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::AutomationProperties::PositionInSetProperty()); - } - } else if (propertyName == "accessibilitySetSize") { - if (propertyValue.isNumber()) { - auto value = static_cast(propertyValue.asDouble()); - auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateInt32(value); + element.SetValue(winrt::AutomationProperties::HelpTextProperty(), boxedValue); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::AutomationProperties::HelpTextProperty()); + } + } else if (propertyName == "accessibilityLabel") { + if (propertyValue.isString()) { + auto value = react::uwp::asHstring(propertyValue); + auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateString(value); + + element.SetValue(winrt::AutomationProperties::NameProperty(), boxedValue); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::AutomationProperties::NameProperty()); + } + AnnounceLiveRegionChangedIfNeeded(element); + } else if (propertyName == "accessible") { + if (propertyValue.isBool()) { + if (!propertyValue.asBool()) + winrt::AutomationProperties::SetAccessibilityView(element, winrt::Peers::AccessibilityView::Raw); + } + } else if (propertyName == "accessibilityLiveRegion") { + if (propertyValue.isString()) { + auto value = propertyValue.getString(); - element.SetValue(winrt::AutomationProperties::SizeOfSetProperty(), boxedValue); - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::AutomationProperties::SizeOfSetProperty()); - } - } else if (propertyName == "accessibilityRole") { - if (propertyValue.isString()) { - const std::string &role = propertyValue.getString(); - if (role == "none") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::None); - else if (role == "button") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Button); - else if (role == "link") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Link); - else if (role == "search") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Search); - else if (role == "image") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Image); - else if (role == "keyboardkey") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::KeyboardKey); - else if (role == "text") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Text); - else if (role == "adjustable") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::Adjustable); - else if (role == "imagebutton") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::ImageButton); - else if (role == "header") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Header); - else if (role == "summary") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Summary); - else if (role == "alert") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Alert); - else if (role == "checkbox") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::CheckBox); - else if (role == "combobox") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ComboBox); - else if (role == "menu") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Menu); - else if (role == "menubar") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::MenuBar); - else if (role == "menuitem") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::MenuItem); - else if (role == "progressbar") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::ProgressBar); - else if (role == "radio") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Radio); - else if (role == "radiogroup") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::RadioGroup); - else if (role == "scrollbar") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::ScrollBar); - else if (role == "spinbutton") - DynamicAutomationProperties::SetAccessibilityRole( - element, winrt::react::uwp::AccessibilityRoles::SpinButton); - else if (role == "switch") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Switch); - else if (role == "tab") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Tab); - else if (role == "tablist") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::TabList); - else if (role == "timer") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Timer); - else if (role == "toolbar") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ToolBar); - else if (role == "list") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::List); - else if (role == "listitem") - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ListItem); - else - DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Unknown); - } else if (propertyValue.isNull()) { - element.ClearValue(DynamicAutomationProperties::AccessibilityRoleProperty()); - } - } else if (propertyName == "accessibilityStates") { - bool states[static_cast(winrt::react::uwp::AccessibilityStates::CountStates)] = {}; + auto liveSetting = winrt::AutomationLiveSetting::Off; - if (propertyValue.isArray()) { - for (const auto &state : propertyValue) { - if (!state.isString()) - continue; - - if (state.getString() == "selected") - states[static_cast(winrt::react::uwp::AccessibilityStates::Selected)] = true; - else if (state.getString() == "disabled") - states[static_cast(winrt::react::uwp::AccessibilityStates::Disabled)] = true; - else if (state.getString() == "checked") - states[static_cast(winrt::react::uwp::AccessibilityStates::Checked)] = true; - else if (state.getString() == "unchecked") - states[static_cast(winrt::react::uwp::AccessibilityStates::Unchecked)] = true; - else if (state.getString() == "busy") - states[static_cast(winrt::react::uwp::AccessibilityStates::Busy)] = true; - else if (state.getString() == "expanded") - states[static_cast(winrt::react::uwp::AccessibilityStates::Expanded)] = true; - else if (state.getString() == "collapsed") - states[static_cast(winrt::react::uwp::AccessibilityStates::Collapsed)] = true; - } + if (value == "polite") { + liveSetting = winrt::AutomationLiveSetting::Polite; + } else if (value == "assertive") { + liveSetting = winrt::AutomationLiveSetting::Assertive; } - DynamicAutomationProperties::SetAccessibilityStateSelected( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Selected)]); - DynamicAutomationProperties::SetAccessibilityStateDisabled( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Disabled)]); - DynamicAutomationProperties::SetAccessibilityStateChecked( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Checked)]); - DynamicAutomationProperties::SetAccessibilityStateUnchecked( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Unchecked)]); - DynamicAutomationProperties::SetAccessibilityStateBusy( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Busy)]); - DynamicAutomationProperties::SetAccessibilityStateExpanded( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Expanded)]); - DynamicAutomationProperties::SetAccessibilityStateCollapsed( - element, states[static_cast(winrt::react::uwp::AccessibilityStates::Collapsed)]); - } else if (propertyName == "testID") { - if (propertyValue.isString()) { - auto value = react::uwp::asHstring(propertyValue); - auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateString(value); - - element.SetValue(winrt::AutomationProperties::AutomationIdProperty(), boxedValue); - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::AutomationProperties::AutomationIdProperty()); - } - } else if (propertyName == "tooltip") { - if (propertyValue.isString()) { - winrt::TextBlock tooltip = winrt::TextBlock(); - tooltip.Text(asHstring(propertyValue)); - winrt::ToolTipService::SetToolTip(element, tooltip); + element.SetValue(winrt::AutomationProperties::LiveSettingProperty(), winrt::box_value(liveSetting)); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::AutomationProperties::LiveSettingProperty()); + } + AnnounceLiveRegionChangedIfNeeded(element); + } else if (propertyName == "accessibilityPosInSet") { + if (propertyValue.isNumber()) { + auto value = static_cast(propertyValue.asDouble()); + auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateInt32(value); + + element.SetValue(winrt::AutomationProperties::PositionInSetProperty(), boxedValue); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::AutomationProperties::PositionInSetProperty()); + } + } else if (propertyName == "accessibilitySetSize") { + if (propertyValue.isNumber()) { + auto value = static_cast(propertyValue.asDouble()); + auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateInt32(value); + + element.SetValue(winrt::AutomationProperties::SizeOfSetProperty(), boxedValue); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::AutomationProperties::SizeOfSetProperty()); + } + } else if (propertyName == "accessibilityRole") { + if (propertyValue.isString()) { + const std::string &role = propertyValue.getString(); + if (role == "none") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::None); + else if (role == "button") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Button); + else if (role == "link") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Link); + else if (role == "search") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Search); + else if (role == "image") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Image); + else if (role == "keyboardkey") + DynamicAutomationProperties::SetAccessibilityRole( + element, winrt::react::uwp::AccessibilityRoles::KeyboardKey); + else if (role == "text") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Text); + else if (role == "adjustable") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Adjustable); + else if (role == "imagebutton") + DynamicAutomationProperties::SetAccessibilityRole( + element, winrt::react::uwp::AccessibilityRoles::ImageButton); + else if (role == "header") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Header); + else if (role == "summary") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Summary); + else if (role == "alert") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Alert); + else if (role == "checkbox") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::CheckBox); + else if (role == "combobox") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ComboBox); + else if (role == "menu") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Menu); + else if (role == "menubar") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::MenuBar); + else if (role == "menuitem") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::MenuItem); + else if (role == "progressbar") + DynamicAutomationProperties::SetAccessibilityRole( + element, winrt::react::uwp::AccessibilityRoles::ProgressBar); + else if (role == "radio") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Radio); + else if (role == "radiogroup") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::RadioGroup); + else if (role == "scrollbar") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ScrollBar); + else if (role == "spinbutton") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::SpinButton); + else if (role == "switch") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Switch); + else if (role == "tab") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Tab); + else if (role == "tablist") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::TabList); + else if (role == "timer") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Timer); + else if (role == "toolbar") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ToolBar); + else if (role == "list") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::List); + else if (role == "listitem") + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::ListItem); + else + DynamicAutomationProperties::SetAccessibilityRole(element, winrt::react::uwp::AccessibilityRoles::Unknown); + } else if (propertyValue.isNull()) { + element.ClearValue(DynamicAutomationProperties::AccessibilityRoleProperty()); + } + } else if (propertyName == "accessibilityStates") { + bool states[static_cast(winrt::react::uwp::AccessibilityStates::CountStates)] = {}; + + if (propertyValue.isArray()) { + for (const auto &state : propertyValue) { + if (!state.isString()) + continue; + + if (state.getString() == "selected") + states[static_cast(winrt::react::uwp::AccessibilityStates::Selected)] = true; + else if (state.getString() == "disabled") + states[static_cast(winrt::react::uwp::AccessibilityStates::Disabled)] = true; + else if (state.getString() == "checked") + states[static_cast(winrt::react::uwp::AccessibilityStates::Checked)] = true; + else if (state.getString() == "unchecked") + states[static_cast(winrt::react::uwp::AccessibilityStates::Unchecked)] = true; + else if (state.getString() == "busy") + states[static_cast(winrt::react::uwp::AccessibilityStates::Busy)] = true; + else if (state.getString() == "expanded") + states[static_cast(winrt::react::uwp::AccessibilityStates::Expanded)] = true; + else if (state.getString() == "collapsed") + states[static_cast(winrt::react::uwp::AccessibilityStates::Collapsed)] = true; } - } else if (propertyName == "zIndex") { - if (propertyValue.isNumber()) { - auto value = static_cast(propertyValue.asDouble()); - auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateInt32(value); + } - element.SetValue(winrt::Canvas::ZIndexProperty(), boxedValue); - } else if (propertyValue.isNull()) { - element.ClearValue(winrt::Canvas::ZIndexProperty()); - } - } else if (TryUpdateFlowDirection(element, propertyName, propertyValue)) { - continue; - } else if (propertyName == "accessibilityActions") { - auto value = json_type_traits>::parseJson(propertyValue); - DynamicAutomationProperties::SetAccessibilityActions(element, value); + DynamicAutomationProperties::SetAccessibilityStateSelected( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Selected)]); + DynamicAutomationProperties::SetAccessibilityStateDisabled( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Disabled)]); + DynamicAutomationProperties::SetAccessibilityStateChecked( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Checked)]); + DynamicAutomationProperties::SetAccessibilityStateUnchecked( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Unchecked)]); + DynamicAutomationProperties::SetAccessibilityStateBusy( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Busy)]); + DynamicAutomationProperties::SetAccessibilityStateExpanded( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Expanded)]); + DynamicAutomationProperties::SetAccessibilityStateCollapsed( + element, states[static_cast(winrt::react::uwp::AccessibilityStates::Collapsed)]); + } else if (propertyName == "testID") { + if (propertyValue.isString()) { + auto value = react::uwp::asHstring(propertyValue); + auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateString(value); + + element.SetValue(winrt::AutomationProperties::AutomationIdProperty(), boxedValue); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::AutomationProperties::AutomationIdProperty()); } + } else if (propertyName == "tooltip") { + if (propertyValue.isString()) { + winrt::TextBlock tooltip = winrt::TextBlock(); + tooltip.Text(asHstring(propertyValue)); + winrt::ToolTipService::SetToolTip(element, tooltip); + } + } else if (propertyName == "zIndex") { + if (propertyValue.isNumber()) { + auto value = static_cast(propertyValue.asDouble()); + auto boxedValue = winrt::Windows::Foundation::PropertyValue::CreateInt32(value); + + element.SetValue(winrt::Canvas::ZIndexProperty(), boxedValue); + } else if (propertyValue.isNull()) { + element.ClearValue(winrt::Canvas::ZIndexProperty()); + } + } else if (TryUpdateFlowDirection(element, propertyName, propertyValue)) { + } else if (propertyName == "accessibilityActions") { + auto value = json_type_traits>::parseJson(propertyValue); + DynamicAutomationProperties::SetAccessibilityActions(element, value); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } // Applies a TransformMatrix to the backing UIElement. diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp index c5465508007..a0571d5b94b 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.cpp +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.cpp @@ -111,36 +111,36 @@ facebook::react::ShadowNode *ImageViewManager::createShadow() const { return new ImageShadowNode(); } -void ImageViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool ImageViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto grid{nodeToUpdate->GetView().as()}; if (grid == nullptr) - return; + return true; bool finalizeBorderRadius{false}; - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName{pair.first.getString()}; - const folly::dynamic &propertyValue{pair.second}; - - if (propertyName == "source") { - setSource(grid, propertyValue); - } else if (propertyName == "resizeMode") { - auto resizeMode{json_type_traits::parseJson(propertyValue)}; - auto reactImage{grid.as()}; - reactImage->ResizeMode(resizeMode); - } else if (TryUpdateCornerRadiusOnNode(nodeToUpdate, grid, propertyName, propertyValue)) { - finalizeBorderRadius = true; - continue; - } else if (TryUpdateBorderProperties(nodeToUpdate, grid, propertyName, propertyValue)) { - continue; - } + bool ret = true; + + if (propertyName == "source") { + setSource(grid, propertyValue); + } else if (propertyName == "resizeMode") { + auto resizeMode{json_type_traits::parseJson(propertyValue)}; + auto reactImage{grid.as()}; + reactImage->ResizeMode(resizeMode); + } else if (TryUpdateCornerRadiusOnNode(nodeToUpdate, grid, propertyName, propertyValue)) { + finalizeBorderRadius = true; + } else if (TryUpdateBorderProperties(nodeToUpdate, grid, propertyName, propertyValue)) { + } else { + ret = Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); // TODO: overflow } - Super::UpdateProperties(nodeToUpdate, reactDiffMap); - if (finalizeBorderRadius) UpdateCornerRadiusOnElement(nodeToUpdate, grid); + + return ret; } void ImageViewManager::EmitImageEvent(winrt::Grid grid, const char *eventName, ReactImageSource &source) { diff --git a/vnext/ReactUWP/Views/Image/ImageViewManager.h b/vnext/ReactUWP/Views/Image/ImageViewManager.h index a27d18c7eab..88f49a1adf4 100644 --- a/vnext/ReactUWP/Views/Image/ImageViewManager.h +++ b/vnext/ReactUWP/Views/Image/ImageViewManager.h @@ -14,7 +14,6 @@ class ImageViewManager : public FrameworkElementViewManager { ImageViewManager(const std::shared_ptr &reactInstance); const char *GetName() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; folly::dynamic GetExportedCustomDirectEventTypeConstants() const override; folly::dynamic GetNativeProps() const override; @@ -22,6 +21,11 @@ class ImageViewManager : public FrameworkElementViewManager { void EmitImageEvent(winrt::Windows::UI::Xaml::Controls::Grid grid, const char *eventName, ReactImageSource &source); protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; private: diff --git a/vnext/ReactUWP/Views/RawTextViewManager.cpp b/vnext/ReactUWP/Views/RawTextViewManager.cpp index 0b43ed7d075..c8141de02ac 100644 --- a/vnext/ReactUWP/Views/RawTextViewManager.cpp +++ b/vnext/ReactUWP/Views/RawTextViewManager.cpp @@ -38,35 +38,36 @@ XamlView RawTextViewManager::CreateViewCore(int64_t /*tag*/) { return run; } -void RawTextViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool RawTextViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto run = nodeToUpdate->GetView().as(); if (run == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "text") { - run.Text(asHstring(propertyValue)); - if (nodeToUpdate->GetParent() != -1) { - if (auto instance = this->m_wkReactInstance.lock()) { - const ShadowNodeBase *parent = static_cast( - instance->NativeUIManager()->getHost()->FindShadowNodeForTag(nodeToUpdate->GetParent())); - if (parent && parent->m_children.size() == 1) { - auto view = parent->GetView(); - auto textBlock = view.try_as(); - if (textBlock != nullptr) { - textBlock.Text(run.Text()); - } + return true; + + if (propertyName == "text") { + run.Text(asHstring(propertyValue)); + + if (nodeToUpdate->GetParent() != -1) { + if (auto instance = this->m_wkReactInstance.lock()) { + const ShadowNodeBase *parent = static_cast( + instance->NativeUIManager()->getHost()->FindShadowNodeForTag(nodeToUpdate->GetParent())); + if (parent && parent->m_children.size() == 1) { + auto view = parent->GetView(); + auto textBlock = view.try_as(); + if (textBlock != nullptr) { + textBlock.Text(run.Text()); } - - NotifyAncestorsTextChanged(instance.operator->(), nodeToUpdate); } + + NotifyAncestorsTextChanged(instance.operator->(), nodeToUpdate); } } + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } void RawTextViewManager::NotifyAncestorsTextChanged(IReactInstance *instance, ShadowNodeBase *nodeToUpdate) { diff --git a/vnext/ReactUWP/Views/RawTextViewManager.h b/vnext/ReactUWP/Views/RawTextViewManager.h index 78315528cbf..6974cd6b909 100644 --- a/vnext/ReactUWP/Views/RawTextViewManager.h +++ b/vnext/ReactUWP/Views/RawTextViewManager.h @@ -17,7 +17,6 @@ class RawTextViewManager : public ViewManagerBase { RawTextViewManager(const std::shared_ptr &reactInstance); const char *GetName() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; void SetLayoutProps( ShadowNodeBase &nodeToUpdate, @@ -29,6 +28,11 @@ class RawTextViewManager : public ViewManagerBase { bool RequiresYogaNode() const override; protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; private: diff --git a/vnext/ReactUWP/Views/SliderViewManager.cpp b/vnext/ReactUWP/Views/SliderViewManager.cpp index b4c3648a171..4dbb979aaa2 100644 --- a/vnext/ReactUWP/Views/SliderViewManager.cpp +++ b/vnext/ReactUWP/Views/SliderViewManager.cpp @@ -61,29 +61,28 @@ XamlView SliderViewManager::CreateViewCore(int64_t /*tag*/) { return slider; } -void SliderViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool SliderViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto slider = nodeToUpdate->GetView().as(); if (slider == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "disabled") { - if (propertyValue.isBool()) - slider.IsEnabled(!propertyValue.asBool()); - else if (pair.second.isNull()) - slider.ClearValue(winrt::Control::IsEnabledProperty()); - } else if (propertyName == "value") { - if (propertyValue.isNumber()) - slider.Value(propertyValue.asDouble()); - else if (pair.second.isNull()) - slider.Value(0); - } + return true; + + if (propertyName == "disabled") { + if (propertyValue.isBool()) + slider.IsEnabled(!propertyValue.asBool()); + else if (propertyValue.isNull()) + slider.ClearValue(winrt::Control::IsEnabledProperty()); + } else if (propertyName == "value") { + if (propertyValue.isNumber()) + slider.Value(propertyValue.asDouble()); + else if (propertyValue.isNull()) + slider.Value(0); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } } // namespace uwp diff --git a/vnext/ReactUWP/Views/SliderViewManager.h b/vnext/ReactUWP/Views/SliderViewManager.h index c7a1e4b80c4..8d28783cbb7 100644 --- a/vnext/ReactUWP/Views/SliderViewManager.h +++ b/vnext/ReactUWP/Views/SliderViewManager.h @@ -19,9 +19,12 @@ class SliderViewManager : public ControlViewManager { facebook::react::ShadowNode *createShadow() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; - protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; friend class SliderShadowNode; diff --git a/vnext/ReactUWP/Views/SwitchViewManager.cpp b/vnext/ReactUWP/Views/SwitchViewManager.cpp index af5e8fa9c5f..e5ad8a0c5a5 100644 --- a/vnext/ReactUWP/Views/SwitchViewManager.cpp +++ b/vnext/ReactUWP/Views/SwitchViewManager.cpp @@ -136,28 +136,28 @@ XamlView SwitchViewManager::CreateViewCore(int64_t /*tag*/) { return toggleSwitch; } -void SwitchViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool SwitchViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto toggleSwitch = nodeToUpdate->GetView().as(); if (toggleSwitch == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "disabled") { - if (propertyValue.isBool()) - toggleSwitch.IsEnabled(!propertyValue.asBool()); - else if (pair.second.isNull()) - toggleSwitch.ClearValue(winrt::Control::IsEnabledProperty()); - } else if (propertyName == "value") { - if (propertyValue.isBool()) - toggleSwitch.IsOn(propertyValue.asBool()); - else if (pair.second.isNull()) - toggleSwitch.ClearValue(winrt::ToggleSwitch::IsOnProperty()); - } + return true; + + if (propertyName == "disabled") { + if (propertyValue.isBool()) + toggleSwitch.IsEnabled(!propertyValue.asBool()); + else if (propertyValue.isNull()) + toggleSwitch.ClearValue(winrt::Control::IsEnabledProperty()); + } else if (propertyName == "value") { + if (propertyValue.isBool()) + toggleSwitch.IsOn(propertyValue.asBool()); + else if (propertyValue.isNull()) + toggleSwitch.ClearValue(winrt::ToggleSwitch::IsOnProperty()); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } } // namespace uwp diff --git a/vnext/ReactUWP/Views/SwitchViewManager.h b/vnext/ReactUWP/Views/SwitchViewManager.h index d45a273ba12..aaa6089c809 100644 --- a/vnext/ReactUWP/Views/SwitchViewManager.h +++ b/vnext/ReactUWP/Views/SwitchViewManager.h @@ -17,9 +17,13 @@ class SwitchViewManager : public ControlViewManager { const char *GetName() const override; folly::dynamic GetNativeProps() const override; facebook::react::ShadowNode *createShadow() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; friend class SwitchShadowNode; diff --git a/vnext/ReactUWP/Views/TextViewManager.cpp b/vnext/ReactUWP/Views/TextViewManager.cpp index 303f06c8c60..eeee6140d2c 100644 --- a/vnext/ReactUWP/Views/TextViewManager.cpp +++ b/vnext/ReactUWP/Views/TextViewManager.cpp @@ -85,68 +85,61 @@ XamlView TextViewManager::CreateViewCore(int64_t /*tag*/) { return textBlock; } -void TextViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool TextViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto textBlock = nodeToUpdate->GetView().as(); if (textBlock == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (TryUpdateForeground(textBlock, propertyName, propertyValue)) { - continue; - } else if (TryUpdateFontProperties(textBlock, propertyName, propertyValue)) { - continue; - } else if (TryUpdatePadding(nodeToUpdate, textBlock, propertyName, propertyValue)) { - continue; - } else if (TryUpdateTextAlignment(textBlock, propertyName, propertyValue)) { - continue; - } else if (TryUpdateTextTrimming(textBlock, propertyName, propertyValue)) { - continue; - } else if (TryUpdateTextDecorationLine(textBlock, propertyName, propertyValue)) { - continue; - } else if (TryUpdateCharacterSpacing(textBlock, propertyName, propertyValue)) { - continue; - } else if (propertyName == "numberOfLines") { - if (propertyValue.isNumber()) { - auto numberLines = static_cast(propertyValue.asDouble()); - if (numberLines == 1) { - textBlock.TextWrapping(winrt::TextWrapping::NoWrap); // setting no wrap for single line - // text for better trimming - // experience - } else { - textBlock.TextWrapping(winrt::TextWrapping::Wrap); - } - textBlock.MaxLines(numberLines); - } else if (propertyValue.isNull()) { - textBlock.TextWrapping(winrt::TextWrapping::Wrap); // set wrapping back to default - textBlock.ClearValue(winrt::TextBlock::MaxLinesProperty()); + return true; + + if (TryUpdateForeground(textBlock, propertyName, propertyValue)) { + } else if (TryUpdateFontProperties(textBlock, propertyName, propertyValue)) { + } else if (TryUpdatePadding(nodeToUpdate, textBlock, propertyName, propertyValue)) { + } else if (TryUpdateTextAlignment(textBlock, propertyName, propertyValue)) { + } else if (TryUpdateTextTrimming(textBlock, propertyName, propertyValue)) { + } else if (TryUpdateTextDecorationLine(textBlock, propertyName, propertyValue)) { + } else if (TryUpdateCharacterSpacing(textBlock, propertyName, propertyValue)) { + } else if (propertyName == "numberOfLines") { + if (propertyValue.isNumber()) { + auto numberLines = static_cast(propertyValue.asDouble()); + if (numberLines == 1) { + textBlock.TextWrapping(winrt::TextWrapping::NoWrap); // setting no wrap for single line + // text for better trimming + // experience + } else { + textBlock.TextWrapping(winrt::TextWrapping::Wrap); } - } else if (propertyName == "lineHeight") { - if (propertyValue.isNumber()) - textBlock.LineHeight(static_cast(propertyValue.asDouble())); - else if (propertyValue.isNull()) - textBlock.ClearValue(winrt::TextBlock::LineHeightProperty()); - } else if (propertyName == "selectable") { - if (propertyValue.isBool()) - textBlock.IsTextSelectionEnabled(propertyValue.asBool()); - else if (propertyValue.isNull()) - textBlock.ClearValue(winrt::TextBlock::IsTextSelectionEnabledProperty()); - } else if (propertyName == "allowFontScaling") { - if (propertyValue.isBool()) - textBlock.IsTextScaleFactorEnabled(propertyValue.asBool()); - else - textBlock.ClearValue(winrt::TextBlock::IsTextScaleFactorEnabledProperty()); - } else if (propertyName == "selectionColor") { - if (IsValidColorValue(propertyValue)) { - textBlock.SelectionHighlightColor(SolidColorBrushFrom(propertyValue)); - } else - textBlock.ClearValue(winrt::TextBlock::SelectionHighlightColorProperty()); + textBlock.MaxLines(numberLines); + } else if (propertyValue.isNull()) { + textBlock.TextWrapping(winrt::TextWrapping::Wrap); // set wrapping back to default + textBlock.ClearValue(winrt::TextBlock::MaxLinesProperty()); } + } else if (propertyName == "lineHeight") { + if (propertyValue.isNumber()) + textBlock.LineHeight(static_cast(propertyValue.asDouble())); + else if (propertyValue.isNull()) + textBlock.ClearValue(winrt::TextBlock::LineHeightProperty()); + } else if (propertyName == "selectable") { + if (propertyValue.isBool()) + textBlock.IsTextSelectionEnabled(propertyValue.asBool()); + else if (propertyValue.isNull()) + textBlock.ClearValue(winrt::TextBlock::IsTextSelectionEnabledProperty()); + } else if (propertyName == "allowFontScaling") { + if (propertyValue.isBool()) { + textBlock.IsTextScaleFactorEnabled(propertyValue.asBool()); + } else { + textBlock.ClearValue(winrt::TextBlock::IsTextScaleFactorEnabledProperty()); + } + } else if (propertyName == "selectionColor") { + if (IsValidColorValue(propertyValue)) { + textBlock.SelectionHighlightColor(SolidColorBrushFrom(propertyValue)); + } else + textBlock.ClearValue(winrt::TextBlock::SelectionHighlightColorProperty()); + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } void TextViewManager::AddView(XamlView parent, XamlView child, int64_t index) { diff --git a/vnext/ReactUWP/Views/TextViewManager.h b/vnext/ReactUWP/Views/TextViewManager.h index 01b95b937fd..6adb5eac74c 100644 --- a/vnext/ReactUWP/Views/TextViewManager.h +++ b/vnext/ReactUWP/Views/TextViewManager.h @@ -17,7 +17,6 @@ class TextViewManager : public FrameworkElementViewManager { facebook::react::ShadowNode *createShadow() const override; const char *GetName() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; void AddView(XamlView parent, XamlView child, int64_t index) override; void RemoveAllChildren(XamlView parent) override; @@ -28,6 +27,11 @@ class TextViewManager : public FrameworkElementViewManager { void OnDescendantTextPropertyChanged(ShadowNodeBase *node); protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; }; diff --git a/vnext/ReactUWP/Views/ViewManagerBase.cpp b/vnext/ReactUWP/Views/ViewManagerBase.cpp index e29e68c7f88..38feb176955 100644 --- a/vnext/ReactUWP/Views/ViewManagerBase.cpp +++ b/vnext/ReactUWP/Views/ViewManagerBase.cpp @@ -6,12 +6,15 @@ #include #include "ViewPanel.h" +#include "cdebug.h" +#include "unicode.h" #include #include #include #include +#include #include using namespace folly; @@ -226,17 +229,28 @@ void ViewManagerBase::UpdateProperties(ShadowNodeBase *nodeToUpdate, const dynam for (const auto &pair : reactDiffMap.items()) { const std::string &propertyName = pair.first.getString(); const folly::dynamic &propertyValue = pair.second; - - if (propertyName == "onLayout") { - nodeToUpdate->m_onLayout = !propertyValue.isNull() && propertyValue.asBool(); - } else if (propertyName == "keyDownEvents") { - nodeToUpdate->UpdateHandledKeyboardEvents(propertyName, propertyValue); - } else if (propertyName == "keyUpEvents") { - nodeToUpdate->UpdateHandledKeyboardEvents(propertyName, propertyValue); + if (!UpdateProperty(nodeToUpdate, propertyName, propertyValue)) { + NotifyUnimplementedProperty(nodeToUpdate, propertyName, propertyValue); } } } +bool ViewManagerBase::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { + if (propertyName == "onLayout") { + nodeToUpdate->m_onLayout = !propertyValue.isNull() && propertyValue.asBool(); + } else if (propertyName == "keyDownEvents") { + nodeToUpdate->UpdateHandledKeyboardEvents(propertyName, propertyValue); + } else if (propertyName == "keyUpEvents") { + nodeToUpdate->UpdateHandledKeyboardEvents(propertyName, propertyValue); + } else { + return false; + } + return true; +} + void ViewManagerBase::TransferProperties(XamlView /*oldView*/, XamlView /*newView*/) {} void ViewManagerBase::DispatchCommand( @@ -246,6 +260,60 @@ void ViewManagerBase::DispatchCommand( assert(false); // View did not handle its command } +void ViewManagerBase::NotifyUnimplementedProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &value) { +#ifdef DEBUG + auto viewManagerName = nodeToUpdate->GetViewManager()->GetName(); + auto element(nodeToUpdate->GetView().as()); + + if (element != nullptr) { + auto className = Microsoft::Common::Unicode::Utf16ToUtf8(winrt::get_class_name(element)); + TestHook::NotifyUnimplementedProperty(viewManagerName, className, propertyName, value); + } else { + cdebug << "[NonIInspectable] viewManagerName = " << viewManagerName << std::endl; + } +#endif // DEBUG +} + +#ifdef DEBUG + +void ViewManagerBase::TestHook::NotifyUnimplementedProperty( + const std::string &viewManager, + const std::string &reactClassName, + const std::string &propertyName, + const folly::dynamic &propertyValue) { + std::string value; + size_t size{}; + try { + if (propertyValue.isObject()) { + value = "[Object]"; + } else if (propertyValue.isNull()) { + value = "[Null]"; + } else if (propertyValue.isArray()) { + size = propertyValue.size(); + value = "[Array]"; + } else { + value = propertyValue.asString(); + } + } catch (const TypeError &e) { + value = e.what(); + } + + cdebug << "[UnimplementedProperty] ViewManager = " << viewManager << " elementClass = " << reactClassName + << " propertyName = " << propertyName << " value = " << value; + + if (size != 0) { + cdebug << " (" << size << " elems)"; + } + + cdebug << std::endl; + // DebugBreak(); +} + +#endif // DEBUG + void ViewManagerBase::SetLayoutProps( ShadowNodeBase &nodeToUpdate, XamlView viewToUpdate, diff --git a/vnext/ReactUWP/Views/ViewViewManager.cpp b/vnext/ReactUWP/Views/ViewViewManager.cpp index 1a19bb24260..438d98c8b1d 100644 --- a/vnext/ReactUWP/Views/ViewViewManager.cpp +++ b/vnext/ReactUWP/Views/ViewViewManager.cpp @@ -332,62 +332,56 @@ folly::dynamic ViewViewManager::GetNativeProps() const { return props; } -void ViewViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool ViewViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto *pViewShadowNode = static_cast(nodeToUpdate); bool shouldBeControl = pViewShadowNode->IsControl(); bool finalizeBorderRadius{false}; auto pPanel = pViewShadowNode->GetViewPanel(); - + bool ret = true; if (pPanel != nullptr) { - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - if (TryUpdateBackgroundBrush(pPanel, propertyName, propertyValue)) { - continue; - } else if (TryUpdateBorderProperties(nodeToUpdate, pPanel, propertyName, propertyValue)) { - continue; - } else if (TryUpdateCornerRadiusOnNode(nodeToUpdate, pPanel, propertyName, propertyValue)) { - finalizeBorderRadius = true; - continue; - } else if (TryUpdateMouseEvents(nodeToUpdate, propertyName, propertyValue)) { - continue; - } else if (propertyName == "onClick") { - pViewShadowNode->OnClick(!propertyValue.isNull() && propertyValue.asBool()); - } else if (propertyName == "overflow") { - if (propertyValue.isString()) { - bool clipChildren = propertyValue.getString() == "hidden"; - pPanel.ClipChildren(clipChildren); - } - } else if (propertyName == "pointerEvents") { - if (propertyValue.isString()) { - bool hitTestable = propertyValue.getString() != "none"; - pPanel.IsHitTestVisible(hitTestable); - } - } else if (propertyName == "acceptsKeyboardFocus") { - if (propertyValue.isBool()) - shouldBeControl = propertyValue.getBool(); - } else if (propertyName == "enableFocusRing") { - if (propertyValue.isBool()) - pViewShadowNode->EnableFocusRing(propertyValue.getBool()); - else if (propertyValue.isNull()) - pViewShadowNode->EnableFocusRing(false); - } else if (propertyName == "tabIndex") { - if (propertyValue.isNumber()) { - auto tabIndex = propertyValue.asDouble(); - if (tabIndex == static_cast(tabIndex)) { - pViewShadowNode->TabIndex(static_cast(tabIndex)); - } - } else if (propertyValue.isNull()) { - pViewShadowNode->TabIndex(-1); + if (TryUpdateBackgroundBrush(pPanel, propertyName, propertyValue)) { + } else if (TryUpdateBorderProperties(nodeToUpdate, pPanel, propertyName, propertyValue)) { + } else if (TryUpdateCornerRadiusOnNode(nodeToUpdate, pPanel, propertyName, propertyValue)) { + finalizeBorderRadius = true; + } else if (TryUpdateMouseEvents(nodeToUpdate, propertyName, propertyValue)) { + } else if (propertyName == "onClick") { + pViewShadowNode->OnClick(!propertyValue.isNull() && propertyValue.asBool()); + } else if (propertyName == "overflow") { + if (propertyValue.isString()) { + bool clipChildren = propertyValue.getString() == "hidden"; + pPanel.ClipChildren(clipChildren); + } + } else if (propertyName == "pointerEvents") { + if (propertyValue.isString()) { + bool hitTestable = propertyValue.getString() != "none"; + pPanel.IsHitTestVisible(hitTestable); + } + } else if (propertyName == "acceptsKeyboardFocus") { + if (propertyValue.isBool()) + shouldBeControl = propertyValue.getBool(); + } else if (propertyName == "enableFocusRing") { + if (propertyValue.isBool()) + pViewShadowNode->EnableFocusRing(propertyValue.getBool()); + else if (propertyValue.isNull()) + pViewShadowNode->EnableFocusRing(false); + } else if (propertyName == "tabIndex") { + if (propertyValue.isNumber()) { + auto tabIndex = propertyValue.asDouble(); + if (tabIndex == static_cast(tabIndex)) { + pViewShadowNode->TabIndex(static_cast(tabIndex)); } + } else if (propertyValue.isNull()) { + pViewShadowNode->TabIndex(-1); } + } else { + ret = Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } } - Super::UpdateProperties(nodeToUpdate, reactDiffMap); - if (auto view = pViewShadowNode->GetView().try_as()) { // If we have DynamicAutomationProperties, we need a ViewControl with a // DynamicAutomationPeer @@ -400,6 +394,7 @@ void ViewViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly pPanel.FinalizeProperties(); TryUpdateView(pViewShadowNode, pPanel, shouldBeControl); + return ret; } void ViewViewManager::TryUpdateView( diff --git a/vnext/ReactUWP/Views/ViewViewManager.h b/vnext/ReactUWP/Views/ViewViewManager.h index f197f89c6ac..4cb537bb215 100644 --- a/vnext/ReactUWP/Views/ViewViewManager.h +++ b/vnext/ReactUWP/Views/ViewViewManager.h @@ -27,8 +27,6 @@ class ViewViewManager : public FrameworkElementViewManager { folly::dynamic GetExportedCustomDirectEventTypeConstants() const override; facebook::react::ShadowNode *createShadow() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; - // Yoga Layout void SetLayoutProps( ShadowNodeBase &nodeToUpdate, @@ -39,6 +37,11 @@ class ViewViewManager : public FrameworkElementViewManager { float height) override; protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; void TryUpdateView(ViewShadowNode *viewShadowNode, winrt::react::uwp::ViewPanel &pPanel, bool useControl); }; diff --git a/vnext/ReactUWP/Views/VirtualTextViewManager.cpp b/vnext/ReactUWP/Views/VirtualTextViewManager.cpp index abadf727a81..5b8f9c49b95 100644 --- a/vnext/ReactUWP/Views/VirtualTextViewManager.cpp +++ b/vnext/ReactUWP/Views/VirtualTextViewManager.cpp @@ -33,29 +33,24 @@ XamlView VirtualTextViewManager::CreateViewCore(int64_t /*tag*/) { return winrt::Span(); } -void VirtualTextViewManager::UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) { +bool VirtualTextViewManager::UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) { auto span = nodeToUpdate->GetView().as(); if (span == nullptr) - return; - - for (const auto &pair : reactDiffMap.items()) { - const std::string &propertyName = pair.first.getString(); - const folly::dynamic &propertyValue = pair.second; - - // FUTURE: In the future cppwinrt will generate code where static methods on - // base types can be called. For now we specify the base type explicitly - if (TryUpdateForeground(span, propertyName, propertyValue)) { - continue; - } else if (TryUpdateFontProperties(span, propertyName, propertyValue)) { - continue; - } else if (TryUpdateCharacterSpacing(span, propertyName, propertyValue)) { - continue; - } else if (TryUpdateTextDecorationLine(span, propertyName, propertyValue)) { - continue; - } + return true; + + // FUTURE: In the future cppwinrt will generate code where static methods on + // base types can be called. For now we specify the base type explicitly + if (TryUpdateForeground(span, propertyName, propertyValue)) { + } else if (TryUpdateFontProperties(span, propertyName, propertyValue)) { + } else if (TryUpdateCharacterSpacing(span, propertyName, propertyValue)) { + } else if (TryUpdateTextDecorationLine(span, propertyName, propertyValue)) { + } else { + return Super::UpdateProperty(nodeToUpdate, propertyName, propertyValue); } - - Super::UpdateProperties(nodeToUpdate, reactDiffMap); + return true; } void VirtualTextViewManager::AddView(XamlView parent, XamlView child, int64_t index) { diff --git a/vnext/ReactUWP/Views/VirtualTextViewManager.h b/vnext/ReactUWP/Views/VirtualTextViewManager.h index 26d9d5f2df8..4881f429cfc 100644 --- a/vnext/ReactUWP/Views/VirtualTextViewManager.h +++ b/vnext/ReactUWP/Views/VirtualTextViewManager.h @@ -15,7 +15,6 @@ class VirtualTextViewManager : public ViewManagerBase { VirtualTextViewManager(const std::shared_ptr &reactInstance); const char *GetName() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; void AddView(XamlView parent, XamlView child, int64_t index) override; void RemoveAllChildren(XamlView parent) override; @@ -24,6 +23,11 @@ class VirtualTextViewManager : public ViewManagerBase { bool RequiresYogaNode() const override; protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + XamlView CreateViewCore(int64_t tag) override; }; diff --git a/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj b/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj index 582f47afc90..33d57837c25 100644 --- a/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj +++ b/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj @@ -173,6 +173,7 @@ + @@ -238,4 +239,4 @@ - + \ No newline at end of file diff --git a/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters b/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters index e6204ac0b12..fd860c6c427 100644 --- a/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters +++ b/vnext/ReactWindowsCore/ReactWindowsCore.vcxproj.filters @@ -114,6 +114,9 @@ Source Files + + Source Files + diff --git a/vnext/ReactWindowsCore/cdebug.cpp b/vnext/ReactWindowsCore/cdebug.cpp new file mode 100644 index 00000000000..794f5188e6e --- /dev/null +++ b/vnext/ReactWindowsCore/cdebug.cpp @@ -0,0 +1,4 @@ +#include "cdebug.h" + +basic_dostream cdebug; +basic_dostream cwdebug; diff --git a/vnext/include/Include.vcxitems b/vnext/include/Include.vcxitems index 194a9dbe378..4c2f792e6c0 100644 --- a/vnext/include/Include.vcxitems +++ b/vnext/include/Include.vcxitems @@ -39,6 +39,7 @@ + diff --git a/vnext/include/Include.vcxitems.filters b/vnext/include/Include.vcxitems.filters index 9e5b13ff3fd..7752ef2e130 100644 --- a/vnext/include/Include.vcxitems.filters +++ b/vnext/include/Include.vcxitems.filters @@ -20,6 +20,9 @@ ReactUWP + + ReactWindowsCore + ReactWindowsCore diff --git a/vnext/include/ReactUWP/Views/ControlViewManager.h b/vnext/include/ReactUWP/Views/ControlViewManager.h index 72f6c14eba0..0d1c4949eae 100644 --- a/vnext/include/ReactUWP/Views/ControlViewManager.h +++ b/vnext/include/ReactUWP/Views/ControlViewManager.h @@ -17,7 +17,10 @@ class REACTWINDOWS_EXPORT ControlViewManager : public FrameworkElementViewManage ControlViewManager(const std::shared_ptr &reactInstance); folly::dynamic GetNativeProps() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; void TransferProperties(XamlView oldView, XamlView newView) override; protected: diff --git a/vnext/include/ReactUWP/Views/FrameworkElementViewManager.h b/vnext/include/ReactUWP/Views/FrameworkElementViewManager.h index 891afa8d6c1..0bf111a8183 100644 --- a/vnext/include/ReactUWP/Views/FrameworkElementViewManager.h +++ b/vnext/include/ReactUWP/Views/FrameworkElementViewManager.h @@ -16,7 +16,6 @@ class REACTWINDOWS_EXPORT FrameworkElementViewManager : public ViewManagerBase { FrameworkElementViewManager(const std::shared_ptr &reactInstance); folly::dynamic GetNativeProps() const override; - void UpdateProperties(ShadowNodeBase *nodeToUpdate, const folly::dynamic &reactDiffMap) override; // Helper functions related to setting/updating TransformMatrix void RefreshTransformMatrix(ShadowNodeBase *shadowNode); @@ -27,6 +26,11 @@ class REACTWINDOWS_EXPORT FrameworkElementViewManager : public ViewManagerBase { virtual void TransferProperties(XamlView oldView, XamlView newView) override; protected: + bool UpdateProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &propertyValue) override; + void TransferProperty(XamlView oldView, XamlView newView, winrt::Windows::UI::Xaml::DependencyProperty dp); void TransferProperty( diff --git a/vnext/include/ReactUWP/Views/ViewManagerBase.h b/vnext/include/ReactUWP/Views/ViewManagerBase.h index bb1fc6b06a0..17f2e2ae6c0 100644 --- a/vnext/include/ReactUWP/Views/ViewManagerBase.h +++ b/vnext/include/ReactUWP/Views/ViewManagerBase.h @@ -80,7 +80,21 @@ class REACTWINDOWS_EXPORT ViewManagerBase : public facebook::react::IViewManager protected: virtual XamlView CreateViewCore(int64_t tag) = 0; virtual void OnViewCreated(XamlView view){}; - + virtual bool + UpdateProperty(ShadowNodeBase *nodeToUpdate, const std::string &propertyName, const folly::dynamic &propertyValue); + virtual void NotifyUnimplementedProperty( + ShadowNodeBase *nodeToUpdate, + const std::string &propertyName, + const folly::dynamic &value); +#ifdef DEBUG + struct TestHook { + static void NotifyUnimplementedProperty( + const std::string &viewManager, + const std::string &reactClassName, + const std::string &propertyName, + const folly::dynamic &propertyValue); + }; +#endif protected: std::weak_ptr m_wkReactInstance; }; diff --git a/vnext/include/ReactWindowsCore/cdebug.h b/vnext/include/ReactWindowsCore/cdebug.h new file mode 100644 index 00000000000..942db2420d8 --- /dev/null +++ b/vnext/include/ReactWindowsCore/cdebug.h @@ -0,0 +1,37 @@ +#pragma once +#include +#include +#include + +template +struct debugbuffer_t : public std::basic_stringbuf> { + virtual ~debugbuffer_t() { + sync(); + } + + virtual int sync() override { + _OutputDebugString(this->str().c_str()); + this->str(std::basic_string()); + return 0; + } + + private: + void _OutputDebugString(PCSTR str) { + ::OutputDebugStringA(str); + } + void _OutputDebugString(PCWSTR str) { + ::OutputDebugStringW(str); + } +}; + +template +class basic_dostream : public std::basic_ostream { + public: + basic_dostream() : std::basic_ostream(new debugbuffer_t()) {} + ~basic_dostream() { + delete this->rdbuf(); + } +}; + +extern basic_dostream cdebug; +extern basic_dostream cwdebug;