desktop/inc/lib/init.hxx                    |    4 +++
 desktop/qa/desktop_lib/test_desktop_lib.cxx |   34 +++++++++++++++++++++++++++
 desktop/source/lib/init.cxx                 |   35 ++++++++++++++++++++++++----
 3 files changed, 69 insertions(+), 4 deletions(-)

New commits:
commit 6f2b99b10f27a8523864d26028b10b9c478a7d16
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Mar 7 09:05:55 2025 +0100
Commit:     Caolán McNamara <caolan.mcnam...@collabora.com>
CommitDate: Fri Mar 7 10:11:07 2025 +0100

    cool#11289 desktop lok: don't assume all views invalidate the same area
    
    Have 2 Calc views, go to e.g. F30 in the first view and F31 in the
    second view. Type enough in the 2nd view that the text goes outside the
    cell, to a next tile, e.g. till the column I area is reached; text
    doesn't show up in the new tile area on typing.
    
    This went wrong in commit 43965ae570ded21653dabb5ec0570f8aaa7c497e
    (cool#11254 desktop lok: avoid invalidations if no tiles are sent,
    2025-03-05), because it assumed that all views get the same
    invalidations, but they do not in this case:
    debug:12008:11949: ScGridWindow::LogicInvalidate: view #5, rect is 
6670x345@(20452,14102)
    debug:12008:11949: ScGridWindow::LogicInvalidate: view #4, rect is 
2144x372@(20452,14102)
    debug:12008:11949: ScGridWindow::LogicInvalidate: view #5, rect is 
6670x345@(20452,14102)
    debug:12008:11949: ScGridWindow::LogicInvalidate: view #4, rect is 
2144x372@(20452,14102)
    debug:12008:11949: ScGridWindow::PaintTile: view #4, x is 11520, y is 7680, 
w is 3840, h is 3840
    
    Fix the problem by tracking paints in all views of a view group sharing
    the same render state ("have the same canonicalviewid").
    
    Getting the view render state on tile paint for each view would be
    expensive, so get that once and then subscribe to the update callback.
    The view render state is then still compared as string -- if that shows
    up in a profile, we could switch to integer-based tracking instead with
    some added complexity ("canonical view id").
    
    Change-Id: I151b1ff9713ea13f2ee218b4cb44e4b0a227abcb
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/182611
    Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoff...@gmail.com>
    Reviewed-by: Caolán McNamara <caolan.mcnam...@collabora.com>
    Tested-by: Caolán McNamara <caolan.mcnam...@collabora.com>

diff --git a/desktop/inc/lib/init.hxx b/desktop/inc/lib/init.hxx
index 427a87a7fc97..0341cc0aa9c8 100644
--- a/desktop/inc/lib/init.hxx
+++ b/desktop/inc/lib/init.hxx
@@ -116,6 +116,7 @@ namespace desktop {
         void setViewId( int viewId ) { m_viewId = viewId; }
 
         void tilePainted(int nPart, int nMode, const tools::Rectangle& 
rRectangle);
+        const OString& getViewRenderState() const { return m_aViewRenderState; 
}
 
         // SfxLockCallbackInterface
         virtual void libreOfficeKitViewCallback(int nType, const OString& 
pPayload) override;
@@ -235,6 +236,7 @@ namespace desktop {
         boost::container::flat_map<int, std::vector<PerViewIdData>> 
m_updatedTypesPerViewId; // key is view, index is type
 
         LibreOfficeKitDocument* m_pDocument;
+        OString m_aViewRenderState;
         int m_viewId = -1; // view id of the associated SfxViewShell
         LibreOfficeKitCallback m_pCallback;
         ImplSVEvent* m_pFlushEvent;
@@ -256,6 +258,8 @@ namespace desktop {
         explicit LibLODocument_Impl(css::uno::Reference<css::lang::XComponent> 
xComponent,
                                     int nDocumentId);
         ~LibLODocument_Impl();
+
+        void updateViewsForPaintedTile(int nOrigViewId, int nPart, int nMode, 
const tools::Rectangle& rRectangle);
     };
 
     struct DESKTOP_DLLPUBLIC LibLibreOffice_Impl : public _LibreOfficeKit
diff --git a/desktop/qa/desktop_lib/test_desktop_lib.cxx 
b/desktop/qa/desktop_lib/test_desktop_lib.cxx
index b20533d6eb3b..088026e52685 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -185,6 +185,7 @@ public:
     void testPartInInvalidation();
     void testBinaryCallback();
     void testOmitInvalidate();
+    void test2ViewsOmitInvalidate();
     void testInput();
     void testRedlineWriter();
     void testRedlineCalc();
@@ -258,6 +259,7 @@ public:
     CPPUNIT_TEST(testPartInInvalidation);
     CPPUNIT_TEST(testBinaryCallback);
     CPPUNIT_TEST(testOmitInvalidate);
+    CPPUNIT_TEST(test2ViewsOmitInvalidate);
     CPPUNIT_TEST(testInput);
     CPPUNIT_TEST(testRedlineWriter);
     CPPUNIT_TEST(testRedlineCalc);
@@ -2084,6 +2086,38 @@ void DesktopLOKTest::testOmitInvalidate()
     }
 }
 
