Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Android): jumping content on fabric #2313

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@ class RNSScreenComponentDescriptor final
public:
using ConcreteComponentDescriptor::ConcreteComponentDescriptor;

static constexpr const char *kScreenDummyLayoutHelperClass =
"com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper";

void adopt(ShadowNode &shadowNode) const override {
react_native_assert(dynamic_cast<RNSScreenShadowNode *>(&shadowNode));
auto &screenShadowNode = static_cast<RNSScreenShadowNode &>(shadowNode);
Expand All @@ -39,7 +36,8 @@ class RNSScreenComponentDescriptor final
#ifdef ANDROID
if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
// When we receive dimensions from JVM side we can remove padding used for
// correction, and we can stop applying height and offset corrections for the frame.
// correction, and we can stop applying height and offset corrections for
// the frame.
// TODO: In future, when we have dynamic header height we might want to
// update Y offset correction here.

Expand Down Expand Up @@ -67,37 +65,10 @@ class RNSScreenComponentDescriptor final
screenShadowNode.getFrameCorrectionModes().unset(
FrameCorrectionModes::Mode::FrameHeightCorrection);
screenShadowNode.getFrameCorrectionModes().unset(
FrameCorrectionModes::Mode::FrameOriginCorrection);
FrameCorrectionModes::Mode::FrameOriginCorrection);

layoutableShadowNode.setSize(
Size{stateData.frameSize.width, stateData.frameSize.height});
} else {
// This code path should be executed only on the very first (few)
// layout(s), when we haven't received state update from JVM side yet.

auto headerConfigChildOpt = findHeaderConfigChild(layoutableShadowNode);

// During creation of the shadow node children are not attached yet.
// We also do not want to set any padding in case.
if (headerConfigChildOpt) {
const auto &headerConfigChild = headerConfigChildOpt->get();
const auto &headerProps =
*std::static_pointer_cast<const RNSScreenStackHeaderConfigProps>(
headerConfigChild->getProps());

const auto headerHeight = headerProps.hidden
? 0.f
: findHeaderHeight(
headerProps.titleFontSize, headerProps.title.empty())
.value_or(0.f);

screenShadowNode.setPadding({0, 0, 0, headerHeight});
screenShadowNode.setHeaderHeight(headerHeight);
screenShadowNode.getFrameCorrectionModes().set(
FrameCorrectionModes::Mode(
FrameCorrectionModes::Mode::FrameHeightCorrection |
FrameCorrectionModes::Mode::FrameOriginCorrection));
}
}
#else
if (stateData.frameSize.width != 0 && stateData.frameSize.height != 0) {
Expand All @@ -107,72 +78,6 @@ class RNSScreenComponentDescriptor final
#endif // ANDROID
ConcreteComponentDescriptor::adopt(shadowNode);
}

std::optional<std::reference_wrapper<const ShadowNode::Shared>>
findHeaderConfigChild(
const YogaLayoutableShadowNode &screenShadowNode) const {
for (const ShadowNode::Shared &child : screenShadowNode.getChildren()) {
if (std::strcmp(
child->getComponentName(), "RNSScreenStackHeaderConfig") == 0) {
return {std::cref(child)};
}
}
return {};
}

#ifdef ANDROID
std::optional<float> findHeaderHeight(
const int fontSize,
const bool isTitleEmpty) const {
JNIEnv *env = facebook::jni::Environment::current();

if (env == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to retrieve env\n";
return {};
}

jclass layoutHelperClass = env->FindClass(kScreenDummyLayoutHelperClass);

if (layoutHelperClass == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to find class with id "
<< kScreenDummyLayoutHelperClass;
return {};
}

jmethodID computeDummyLayoutID =
env->GetMethodID(layoutHelperClass, "computeDummyLayout", "(IZ)F");

if (computeDummyLayoutID == nullptr) {
LOG(ERROR)
<< "[RNScreens] Failed to retrieve computeDummyLayout method ID";
return {};
}

jmethodID getInstanceMethodID = env->GetStaticMethodID(
layoutHelperClass,
"getInstance",
"()Lcom/swmansion/rnscreens/utils/ScreenDummyLayoutHelper;");

if (getInstanceMethodID == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to retrieve getInstanceMethodID";
return {};
}

jobject packageInstance =
env->CallStaticObjectMethod(layoutHelperClass, getInstanceMethodID);

if (packageInstance == nullptr) {
LOG(ERROR)
<< "[RNScreens] Failed to retrieve packageInstance or the package instance was null on JVM side";
return {};
}

jfloat headerHeight = env->CallFloatMethod(
packageInstance, computeDummyLayoutID, fontSize, isTitleEmpty);

return {headerHeight};
}
#endif // ANDROID
};

} // namespace react
Expand Down
101 changes: 101 additions & 0 deletions common/cpp/react/renderer/components/rnscreens/RNSScreenShadowNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,107 @@ Point RNSScreenShadowNode::getContentOriginOffset(
return {contentOffset.x, contentOffset.y};
}

