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

Element eventemitter and container layout adjustments #17

Merged
merged 7 commits into from
Jan 14, 2025
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
6 changes: 4 additions & 2 deletions android/shadowlist/jni/OnLoad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@

#include "ShadowlistModule.h"
#include "SLRuntimeManager.h"
#include "SLModuleJSI.h"

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
return facebook::jni::initialize(vm, [] {
facebook::react::ShadowlistModule::registerNatives();
facebook::react::ShadowlistModule::registerNatives();
});
}

extern "C" JNIEXPORT void JNICALL Java_com_shadowlist_ShadowlistModule_injectJSIBindings(JNIEnv *env, jobject thiz, jlong jsiRuntime) {
extern "C" JNIEXPORT void JNICALL Java_com_shadowlist_ShadowlistModule_injectJSIBindings(JNIEnv *env, jobject thiz, jobject jsiRuntime) {
jsi::Runtime* runtime = reinterpret_cast<jsi::Runtime*>(jsiRuntime);
SLRuntimeManager::getInstance().setRuntime(runtime);
SLModuleJSI::install(*runtime);
}
12 changes: 8 additions & 4 deletions android/src/main/java/com/shadowlist/SLContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@
import com.facebook.react.common.mapbuffer.MapBuffer;
import com.facebook.react.uimanager.PixelUtil;
import com.facebook.react.uimanager.StateWrapper;
import com.facebook.react.views.scroll.ReactHorizontalScrollView;
import com.facebook.react.views.scroll.ReactScrollView;
import com.facebook.react.views.view.ReactViewGroup;

public class SLContainer extends ReactViewGroup {
private boolean mOrientation;
private HorizontalScrollView mScrollContainerHorizontal;
private ScrollView mScrollContainerVertical;
private ReactHorizontalScrollView mScrollContainerHorizontal;
private ReactScrollView mScrollContainerVertical;
private ReactViewGroup mScrollContent;
private SLContainerManager.OnStartReachedHandler mOnStartReachedHandler;
private SLContainerManager.OnEndReachedHandler mOnEndReachedHandler;
Expand All @@ -37,8 +39,8 @@ public SLContainer(Context context, AttributeSet attrs) {
}

private void init(Context context) {
mScrollContainerHorizontal = new HorizontalScrollView(context);
mScrollContainerVertical = new ScrollView(context);
mScrollContainerHorizontal = new ReactHorizontalScrollView(context);
mScrollContainerVertical = new ReactScrollView(context);

mScrollContent = new ReactViewGroup(context);

Expand All @@ -48,12 +50,14 @@ private void init(Context context) {
stateMapBuffer.putDouble("scrollPositionTop", PixelUtil.toDIPFromPixel(scrollY));
mStateWrapper.updateState(stateMapBuffer);
};

OnScrollChangeListener scrollListenerHorizontal = (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
WritableMap stateMapBuffer = new WritableNativeMap();
stateMapBuffer.putDouble("scrollPositionLeft", PixelUtil.toDIPFromPixel(oldScrollX));
stateMapBuffer.putDouble("scrollPositionTop", PixelUtil.toDIPFromPixel(scrollY));
mStateWrapper.updateState(stateMapBuffer);
};

mScrollContainerVertical.setOnScrollChangeListener(scrollListenerVertical);
mScrollContainerVertical.setVerticalScrollBarEnabled(true);
mScrollContainerHorizontal.setOnScrollChangeListener(scrollListenerHorizontal);
Expand Down
30 changes: 16 additions & 14 deletions cpp/react/renderer/components/RNShadowlistSpec/SLModuleJSI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@ namespace facebook::react {

using namespace facebook;

void SLModuleJSI::install(facebook::jsi::Runtime &runtime) {
auto getRegistryElementMapping = facebook::jsi::Function::createFromHostFunction(
runtime,
facebook::jsi::PropNameID::forAscii(runtime, "__NATIVE_getRegistryElementMapping"),
1,
[&](facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisValue,
const facebook::jsi::Value *arguments,
size_t count) -> facebook::jsi::Value
{
int elementFamilyTag = SLRuntimeManager::getInstance().getTag(arguments[0].asNumber());
return facebook::jsi::Value(elementFamilyTag);
});
runtime.global().setProperty(runtime, "__NATIVE_getRegistryElementMapping", std::move(getRegistryElementMapping));
}

void SLModuleJSI::install(facebook::jsi::Runtime &runtime, std::shared_ptr<SLCommitHook> &commitHook) {
auto registerContainerFamily = facebook::jsi::Function::createFromHostFunction(
runtime,
Expand Down Expand Up @@ -69,20 +85,6 @@ void SLModuleJSI::install(facebook::jsi::Runtime &runtime, std::shared_ptr<SLCom
return facebook::jsi::Value::undefined();
});
runtime.global().setProperty(runtime, "__NATIVE_unregisterElementNode", std::move(unregisterElementFamily));

auto getRegistryElementMapping = facebook::jsi::Function::createFromHostFunction(
runtime,
facebook::jsi::PropNameID::forAscii(runtime, "__NATIVE_getRegistryElementMapping"),
1,
[&](facebook::jsi::Runtime &runtime,
const facebook::jsi::Value &thisValue,
const facebook::jsi::Value *arguments,
size_t count) -> facebook::jsi::Value
{
int elementFamilyTag = SLRuntimeManager::getInstance().getTag(arguments[0].asNumber());
return facebook::jsi::Value(elementFamilyTag);
});
runtime.global().setProperty(runtime, "__NATIVE_getRegistryElementMapping", std::move(getRegistryElementMapping));
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ namespace facebook::react {

class SLModuleJSI {
public:
static void install(facebook::jsi::Runtime &runtime);
static void install(facebook::jsi::Runtime &runtime, std::shared_ptr<SLCommitHook> &commitHook);
};

Expand Down
29 changes: 17 additions & 12 deletions cpp/react/renderer/components/RNShadowlistSpec/SLTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,16 @@

namespace facebook::react {

int nextFamilyTag = -2;
/*
* Following offset of elements should probably be enough for now
* However this should be revisited and optimized with different strategy later
*/
int nextFamilyTag = 524288;

auto adjustFamilyTag = [](int tag) {
const int MIN_TAG_VALUE = -2e9;
const int CLAMPED_TAG = -2;
return tag < MIN_TAG_VALUE ? CLAMPED_TAG : tag - 2;
const int MAX_TAG_VALUE = std::numeric_limits<int>::max();
const int CLAMPED_TAG = 2;
return tag > MAX_TAG_VALUE ? CLAMPED_TAG : tag + 2;
};

static void updateRawTextProps(const SLContainerProps::SLContainerDataItem &elementData, const std::shared_ptr<ShadowNode> &nextShadowNode, const ShadowNode::Shared &shadowNode) {
Expand Down Expand Up @@ -47,22 +51,23 @@ ShadowNode::Unshared SLTemplate::cloneShadowNodeTree(const int& elementDataIndex
*SLRuntimeManager::getInstance().getRuntime(),
shadowNode->getInstanceHandle(*SLRuntimeManager::getInstance().getRuntime()),
nextFamilyTag);
auto const fragment = ShadowNodeFamilyFragment{nextFamilyTag, shadowNode->getSurfaceId(), instanceHandle};
auto const family = componentDescriptor.createFamily(fragment);

auto const family = componentDescriptor.createFamily({nextFamilyTag, shadowNode->getSurfaceId(), instanceHandle});

#ifdef ANDROID
auto const props = componentDescriptor.cloneProps(propsParserContext, shadowNode->getProps(), RawProps(shadowNode->getProps()->rawProps));
auto const nextProps = componentDescriptor.cloneProps(propsParserContext, shadowNode->getProps(), RawProps(shadowNode->getProps()->rawProps));
#else
auto const props = componentDescriptor.cloneProps(propsParserContext, shadowNode->getProps(), {});
auto const nextProps = componentDescriptor.cloneProps(propsParserContext, shadowNode->getProps(), {});
#endif

auto const state = componentDescriptor.createInitialState(props, family);
auto const nextShadowNode = componentDescriptor.createShadowNode(
ShadowNodeFragment{props, ShadowNodeFragment::childrenPlaceholder(), state}, family);
auto const nextState = componentDescriptor.createInitialState(nextProps, family);
auto const nextShadowNode = componentDescriptor.createShadowNode({nextProps, ShadowNodeFragment::childrenPlaceholder(), nextState}, family);

updateRawTextProps(elementData, nextShadowNode, shadowNode);
updateImageProps(elementData, nextShadowNode, shadowNode);

nextShadowNode->setMounted(true);

for (const auto &childShadowNode : shadowNode->getChildren()) {
auto const clonedChildShadowNode = cloneShadowNodeTree(elementDataIndex, elementData, childShadowNode);
componentDescriptor.appendChild(nextShadowNode, clonedChildShadowNode);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {

auto &templateRegistry = elementShadowNodeTemplateRegistry[getTag()];
auto &componentRegistry = elementShadowNodeComponentRegistry[getTag()];

auto &props = getConcreteProps();
auto nextStateData = getStateData();

Expand All @@ -34,6 +34,7 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
const int viewportOffset = 1000;

nextStateData.scrollPositionUpdated = false;
nextStateData.scrollContainer = getLayoutMetrics().frame.size;

if (!nextStateData.childrenMeasurementsTree.size() && props.initialScrollIndex) {
nextStateData.scrollIndex = props.initialScrollIndex;
Expand Down Expand Up @@ -63,7 +64,7 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
}

/*
* If data is prepended, extend the measurements tree from the beginning
* If data is prepended, extend the measurements tree from the beginning
* to match the new data size. If data is appended, resize the tree at the end
*/
if (elementsDataPrepended) {
Expand All @@ -80,7 +81,7 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
} else {
nextStateData.childrenMeasurementsTree.resize(elementsDataSize);
}

/*
* Static templates measurements, incl. Header, Empty, Footer
*/
Expand Down Expand Up @@ -153,8 +154,6 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
containerShadowNodeChildren->push_back(componentRegistry["ListHeaderComponentUniqueId"]);
}



/*
* Calculate sequence of indices above and below the current scroll index
*/
Expand Down Expand Up @@ -185,18 +184,18 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
for (ComponentRegistryItem componentRegistryItem : scrollContentAboveComponents) {
auto elementMetrics = adjustElement(
{
.x = (componentRegistryItem.index % props.numColumns) * (getLayoutMetrics().frame.size.width / props.numColumns),
.x = (componentRegistryItem.index % props.numColumns) * (nextStateData.scrollContainer.width / props.numColumns),
.y = scrollContentAboveOffset.get(componentRegistryItem.index % props.numColumns) + scrollContentBelowOffset.get(componentRegistryItem.index % props.numColumns)
},
componentRegistry[componentRegistryItem.elementDataUniqueKey]);

scrollContentAboveOffset.add(componentRegistryItem.index % props.numColumns, componentRegistryItem.size);
scrollContentAboveIndex = componentRegistryItem.index;

int scrollPosition = nextStateData.scrollPositionUpdated ? (
scrollContentAboveOffset.get(componentRegistryItem.index % props.numColumns) + getRelativePointFromPoint(nextStateData.scrollPosition) - nextStateData.templateMeasurementsTree[0]
) : getRelativePointFromPoint(nextStateData.scrollPosition);

if (getRelativePointFromPoint(elementMetrics.frame.origin) <= (scrollPosition + getRelativeSizeFromSize(nextStateData.scrollContainer) + viewportOffset) &&
(getRelativePointFromPoint(elementMetrics.frame.origin) + getRelativeSizeFromSize(elementMetrics.frame.size)) >= (scrollPosition - viewportOffset)) {
containerShadowNodeChildren->push_back(componentRegistry[componentRegistryItem.elementDataUniqueKey]);
Expand All @@ -211,18 +210,18 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
auto elementShadowNodeLayoutable = std::static_pointer_cast<YogaLayoutableShadowNode>(componentRegistry[componentRegistryItem.elementDataUniqueKey]);
auto elementMetrics = adjustElement(
{
.x = (componentRegistryItem.index % props.numColumns) * (getLayoutMetrics().frame.size.width / props.numColumns),
.x = (componentRegistryItem.index % props.numColumns) * (nextStateData.scrollContainer.width / props.numColumns),
.y = scrollContentAboveOffset.get(componentRegistryItem.index % props.numColumns) + scrollContentBelowOffset.get(componentRegistryItem.index % props.numColumns)
},
componentRegistry[componentRegistryItem.elementDataUniqueKey]);

scrollContentBelowOffset.add(componentRegistryItem.index % props.numColumns, componentRegistryItem.size);
scrollContentBelowIndex = componentRegistryItem.index;

int scrollPosition = nextStateData.scrollPositionUpdated ? (
scrollContentAboveOffset.get(componentRegistryItem.index % props.numColumns) + getRelativePointFromPoint(nextStateData.scrollPosition) - nextStateData.templateMeasurementsTree[0]
) : getRelativePointFromPoint(nextStateData.scrollPosition);

if (getRelativePointFromPoint(elementMetrics.frame.origin) <= (scrollPosition + getRelativeSizeFromSize(nextStateData.scrollContainer) + viewportOffset) &&
(getRelativePointFromPoint(elementMetrics.frame.origin) + getRelativeSizeFromSize(elementMetrics.frame.size)) >= (scrollPosition - viewportOffset)) {
containerShadowNodeChildren->push_back(componentRegistry[componentRegistryItem.elementDataUniqueKey]);
Expand All @@ -232,14 +231,14 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
if (!props.uniqueIds.size()) {
auto templateRegistryItem = transformTemplateComponent("ListEmptyComponentUniqueId", 1);
containerShadowNodeChildren->push_back(componentRegistry["ListEmptyComponentUniqueId"]);

adjustElement(
{
.x = 0,
.y = scrollContentAboveOffset.max() + scrollContentBelowOffset.max()
},
componentRegistry["ListEmptyComponentUniqueId"]);

scrollContentBelowOffset.add(1, templateRegistryItem.size);
}

Expand All @@ -249,7 +248,7 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
if (props.inverted) {
transformTemplateComponent("ListHeaderComponentUniqueId", 1);
containerShadowNodeChildren->push_back(componentRegistry["ListHeaderComponentUniqueId"]);

adjustElement(
{
.x = 0,
Expand All @@ -259,7 +258,7 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
} else {
transformTemplateComponent("ListFooterComponentUniqueId", 1);
containerShadowNodeChildren->push_back(componentRegistry["ListFooterComponentUniqueId"]);

adjustElement(
{
.x = 0,
Expand All @@ -283,18 +282,17 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {

nextStateData.firstChildUniqueId = props.uniqueIds.front();
nextStateData.lastChildUniqueId = props.uniqueIds.back();
nextStateData.scrollContainer = getLayoutMetrics().frame.size;


if (props.horizontal) {
nextStateData.scrollContent.height = getLayoutMetrics().frame.size.height;
nextStateData.scrollContent.height = nextStateData.scrollContainer.height;
nextStateData.scrollContent.width = (
scrollContentAboveOffset.max() +
scrollContentBelowOffset.max() +
nextStateData.templateMeasurementsTree[1]
);
nextStateData.scrollContentUpdated = true;
} else if (!props.horizontal) {
nextStateData.scrollContent.width = getLayoutMetrics().frame.size.width;
nextStateData.scrollContent.width = nextStateData.scrollContainer.width;
nextStateData.scrollContent.height = (
scrollContentAboveOffset.max() +
scrollContentBelowOffset.max() +
Expand Down Expand Up @@ -332,14 +330,14 @@ void SLContainerShadowNode::layout(LayoutContext layoutContext) {
.visibleStartIndex = scrollContentBelowIndex,
.visibleEndIndex = scrollContentAboveIndex,
});

getConcreteEventEmitter().onScroll({
.contentSize = nextStateData.scrollContent,
.contentOffset = nextStateData.scrollPosition,
});

setStateData(std::move(nextStateData));

#ifndef RCT_DEBUG
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
Expand Down Expand Up @@ -378,7 +376,7 @@ LayoutMetrics SLContainerShadowNode::layoutElement(LayoutContext layoutContext,
}

elementShadowNodeLayoutable->layoutTree(layoutContext, layoutConstraints);

return elementShadowNodeLayoutable->getLayoutMetrics();
}

Expand All @@ -394,7 +392,7 @@ LayoutMetrics SLContainerShadowNode::adjustElement(Point origin, ShadowNode::Uns
layoutMetrics.frame.origin.x = origin.x;
}
elementShadowNodeLayoutable->setLayoutMetrics(layoutMetrics);

return layoutMetrics;
}

Expand Down
4 changes: 4 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ export default function App() {
horizontal={IS_HORIZONTAL}
initialScrollIndex={INITIAL_SCROLL_INDEX}
numColumns={1}
contentContainerStyle={styles.list}
/>
</View>
);
Expand All @@ -111,6 +112,9 @@ const styles = StyleSheet.create({
backgroundColor: '#333333',
paddingTop: 60,
},
list: {
flex: 1,
},
text: {
color: '#333333',
backgroundColor: '#1dd1a1',
Expand Down
1 change: 1 addition & 0 deletions ios/Shadowlist.mm
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ - (void)installJSIBindingsWithRuntime:(facebook::jsi::Runtime &)runtime
{
self->runtime_ = &runtime;
SLRuntimeManager::getInstance().setRuntime(self->runtime_);
SLModuleJSI::install(runtime);
SLModuleJSI::install(runtime, commitHook_);
}

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "shadowlist",
"version": "0.4.23",
"version": "0.4.24",
"description": "60fps, high-performance list for React Native.",
"source": "./src/index.tsx",
"main": "./lib/commonjs/index.js",
Expand Down
4 changes: 2 additions & 2 deletions src/SLContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ const SLContainerWrapper = (

const styles = StyleSheet.create({
containerVertical: {
flex: 1,
height: '100%',
flexDirection: 'column',
},
containerHorizontal: {
flex: 1,
width: '100%',
flexDirection: 'row',
},
});
Expand Down
Loading