+void DesktopLOKTest::test2ViewsOmitInvalidate()
+{
+    // Given a document with 2 views:
+    LibLODocument_Impl* pDocument = loadDoc("blank_text.odt");
+    std::vector<std::tuple<int, std::string>> aCallbacks1;
+    std::shared_ptr<CallbackFlushHandler> pHandler1(new 
CallbackFlushHandler(pDocument, callbackBinaryCallbackTest, &aCallbacks1));
+    pHandler1->setViewId(0);
+    pDocument->mpCallbackFlushHandlers[0] = pHandler1;
+    std::vector<std::tuple<int, std::string>> aCallbacks2;
+    std::shared_ptr<CallbackFlushHandler> pHandler2(new 
CallbackFlushHandler(pDocument, callbackBinaryCallbackTest, &aCallbacks2));
+    pHandler2->setViewId(1);
+    pDocument->mpCallbackFlushHandlers[1] = pHandler2;
+
+    // When painting a tile for a larger area, and then 2 invalidates: the 
first view gets a smaller
+    // invalidate, the second view gets a larger invalidate:
+    tools::Rectangle aPaint{Point(0, 0), Size(20, 10)};
+    pDocument->updateViewsForPaintedTile(/*nOrigViewId=*/0, /*nPart=*/0, 
/*nMode=*/0, aPaint);
+    tools::Rectangle aSmaller{Point(0, 0), Size(10, 10)};
+    pHandler1->libreOfficeKitViewInvalidateTilesCallback(&aSmaller, 
/*nPart=*/0, /*nMode=*/0);
+    tools::Rectangle aLarger{Point(0, 0), Size(20, 10)};
+    pHandler2->libreOfficeKitViewInvalidateTilesCallback(&aLarger, 
/*nPart=*/0, /*nMode=*/0);
+
+    // Then make sure this larger invalidate for the 2nd view is not lost:
+    Scheduler::ProcessEventsToIdle();
+    // Without the accompanying fix in place, this test would have failed with:
+    // - Expected: 1
+    // - Actual  : 0
+    // i.e. the 2nd view's (larger) invalidate was lost.
+    CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aCallbacks2.size());
+    CPPUNIT_ASSERT_EQUAL(std::string("0, 0, 19, 9, 0, 0"), 
std::get<1>(aCallbacks2[0]));
+}
+
 void DesktopLOKTest::testInput()
 {
     // Load a Writer document, enable change recording and press a key.
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index b0ee1d36a431..8a18622b79f2 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -1627,6 +1627,12 @@ 
CallbackFlushHandler::CallbackFlushHandler(LibreOfficeKitDocument* pDocument, Li
     m_states.emplace(LOK_CALLBACK_RULER_UPDATE, "NIL"_ostr);
     m_states.emplace(LOK_CALLBACK_VERTICAL_RULER_UPDATE, "NIL"_ostr);
     m_states.emplace(LOK_CALLBACK_STATUS_INDICATOR_SET_VALUE, "NIL"_ostr);
+
+    if (char* pViewRenderState = 
pDocument->pClass->getCommandValues(pDocument, ".uno:ViewRenderState"))
+    {
+        m_aViewRenderState = pViewRenderState;
+        free(pViewRenderState);
+    }
 }
 
 void CallbackFlushHandler::stop()
@@ -1831,6 +1837,10 @@ void CallbackFlushHandler::queue(const int type, 
CallbackData& aCallbackData)
     {
         bIsComment = true;
     }
+    else if (type == LOK_CALLBACK_VIEW_RENDER_STATE)
+    {
+        m_aViewRenderState = aCallbackData.getPayload();
+    }
 
     if (callbacksDisabled() && !bIsChartActive && !bIsComment)
     {
@@ -4503,11 +4513,28 @@ static void doc_paintPartTile(LibreOfficeKitDocument* 
pThis,
 
     enableViewCallbacks(pDocument, nOrigViewId);
 
-    auto it = pDocument->mpCallbackFlushHandlers.find(nOrigViewId);
-    if (it != pDocument->mpCallbackFlushHandlers.end())
+    // Inform all views with the same view render state about the paint, so 
they know if makes sense
+    // to invalidate those areas later.
+    tools::Rectangle aRectangle{Point(nTilePosX, nTilePosY), Size(nTileWidth, 
nTileHeight)};
+    pDocument->updateViewsForPaintedTile(nOrigViewId, nPart, nMode, 
aRectangle);
+}
+
+void LibLODocument_Impl::updateViewsForPaintedTile(int nOrigViewId, int nPart, 
int nMode, const tools::Rectangle& rRectangle)
+{
+    auto it = mpCallbackFlushHandlers.find(nOrigViewId);
+    if (it == mpCallbackFlushHandlers.end())
     {
-        tools::Rectangle aRectangle{Point(nTilePosX, nTilePosY), 
Size(nTileWidth, nTileHeight)};
-        it->second->tilePainted(nPart, nMode, aRectangle);
+        return;
+    }
+
+    const OString& rViewRenderState = it->second->getViewRenderState();
+    for (const auto& rHandler : mpCallbackFlushHandlers)
+    {
+        if (rHandler.second->getViewRenderState() != rViewRenderState)
+        {
+            continue;
+        }
+        rHandler.second->tilePainted(nPart, nMode, rRectangle);
     }
 }
 

Reply via email to