From 3c1114eea72d4d1766443cc9ff4505d3eefdfa75 Mon Sep 17 00:00:00 2001 From: Joshua Gross Date: Tue, 26 Feb 2019 14:33:19 -0800 Subject: [PATCH] Enable Slider component Summary: Enable Slider component in Fabric on Android. {F151706188} Reviewed By: mdvacca Differential Revision: D14220147 fbshipit-source-id: 10b29112e950c8de98cba995839780c4f4e8d3b6 --- .../react/fabric/FabricUIManager.java | 1 + .../views/slider/ReactSliderManager.java | 21 ++++++- .../views/text/ReactTextViewManager.java | 1 + .../image/ImageComponentDescriptor.h | 10 ++- ReactCommon/fabric/components/slider/BUCK | 37 ++++++++++- .../slider/SliderComponentDescriptor.h | 21 ++++++- .../components/slider/SliderShadowNode.cpp | 14 +++++ .../components/slider/SliderShadowNode.h | 7 +++ .../android/SliderMeasurementsManager.cpp | 61 +++++++++++++++++++ .../android/SliderMeasurementsManager.h | 33 ++++++++++ .../ios/SliderMeasurementsManager.cpp | 24 ++++++++ .../platform/ios/SliderMeasurementsManager.h | 29 +++++++++ 12 files changed, 255 insertions(+), 4 deletions(-) create mode 100644 ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.cpp create mode 100644 ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.h create mode 100644 ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.cpp create mode 100644 ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.h diff --git a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java index c645b0cbe54..58cdcd7d970 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/fabric/FabricUIManager.java @@ -77,6 +77,7 @@ public class FabricUIManager implements UIManager, LifecycleEventListener { sComponentNames.put("View", "RCTView"); sComponentNames.put("Image", "RCTImageView"); sComponentNames.put("ScrollView", "RCTScrollView"); + sComponentNames.put("Slider", "RCTSlider"); sComponentNames.put("ReactPerformanceLoggerFlag", "ReactPerformanceLoggerFlag"); sComponentNames.put("Paragraph", "RCTText"); sComponentNames.put("Text", "RCText"); diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java index 84066b5d1bb..c287a04f0c8 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java @@ -14,6 +14,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; import com.facebook.react.bridge.ReactContext; +import com.facebook.react.bridge.ReadableMap; import com.facebook.react.common.MapBuilder; import com.facebook.react.uimanager.LayoutShadowNode; import com.facebook.react.uimanager.ReactShadowNodeImpl; @@ -192,4 +193,22 @@ public class ReactSliderManager extends SimpleViewManager { ReactSlidingCompleteEvent.EVENT_NAME, MapBuilder.of("registrationName", "onSlidingComplete")); } -} + + @Override + public long measure( + ReactContext context, + ReadableMap localData, + ReadableMap props, + float width, + YogaMeasureMode widthMode, + float height, + YogaMeasureMode heightMode) { + SeekBar reactSlider = new ReactSlider(context, null, STYLE); + final int spec = View.MeasureSpec.makeMeasureSpec( + ViewGroup.LayoutParams.WRAP_CONTENT, + View.MeasureSpec.UNSPECIFIED); + reactSlider.measure(spec, spec); + + return YogaMeasureOutput.make(reactSlider.getMeasuredWidth(), reactSlider.getMeasuredHeight()); + } + } diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java index eb0e9f9c0ce..14f324daa26 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/text/ReactTextViewManager.java @@ -96,6 +96,7 @@ public class ReactTextViewManager return MapBuilder.of("topTextLayout", MapBuilder.of("registrationName", "onTextLayout")); } + @Override public long measure( ReactContext context, ReadableMap localData, diff --git a/ReactCommon/fabric/components/image/ImageComponentDescriptor.h b/ReactCommon/fabric/components/image/ImageComponentDescriptor.h index f7168dcde42..b11956877e9 100644 --- a/ReactCommon/fabric/components/image/ImageComponentDescriptor.h +++ b/ReactCommon/fabric/components/image/ImageComponentDescriptor.h @@ -25,11 +25,19 @@ class ImageComponentDescriptor final SharedEventDispatcher eventDispatcher, const SharedContextContainer &contextContainer) : ConcreteComponentDescriptor(eventDispatcher), + // TODO (39486757): implement image manager on Android, currently Android does + // not have an ImageManager so this will crash +#ifndef ANDROID imageManager_( contextContainer ? contextContainer->getInstance( "ImageManager") - : nullptr) {} + : nullptr) { + } +#else + imageManager_(nullptr) { + } +#endif void adopt(UnsharedShadowNode shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); diff --git a/ReactCommon/fabric/components/slider/BUCK b/ReactCommon/fabric/components/slider/BUCK index e8af90f57c5..c446e00a55f 100644 --- a/ReactCommon/fabric/components/slider/BUCK +++ b/ReactCommon/fabric/components/slider/BUCK @@ -6,6 +6,7 @@ load( "fb_xplat_cxx_test", "get_apple_compiler_flags", "get_apple_inspector_flags", + "react_native_target", "react_native_xplat_target", "rn_xplat_cxx_library", "subdir_glob", @@ -17,7 +18,10 @@ rn_xplat_cxx_library( name = "slider", srcs = glob( ["**/*.cpp"], - exclude = glob(["tests/**/*.cpp"]), + exclude = glob([ + "tests/**/*.cpp", + "platform/**/*.cpp", + ]), ), headers = [], header_namespace = "", @@ -33,8 +37,38 @@ rn_xplat_cxx_library( "-std=c++14", "-Wall", ], + fbandroid_deps = [ + react_native_target("jni/react/jni:jni"), + ], + fbandroid_exported_headers = subdir_glob( + [ + ("", "*.h"), + ("platform/android", "*.h"), + ], + prefix = "react/components/slider", + ), + fbandroid_headers = glob( + ["platform/android/*.h"], + ), + fbandroid_srcs = glob( + ["platform/android/*.cpp"], + ), fbobjc_compiler_flags = APPLE_COMPILER_FLAGS, fbobjc_preprocessor_flags = get_debug_preprocessor_flags() + get_apple_inspector_flags(), + force_static = True, + ios_exported_headers = subdir_glob( + [ + ("", "*.h"), + ("platform/ios", "*.h"), + ], + prefix = "react/components/slider", + ), + ios_headers = glob( + ["platform/ios/*.h"], + ), + ios_srcs = glob( + ["platform/ios/*.cpp"], + ), macosx_tests_override = [], platforms = (ANDROID, APPLE), preprocessor_flags = [ @@ -56,6 +90,7 @@ rn_xplat_cxx_library( react_native_xplat_target("fabric/components/view:view"), react_native_xplat_target("fabric/graphics:graphics"), react_native_xplat_target("fabric/imagemanager:imagemanager"), + react_native_xplat_target("fabric/uimanager:uimanager"), ], ) diff --git a/ReactCommon/fabric/components/slider/SliderComponentDescriptor.h b/ReactCommon/fabric/components/slider/SliderComponentDescriptor.h index c692d14e840..1d1cfe50e2f 100644 --- a/ReactCommon/fabric/components/slider/SliderComponentDescriptor.h +++ b/ReactCommon/fabric/components/slider/SliderComponentDescriptor.h @@ -7,6 +7,7 @@ #pragma once +#include #include #include @@ -23,11 +24,20 @@ class SliderComponentDescriptor final SharedEventDispatcher eventDispatcher, const SharedContextContainer &contextContainer) : ConcreteComponentDescriptor(eventDispatcher), + // TODO (39486757): implement image manager on Android, currently Android does + // not have an ImageManager so this will crash +#ifndef ANDROID imageManager_( contextContainer ? contextContainer->getInstance( "ImageManager") - : nullptr) {} + : nullptr), +#else + imageManager_(nullptr), +#endif + measurementsManager_( + std::make_shared(contextContainer)) { + } void adopt(UnsharedShadowNode shadowNode) const override { ConcreteComponentDescriptor::adopt(shadowNode); @@ -39,10 +49,19 @@ class SliderComponentDescriptor final // `SliderShadowNode` uses `ImageManager` to initiate image loading and // communicate the loading state and results to mounting layer. sliderShadowNode->setImageManager(imageManager_); + + // `SliderShadowNode` uses `SliderMeasurementsManager` to + // provide measurements to Yoga. + sliderShadowNode->setSliderMeasurementsManager(measurementsManager_); + + // All `SliderShadowNode`s must have leaf Yoga nodes with properly + // setup measure function. + sliderShadowNode->enableMeasurement(); } private: const SharedImageManager imageManager_; + const std::shared_ptr measurementsManager_; }; } // namespace react diff --git a/ReactCommon/fabric/components/slider/SliderShadowNode.cpp b/ReactCommon/fabric/components/slider/SliderShadowNode.cpp index 5726bc2fb25..8932ef2a34d 100644 --- a/ReactCommon/fabric/components/slider/SliderShadowNode.cpp +++ b/ReactCommon/fabric/components/slider/SliderShadowNode.cpp @@ -21,6 +21,12 @@ void SliderShadowNode::setImageManager(const SharedImageManager &imageManager) { imageManager_ = imageManager; } +void SliderShadowNode::setSliderMeasurementsManager( + const std::shared_ptr &measurementsManager) { + ensureUnsealed(); + measurementsManager_ = measurementsManager; +} + void SliderShadowNode::updateLocalData() { const auto &newTrackImageSource = getTrackImageSource(); const auto &newMinimumTrackImageSource = getMinimumTrackImageSource(); @@ -86,6 +92,14 @@ ImageSource SliderShadowNode::getThumbImageSource() const { #pragma mark - LayoutableShadowNode +Size SliderShadowNode::measure(LayoutConstraints layoutConstraints) const { + if (measurementsManager_->shouldMeasureSlider()) { + return measurementsManager_->measure(layoutConstraints); + } + + return {}; +} + void SliderShadowNode::layout(LayoutContext layoutContext) { updateLocalData(); ConcreteViewShadowNode::layout(layoutContext); diff --git a/ReactCommon/fabric/components/slider/SliderShadowNode.h b/ReactCommon/fabric/components/slider/SliderShadowNode.h index 27ffdb04302..905e039cbf4 100644 --- a/ReactCommon/fabric/components/slider/SliderShadowNode.h +++ b/ReactCommon/fabric/components/slider/SliderShadowNode.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include #include #include @@ -31,8 +32,13 @@ class SliderShadowNode final : public ConcreteViewShadowNode< // Associates a shared `ImageManager` with the node. void setImageManager(const SharedImageManager &imageManager); + // Associates a shared `SliderMeasurementsManager` with the node. + void setSliderMeasurementsManager( + const std::shared_ptr &measurementsManager); + #pragma mark - LayoutableShadowNode + Size measure(LayoutConstraints layoutConstraints) const override; void layout(LayoutContext layoutContext) override; private: @@ -45,6 +51,7 @@ class SliderShadowNode final : public ConcreteViewShadowNode< ImageSource getThumbImageSource() const; SharedImageManager imageManager_; + std::shared_ptr measurementsManager_; }; } // namespace react diff --git a/ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.cpp b/ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.cpp new file mode 100644 index 00000000000..07782a7745a --- /dev/null +++ b/ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.cpp @@ -0,0 +1,61 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "SliderMeasurementsManager.h" + +#include +#include +#include + +using namespace facebook::jni; + +namespace facebook { +namespace react { + +const bool SliderMeasurementsManager::shouldMeasureSlider() const { + return true; +} + +Size SliderMeasurementsManager::measure( + LayoutConstraints layoutConstraints) const { + const jni::global_ref &fabricUIManager = + contextContainer_->getInstance>( + "FabricUIManager"); + + static auto measure = + jni::findClassStatic("com/facebook/react/fabric/FabricUIManager") + ->getMethod("measure"); + + auto minimumSize = layoutConstraints.minimumSize; + auto maximumSize = layoutConstraints.maximumSize; + int minWidth = (int)minimumSize.width; + int minHeight = (int)minimumSize.height; + int maxWidth = (int)maximumSize.width; + int maxHeight = (int)maximumSize.height; + + local_ref componentName = make_jstring("RCTSlider"); + + return yogaMeassureToSize(measure( + fabricUIManager, + componentName.get(), + nullptr, + nullptr, + minWidth, + maxWidth, + minHeight, + maxHeight)); +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.h b/ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.h new file mode 100644 index 00000000000..55076a093bf --- /dev/null +++ b/ReactCommon/fabric/components/slider/platform/android/SliderMeasurementsManager.h @@ -0,0 +1,33 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +/** + * Class that manages slider measurements across platforms. + * On iOS it is a noop, since the height is passed in from JS on iOS only. + */ +class SliderMeasurementsManager { + public: + SliderMeasurementsManager(const SharedContextContainer &contextContainer) + : contextContainer_(contextContainer) {} + const bool shouldMeasureSlider() const; + Size measure(LayoutConstraints layoutConstraints) const; + + private: + const SharedContextContainer contextContainer_; +}; + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.cpp b/ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.cpp new file mode 100644 index 00000000000..6112e4fc971 --- /dev/null +++ b/ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.cpp @@ -0,0 +1,24 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#include "SliderMeasurementsManager.h" + +namespace facebook { +namespace react { + +const bool SliderMeasurementsManager::shouldMeasureSlider() const { + return false; +} + +Size SliderMeasurementsManager::measure( + LayoutConstraints layoutConstraints) const { + assert(false); // should never reach this point + return {}; +} + +} // namespace react +} // namespace facebook diff --git a/ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.h b/ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.h new file mode 100644 index 00000000000..3f253718d98 --- /dev/null +++ b/ReactCommon/fabric/components/slider/platform/ios/SliderMeasurementsManager.h @@ -0,0 +1,29 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +#pragma once + +#include +#include +#include + +namespace facebook { +namespace react { + +/** + * Class that manages slider measurements across platforms. + * On iOS it is a noop, since the height is passed in from JS on iOS only. + */ +class SliderMeasurementsManager { + public: + SliderMeasurementsManager(const SharedContextContainer &contextContainer) {} + const bool shouldMeasureSlider() const; + Size measure(LayoutConstraints layoutConstraints) const; +}; + +} // namespace react +} // namespace facebook