sw/inc/viewsh.hxx                                |    2 
 sw/qa/extras/tiledrendering2/tiledrendering2.cxx |   52 +++++++++++++++++++++++
 sw/source/core/layout/layact.cxx                 |   13 +++++
 3 files changed, 65 insertions(+), 2 deletions(-)

New commits:
commit 41aec3e9a088ad4e99e43e033c7653e2c25a85ba
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Thu Aug 15 10:20:25 2024 +0200
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Thu Aug 15 14:55:51 2024 +0200

    Related: cool#9735 sw lok: handle the AnyInput() callback during idle layout
    
    Open the 300 pages bugdoc, paste a oneliner plain text content in a
    paragraph which is part of a numbered list, observe a 274 ms hang till
    layout is done for all pages, then we get an updated tile.
    
    We already do the layout in two passes: once for the visible area and
    then an idle pass for the rest of the pages. But we didn't notice that
    the LOK client has pending input events, so the list of events were like
    this:
    debug:20492:20486: SwTransferable::PasteData: finished in 5 ms
    debug:20492:20486: SwLayAction::InternalAction: finished in 273 ms
    debug:20492:20486: SwViewShell::PaintTile
    
    With this patch, the order of evens is rather like:
    debug:7541:7535: SwTransferable::PasteData: finished in 4 ms
    debug:7541:7535: SwViewShell::PaintTile
    debug:7541:7535: SwLayAction::InternalAction: finished in 261 ms
    
    Which means that once a LOK client opts in to provide an any input
    callback, the end-to-end time from the paste uno command dispatch to
    receiving the first tile update decreases from 963 ms to 14 ms.
    
    Change-Id: Ia9e734f84121b7d87150cb3479fc265ca8ee0292
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/171885
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>
    Tested-by: Jenkins

diff --git a/sw/inc/viewsh.hxx b/sw/inc/viewsh.hxx
index 01a3cc259929..9c0bd4014a16 100644
--- a/sw/inc/viewsh.hxx
+++ b/sw/inc/viewsh.hxx
@@ -458,7 +458,7 @@ public:
     // DOCUMENT COMPATIBILITY FLAGS END
 
     // Calls Idle-formatter of Layout.
-    void LayoutIdle();
+    SW_DLLPUBLIC void LayoutIdle();
 
     const SwViewOption *GetViewOptions() const { return mpOpt.get(); }
     virtual void  ApplyViewOptions( const SwViewOption &rOpt );
diff --git a/sw/qa/extras/tiledrendering2/tiledrendering2.cxx 
b/sw/qa/extras/tiledrendering2/tiledrendering2.cxx
index 0aba4ef6ab38..ecb9cea039aa 100644
--- a/sw/qa/extras/tiledrendering2/tiledrendering2.cxx
+++ b/sw/qa/extras/tiledrendering2/tiledrendering2.cxx
@@ -84,6 +84,17 @@ SwXTextDocument* SwTiledRenderingTest::createDoc(const char* 
pName)
     return pTextDocument;
 }
 
+/// Test callback that works with 
comphelper::LibreOfficeKit::setAnyInputCallback().
+class AnyInputCallback final
+{
+public:
+    static bool callback(void* /*pData*/) { return true; }
+
+    AnyInputCallback() { 
comphelper::LibreOfficeKit::setAnyInputCallback(&callback, this); }
+
+    ~AnyInputCallback() { 
comphelper::LibreOfficeKit::setAnyInputCallback(nullptr, nullptr); }
+};
+
 /// A view callback tracks callbacks invoked on one specific view.
 class ViewCallback final
 {
@@ -304,6 +315,47 @@ CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAsyncLayout)
     CPPUNIT_ASSERT(!pPage2->IsInvalidContent());
     CPPUNIT_ASSERT(!pPage3->IsInvalidContent());
 }