std::optional<std::reference_wrapper<const ShadowNode::Shared>>
findHeaderConfigChild(const YogaLayoutableShadowNode &screenShadowNode) {
for (const ShadowNode::Shared &child : screenShadowNode.getChildren()) {
if (std::strcmp(child->getComponentName(), "RNSScreenStackHeaderConfig") ==
0) {
return {std::cref(child)};
}
}
return {};
}

static constexpr const char *kScreenDummyLayoutHelperClass =
"com/swmansion/rnscreens/utils/ScreenDummyLayoutHelper";

#ifdef ANDROID
std::optional<float> findHeaderHeight(
const int fontSize,
const bool isTitleEmpty) {
JNIEnv *env = facebook::jni::Environment::current();

if (env == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to retrieve env\n";
return {};
}

jclass layoutHelperClass = env->FindClass(kScreenDummyLayoutHelperClass);

if (layoutHelperClass == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to find class with id "
<< kScreenDummyLayoutHelperClass;
return {};
}

jmethodID computeDummyLayoutID =
env->GetMethodID(layoutHelperClass, "computeDummyLayout", "(IZ)F");

if (computeDummyLayoutID == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to retrieve computeDummyLayout method ID";
return {};
}

jmethodID getInstanceMethodID = env->GetStaticMethodID(
layoutHelperClass,
"getInstance",
"()Lcom/swmansion/rnscreens/utils/ScreenDummyLayoutHelper;");

if (getInstanceMethodID == nullptr) {
LOG(ERROR) << "[RNScreens] Failed to retrieve getInstanceMethodID";
return {};
}

jobject packageInstance =
env->CallStaticObjectMethod(layoutHelperClass, getInstanceMethodID);

if (packageInstance == nullptr) {
LOG(ERROR)
<< "[RNScreens] Failed to retrieve packageInstance or the package instance was null on JVM side";
return {};
}

jfloat headerHeight = env->CallFloatMethod(
packageInstance, computeDummyLayoutID, fontSize, isTitleEmpty);

return {headerHeight};
}
#endif // ANDROID

void RNSScreenShadowNode::appendChild(const ShadowNode::Shared &child) {
YogaLayoutableShadowNode::appendChild(child);
#ifdef ANDROID
const auto &stateData = getStateData();
if (stateData.frameSize.width == 0 || stateData.frameSize.height == 0) {
// This code path should be executed only on the very first (few)
// layout(s), when we haven't received state update from JVM side yet.
auto headerConfigChildOpt = findHeaderConfigChild(*this);
auto &screenShadowNode = static_cast<RNSScreenShadowNode &>(*this);

// During creation of the shadow node children are not attached yet.
// We also do not want to set any padding in case.
if (headerConfigChildOpt) {
const auto &headerConfigChild = headerConfigChildOpt->get();
const auto &headerProps =
*std::static_pointer_cast<const RNSScreenStackHeaderConfigProps>(
headerConfigChild->getProps());

const auto headerHeight = headerProps.hidden
? 0.f
: findHeaderHeight(
headerProps.titleFontSize, headerProps.title.empty())
.value_or(0.f);

screenShadowNode.setPadding({0, 0, 0, headerHeight});
screenShadowNode.setHeaderHeight(headerHeight);
screenShadowNode.getFrameCorrectionModes().set(FrameCorrectionModes::Mode(
FrameCorrectionModes::Mode::FrameHeightCorrection |
FrameCorrectionModes::Mode::FrameOriginCorrection));
}
}
#endif // ANDROID
}

void RNSScreenShadowNode::layout(facebook::react::LayoutContext layoutContext) {
YogaLayoutableShadowNode::layout(layoutContext);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class JSI_EXPORT RNSScreenShadowNode final : public ConcreteViewShadowNode<

Point getContentOriginOffset(bool includeTransform) const override;

void appendChild(const ShadowNode::Shared &child) override;

void layout(LayoutContext layoutContext) override;

#pragma mark - Custom interface
Expand Down