From 323d511eed0736763ffb4843cd0afbb52deee3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 1 Aug 2026 01:16:13 -0300 Subject: [PATCH] =?UTF-8?q?Fixed=20a=20regression=20in=20UIWebView=20that?= =?UTF-8?q?=20caused=20to=20basically=20don't=20have=20render=20culling:?= =?UTF-8?q?=20=20=20-=20Added=20UISceneNode::getVisibleWorldBounds()=20wit?= =?UTF-8?q?h=20an=20optional=20bounds-node=20binding.=20=20=20-=20UIWebVie?= =?UTF-8?q?w=20binds=20its=20document=20scene=20to=20the=20scroll=20contai?= =?UTF-8?q?ner=E2=80=99s=20clipped=20viewport.=20=20=20-=20UINode::nodeDra?= =?UTF-8?q?w()=20now=20culls=20against=20visible=20scene=20bounds=20instea?= =?UTF-8?q?d=20of=20the=20full=20document=20extent.=20=20=20-=20Existing?= =?UTF-8?q?=20top-level=20scenes=20retain=20their=20previous=20behavior.?= =?UTF-8?q?=20=20=20-=20Added=20a=20regression=20test=20confirming=20offsc?= =?UTF-8?q?reen=20document=20widgets=20are=20skipped=20and=20visible=20wid?= =?UTF-8?q?gets=20render.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/eepp/ui/uiscenenode.hpp | 14 ++++++++++++++ src/eepp/ui/uinode.cpp | 2 +- src/eepp/ui/uiscenenode.cpp | 8 ++++++++ src/eepp/ui/uiwebview.cpp | 1 + src/tests/unit_tests/uiwebview_tests.cpp | 23 +++++++++++++++++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) 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();