+
+CPPUNIT_TEST_FIXTURE(SwTiledRenderingTest, testAnyInput)
+{
+    // Given a document with 3 pages, the first page is visible:
+    SwXTextDocument* pXTextDocument = createDoc();
+    CPPUNIT_ASSERT(pXTextDocument);
+    ViewCallback aView;
+    SwDocShell* pDocShell = getSwDocShell();
+    SwWrtShell* pWrtShell = pDocShell->GetWrtShell();
+    pWrtShell->InsertPageBreak();
+    pWrtShell->InsertPageBreak();
+    SwRootFrame* pLayout = pWrtShell->GetLayout();
+    SwPageFrame* pPage1 = pLayout->GetLower()->DynCastPageFrame();
+    pWrtShell->setLOKVisibleArea(pPage1->getFrameArea().SVRect());
+
+    // When all pages get invalidated:
+    pWrtShell->StartAllAction();
+    pPage1->InvalidateContent();
+    SwPageFrame* pPage2 = pPage1->GetNext()->DynCastPageFrame();
+    pPage2->InvalidateContent();
+    SwPageFrame* pPage3 = pPage2->GetNext()->DynCastPageFrame();
+    pPage3->InvalidateContent();
+    pWrtShell->EndAllAction();
+
+    // Then make sure sync layout calculates page 1:
+    CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
+    CPPUNIT_ASSERT(pPage2->IsInvalidContent());
+    CPPUNIT_ASSERT(pPage3->IsInvalidContent());
+
+    // And when doing one idle layout:
+    AnyInputCallback aAnyInput;
+    pWrtShell->LayoutIdle();
+
+    // Then make sure async layout calculates page 2:
+    CPPUNIT_ASSERT(!pPage1->IsInvalidContent());
+    CPPUNIT_ASSERT(!pPage2->IsInvalidContent());
+    // Without the fix in place, async layout calculated all pages, even if 
there were pending input
+    // events.
+    CPPUNIT_ASSERT(pPage3->IsInvalidContent());
+    Scheduler::ProcessEventsToIdle();
+}
 }
 
 CPPUNIT_PLUGIN_IMPLEMENT();
diff --git a/sw/source/core/layout/layact.cxx b/sw/source/core/layout/layact.cxx
index cfd1afbff88f..8c6c55478472 100644
--- a/sw/source/core/layout/layact.cxx
+++ b/sw/source/core/layout/layact.cxx
@@ -88,6 +88,12 @@ inline void SwLayAction::CheckIdleEnd()
 {
     if (!IsInterrupt())
         m_bInterrupt = bool(GetInputType()) && 
Application::AnyInput(GetInputType());
+
+    if (comphelper::LibreOfficeKit::isActive() && !IsInterrupt() && 
bool(GetInputType()))
+    {
+        // Also check if the LOK client has any pending input events.
+        m_bInterrupt = comphelper::LibreOfficeKit::anyInput();
+    }
 }
 
 void SwLayAction::SetStatBar( bool bNew )
@@ -719,7 +725,12 @@ void SwLayAction::InternalAction(OutputDevice* 
pRenderContext)
         // already - the border of the page will never be painted.
         SwPageFrame *pPg = pPage;
         if (lcl_isLayoutLooping()) return;
-        const SwRect &rVis = m_pImp->GetShell()->VisArea();
+        // LOK case: VisArea() is the entire document and getLOKVisibleArea() 
may contain the actual
+        // visible area.
+        const SwRect &rVisArea = m_pImp->GetShell()->VisArea();
+        SwRect aLokVisArea(m_pImp->GetShell()->getLOKVisibleArea());
+        bool bUseLokVisArea = comphelper::LibreOfficeKit::isActive() && 
!aLokVisArea.IsEmpty();
+        const SwRect& rVis = bUseLokVisArea ? aLokVisArea : rVisArea;
 
         while( pPg && pPg->getFrameArea().Bottom() < rVis.Top() )
             pPg = static_cast<SwPageFrame*>(pPg->GetNext());

Reply via email to