diff --git a/include/eepp/ui/uiscenenode.hpp b/include/eepp/ui/uiscenenode.hpp index 65229c3dd..91ac563fe 100644 --- a/include/eepp/ui/uiscenenode.hpp +++ b/include/eepp/ui/uiscenenode.hpp @@ -133,6 +133,19 @@ class EE_API UISceneNode : public SceneNode { /** @return The explicit viewport size, or the scene extent when no override is set. */ const Sizef& getViewportPixelsSize() const; + /** + * @brief Sets the node whose world bounds delimit the visible portion of this scene. + * + * Embedded scenes can have an extent larger than their host viewport. Binding the visible + * bounds to the host's clipping node lets render culling use that viewport while preserving the + * full scene extent for layout and scrolling. The node must outlive this scene or clear the + * binding before it is destroyed. + */ + void setVisibleBoundsNode( Node* node ); + + /** @return The visible world bounds used for render culling. */ + const Rectf& getVisibleWorldBounds(); + /** * @brief Sets the layout viewport size used by the scene root as the initial containing block. * @@ -975,6 +988,7 @@ class EE_API UISceneNode : public SceneNode { Uint32 mCurrentMarker{ 0 }; Sizef mViewportPixelsSize; bool mHasViewportPixelsSize{ false }; + Node* mVisibleBoundsNode{ nullptr }; Sizef mLayoutViewportPixelsSize; bool mHasLayoutViewportPixelsSize{ false }; bool mFollowParentSize{ true }; diff --git a/src/eepp/ui/uinode.cpp b/src/eepp/ui/uinode.cpp index 8fe08d863..2018f1067 100644 --- a/src/eepp/ui/uinode.cpp +++ b/src/eepp/ui/uinode.cpp @@ -1113,7 +1113,7 @@ void UINode::nodeDraw() { smartClipStart( ClipType::BorderBox, needsClipPlanes ); - bool intersected = mWorldBounds.intersect( mSceneNode->getWorldBounds() ); + bool intersected = mWorldBounds.intersect( mUISceneNode->getVisibleWorldBounds() ); if ( intersected ) { smartClipStart( ClipType::ContentBox, needsClipPlanes ); diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 3eede34de..84709475f 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -1075,6 +1075,14 @@ const Sizef& UISceneNode::getViewportPixelsSize() const { return mHasViewportPixelsSize ? mViewportPixelsSize : getPixelsSize(); } +void UISceneNode::setVisibleBoundsNode( Node* node ) { + mVisibleBoundsNode = node; +} + +const Rectf& UISceneNode::getVisibleWorldBounds() { + return mVisibleBoundsNode ? mVisibleBoundsNode->getWorldBounds() : getWorldBounds(); +} + void UISceneNode::setLayoutViewportPixelsSize( const Sizef& size ) { if ( mHasLayoutViewportPixelsSize && mLayoutViewportPixelsSize == size ) return; diff --git a/src/eepp/ui/uiwebview.cpp b/src/eepp/ui/uiwebview.cpp index c61b7eaac..bfcd61cf2 100644 --- a/src/eepp/ui/uiwebview.cpp +++ b/src/eepp/ui/uiwebview.cpp @@ -168,6 +168,7 @@ UIWebView::UIWebView() : UIScrollView( "webview" ) { mDocumentScene = UISceneNode::New(); mDocumentScene->setFollowParentSize( false ); + mDocumentScene->setVisibleBoundsNode( mContainer ); mDocumentScene->setParent( mDocumentLayout ); mDocContainer = UIWebViewDocumentContainer::New(); diff --git a/src/tests/unit_tests/uiwebview_tests.cpp b/src/tests/unit_tests/uiwebview_tests.cpp index eee5517e2..9da438a15 100644 --- a/src/tests/unit_tests/uiwebview_tests.cpp +++ b/src/tests/unit_tests/uiwebview_tests.cpp @@ -43,6 +43,15 @@ using namespace EE::Window; using namespace EE::Scene; using namespace EE::UI; +class CountingDrawWidget : public UIWidget { + public: + static CountingDrawWidget* New() { return eeNew( CountingDrawWidget, () ); } + + void draw() override { drawCount++; } + + int drawCount{ 0 }; +}; + static bool readHttpRequestHeaders( TcpSocket& client, std::string* headers = nullptr ) { std::string request; char buffer[1024]; @@ -144,6 +153,20 @@ UTEST( UIWebView, OwnedDocumentSceneScrollTarget ) { EXPECT_NEAR( documentScene->getViewportPixelsSize().getHeight(), 300.f, 1.f ); EXPECT_GT( documentScene->getPixelsSize().getHeight(), 1000.f ); EXPECT_TRUE( webView->getVerticalScrollBar()->isVisible() ); + EXPECT_TRUE( documentScene->getVisibleWorldBounds() == + webView->getContainer()->getWorldBounds() ); + EXPECT_LT( documentScene->getVisibleWorldBounds().getHeight(), + documentScene->getWorldBounds().getHeight() ); + + auto* drawProbe = CountingDrawWidget::New(); + drawProbe->setPixelsSize( 20.f, 20.f ); + drawProbe->setPixelsPosition( 10.f, 700.f ); + drawProbe->setParent( documentScene->getRoot() ); + drawProbe->nodeDraw(); + EXPECT_EQ( drawProbe->drawCount, 0 ); + drawProbe->setPixelsPosition( 10.f, 10.f ); + drawProbe->nodeDraw(); + EXPECT_EQ( drawProbe->drawCount, 1 ); ASSERT_TRUE( documentScene->getParent() != nullptr && documentScene->getParent()->isUINode() ); UINode* scrollTarget = documentScene->getParent()->asType();