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 7ee444ff8da4cad9eafc1668e01a6cd6ef7e1b9c
Author:     Miklos Vajna <vmik...@collabora.com>
AuthorDate: Fri Mar 7 09:05:55 2025 +0100
Commit:     Miklos Vajna <vmik...@collabora.com>
CommitDate: Fri Mar 7 12:11:16 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/+/182615
    Tested-by: Jenkins
    Reviewed-by: Miklos Vajna <vmik...@collabora.com>

diff --git a/desktop/inc/lib/init.hxx b/desktop/inc/lib/init.hxx
index e9a66a1510ec..468e2ee38633 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 6a25358134de..bb957e0611a5 100644
--- a/desktop/qa/desktop_lib/test_desktop_lib.cxx
+++ b/desktop/qa/desktop_lib/test_desktop_lib.cxx
@@ -179,6 +179,7 @@ public:
     void testPartInInvalidation();
     void testBinaryCallback();
     void testOmitInvalidate();
+    void test2ViewsOmitInvalidate();
     void testInput();
     void testRedlineWriter();
     void testRedlineCalc();
@@ -252,6 +253,7 @@ public:
     CPPUNIT_TEST(testPartInInvalidation);
     CPPUNIT_TEST(testBinaryCallback);
     CPPUNIT_TEST(testOmitInvalidate);
+    CPPUNIT_TEST(test2ViewsOmitInvalidate);
     CPPUNIT_TEST(testInput);
     CPPUNIT_TEST(testRedlineWriter);
     CPPUNIT_TEST(testRedlineCalc);
@@ -2078,6 +2080,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 e748997eb326..7e5f864e862a 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -1618,6 +1618,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()
@@ -1822,6 +1828,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)
     {
@@ -4473,11 +4483,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