summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarco Cecchetti <marco.cecchetti@collabora.com>2023-12-04 09:31:23 +0100
committerCaolán McNamara <caolan.mcnamara@collabora.com>2023-12-05 11:08:34 +0100
commit8a732d9ff15ba73835c3fa60208f954b772f5a9c (patch)
treeb619e02545bcf8c1f7c08e9312046e178d655c37
parent4b6a1c05b03f2108ff48018606fde070d0b019bd (diff)
calc: on editing invalidation of view with different zoom is wrong
This patch fixes the following invalidation issue: There are 2 views with different zoom levels. In a view text editing for a cell occurs. The other view is not invalidated properly: the computed invalidation rectangle is misplaced. Change-Id: I72db61486647640ee68e6cb2db96b2902de5b997 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/160303 Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com> Tested-by: Caolán McNamara <caolan.mcnamara@collabora.com> Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
-rw-r--r--editeng/source/editeng/editview.cxx5
-rw-r--r--include/vcl/window.hxx2
-rw-r--r--sc/qa/unit/tiledrendering/tiledrendering.cxx115
-rw-r--r--sc/source/ui/inc/gridwin.hxx1
-rw-r--r--sc/source/ui/view/gridwin4.cxx26
-rw-r--r--vcl/source/window/paint.cxx5
6 files changed, 153 insertions, 1 deletions
diff --git a/editeng/source/editeng/editview.cxx b/editeng/source/editeng/editview.cxx
index 0f53a53de045..6cb2913be3da 100644
--- a/editeng/source/editeng/editview.cxx
+++ b/editeng/source/editeng/editview.cxx
@@ -231,7 +231,10 @@ void EditView::InvalidateOtherViewWindows( const tools::Rectangle& rInvRect )
for (auto& pWin : pImpEditView->aOutWindowSet)
{
if (pWin)
- pWin->Invalidate( bNegativeX ? lcl_negateRectX(rInvRect) : rInvRect );
+ {
+ if (!pWin->InvalidateByForeignEditView(this))
+ pWin->Invalidate( bNegativeX ? lcl_negateRectX(rInvRect) : rInvRect );
+ }
}
}
}
diff --git a/include/vcl/window.hxx b/include/vcl/window.hxx
index d03555bd5b99..829b9f174b57 100644
--- a/include/vcl/window.hxx
+++ b/include/vcl/window.hxx
@@ -62,6 +62,7 @@ class VclWindowEvent;
class AllSettings;
class InputContext;
class VclEventListeners;
+class EditView;
enum class ImplPaintFlags;
enum class VclEventId;
enum class PointerStyle;
@@ -970,6 +971,7 @@ public:
*/
virtual void LogicInvalidate(const tools::Rectangle* pRectangle);
+ virtual bool InvalidateByForeignEditView(EditView* );
/**
* Notification about some rectangle of the output device got invalidated. Used for the
* dialogs and floating windows (e.g. context menu, popup).
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 1f18401a970c..7d89cb787c85 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -9,6 +9,7 @@
#include <test/unoapixml_test.hxx>
#include <test/helper/transferable.hxx>
+#include <cppunit/tools/StringHelper.h>
#include <boost/property_tree/json_parser.hpp>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
@@ -60,6 +61,31 @@ static std::ostream& operator<<(std::ostream& os, ViewShellId const & id)
os << static_cast<sal_Int32>(id); return os;
}
+// for passing data to testInvalidateOnTextEditWithDifferentZoomLevels
+struct ColRowZoom
+{
+ SCCOL col;
+ SCROW row;
+ int zoom;
+};
+
+CPPUNIT_NS_BEGIN
+namespace StringHelper
+{
+// used by CPPUNIT_TEST_PARAMETERIZED for testInvalidateOnTextEditWithDifferentZoomLevels
+template<>
+inline std::string toString(const ColRowZoom& item)
+{
+ std::ostringstream ss;
+ ss << "zoom level: " << item.zoom << ", "
+ << "col: " << item.col << ", "
+ << "row: " << item.row;
+ return ss.str();
+}
+
+}
+CPPUNIT_NS_END
+
namespace
{
@@ -129,6 +155,7 @@ public:
void testUndoReorderingRedo();
void testUndoReorderingMulti();
void testGetViewRenderState();
+ void testInvalidateOnTextEditWithDifferentZoomLevels(const ColRowZoom& rData);
CPPUNIT_TEST_SUITE(ScTiledRenderingTest);
CPPUNIT_TEST(testRowColumnHeaders);
@@ -190,6 +217,14 @@ public:
CPPUNIT_TEST(testUndoReorderingRedo);
CPPUNIT_TEST(testUndoReorderingMulti);
CPPUNIT_TEST(testGetViewRenderState);
+ CPPUNIT_TEST_PARAMETERIZED(testInvalidateOnTextEditWithDifferentZoomLevels,
+ std::initializer_list<ColRowZoom>
+ {
+ // zoom level 120%
+ {0, 999, 1}, {99, 0, 1},
+ // zoom level 40%
+ {0, 999, -5}, {99, 0, -5}
+ });
CPPUNIT_TEST_SUITE_END();
private:
@@ -3286,6 +3321,86 @@ void ScTiledRenderingTest::testGetViewRenderState()
CPPUNIT_ASSERT_EQUAL(OString(";Default"), pModelObj->getViewRenderState());
}
+/*
+ * testInvalidateOnTextEditWithDifferentZoomLevels
+ * steps:
+ * set view 1 zoom to the passed zoom level
+ * in view 1 type a char at the passed cell address
+ * store invalidation rectangle
+ * exit from in place editing (press esc)
+ * create view 2 (keep 100% zoom)
+ * go to the same cell address used in view 1
+ * type a char into the cell
+ * get invalidation rectangle for view 1
+ * check if the invalidation rectangle is equal to the one stored previously
+*/
+void ScTiledRenderingTest::testInvalidateOnTextEditWithDifferentZoomLevels(const ColRowZoom& rData)
+{
+ ScModelObj* pModelObj = createDoc("empty.ods");
+ CPPUNIT_ASSERT(pModelObj);
+ ScDocument* pDoc = pModelObj->GetDocument();
+ CPPUNIT_ASSERT(pDoc);
+
+ OUString sZoomUnoCmd = ".uno:ZoomPlus";
+ int nZoomLevel = rData.zoom;
+ if (nZoomLevel < 0)
+ {
+ nZoomLevel = -nZoomLevel;
+ sZoomUnoCmd = ".uno:ZoomMinus";
+ }
+
+ // view #1
+ ViewCallback aView1;
+ // set zoom level
+ for (int i = 0; i < nZoomLevel; ++i)
+ dispatchCommand(mxComponent, sZoomUnoCmd, {});
+ Scheduler::ProcessEventsToIdle();
+
+ auto* pTabViewShell1 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+ CPPUNIT_ASSERT(pTabViewShell1);
+
+ // enable in place editing in view 1
+ auto& rInvalidations = aView1.m_aInvalidations;
+ pTabViewShell1->SetCursor(rData.col, rData.row);
+ Scheduler::ProcessEventsToIdle();
+ aView1.m_bInvalidateTiles = false;
+ rInvalidations.clear();
+ pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
+ pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
+ CPPUNIT_ASSERT(!rInvalidations.empty());
+ tools::Rectangle aInvRect1 = rInvalidations[0];
+
+ // end editing
+ pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 0, awt::Key::ESCAPE);
+ pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 0, awt::Key::ESCAPE);
+ Scheduler::ProcessEventsToIdle();
+
+ // view #2
+ SfxLokHelper::createView();
+ pModelObj->initializeForTiledRendering(uno::Sequence<beans::PropertyValue>());
+ ViewCallback aView2;
+ Scheduler::ProcessEventsToIdle();
+
+ auto* pTabViewShell2 = dynamic_cast<ScTabViewShell*>(SfxViewShell::Current());
+ CPPUNIT_ASSERT(pTabViewShell2);
+ pTabViewShell2->SetCursor(rData.col, rData.row);
+ Scheduler::ProcessEventsToIdle();
+
+ // text edit in view #2
+ aView1.m_bInvalidateTiles = false;
+ rInvalidations.clear();
+ pModelObj->postKeyEvent(LOK_KEYEVENT_KEYINPUT, 'x', 0);
+ pModelObj->postKeyEvent(LOK_KEYEVENT_KEYUP, 'x', 0);
+ Scheduler::ProcessEventsToIdle();
+ CPPUNIT_ASSERT(aView1.m_bInvalidateTiles);
+ CPPUNIT_ASSERT(!rInvalidations.empty());
+ tools::Rectangle aInvRect2 = rInvalidations[0];
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalidation rectangle is wrong.", aInvRect1, aInvRect2);
+}
+
}
CPPUNIT_TEST_SUITE_REGISTRATION(ScTiledRenderingTest);
diff --git a/sc/source/ui/inc/gridwin.hxx b/sc/source/ui/inc/gridwin.hxx
index 8334e5fd0d51..048d2d3d9359 100644
--- a/sc/source/ui/inc/gridwin.hxx
+++ b/sc/source/ui/inc/gridwin.hxx
@@ -380,6 +380,7 @@ public:
void LogicInvalidate(const tools::Rectangle* pRectangle) override;
void LogicInvalidatePart(const tools::Rectangle* pRectangle, int nPart);
+ bool InvalidateByForeignEditView(EditView* pEditView) override;
/// Update the cell selection according to what handles have been dragged.
/// @see vcl::ITiledRenderable::setTextSelection() for the values of nType.
/// Coordinates are in pixels.
diff --git a/sc/source/ui/view/gridwin4.cxx b/sc/source/ui/view/gridwin4.cxx
index 1f235fcd6aa4..390c4b2bdb63 100644
--- a/sc/source/ui/view/gridwin4.cxx
+++ b/sc/source/ui/view/gridwin4.cxx
@@ -671,6 +671,11 @@ int lcl_GetMultiLineHeight(EditEngine* pEditEngine)
return nHeight;
}
+
+tools::Rectangle lcl_negateRectX(const tools::Rectangle& rRect)
+{
+ return tools::Rectangle(-rRect.Right(), rRect.Top(), -rRect.Left(), rRect.Bottom());
+}
}
void ScGridWindow::DrawContent(OutputDevice &rDevice, const ScTableInfo& rTableInfo, ScOutputData& aOutputData,
@@ -1765,6 +1770,27 @@ void ScGridWindow::LogicInvalidate(const tools::Rectangle* pRectangle)
LogicInvalidatePart(pRectangle, pViewShell->getPart());
}
+bool ScGridWindow::InvalidateByForeignEditView(EditView* pEditView)
+{
+ if (!pEditView)
+ return false;
+
+ auto* pGridWin = dynamic_cast<ScGridWindow*>(pEditView->GetWindow());
+ if (!pGridWin)
+ return false;
+
+ const ScViewData& rViewData = pGridWin->getViewData();
+ tools::Long nRefTabNo = rViewData.GetRefTabNo();
+ tools::Long nX = rViewData.GetCurXForTab(nRefTabNo);
+ tools::Long nY = rViewData.GetCurYForTab(nRefTabNo);
+
+ tools::Rectangle aPixRect = getViewData().GetEditArea(eWhich, nX, nY, this, nullptr, true);
+ tools::Rectangle aLogicRect = PixelToLogic(aPixRect, getViewData().GetLogicMode());
+ Invalidate(pEditView->IsNegativeX() ? lcl_negateRectX(aLogicRect) : aLogicRect);
+
+ return true;
+}
+
void ScGridWindow::SetCellSelectionPixel(int nType, int nPixelX, int nPixelY)
{
ScTabView* pTabView = mrViewData.GetView();
diff --git a/vcl/source/window/paint.cxx b/vcl/source/window/paint.cxx
index a70f1c0e4004..2e9b153bc961 100644
--- a/vcl/source/window/paint.cxx
+++ b/vcl/source/window/paint.cxx
@@ -1198,6 +1198,11 @@ void Window::LogicInvalidate(const tools::Rectangle* pRectangle)
PixelInvalidate(nullptr);
}
+bool Window::InvalidateByForeignEditView(EditView* )
+{
+ return false;
+}
+
void Window::PixelInvalidate(const tools::Rectangle* pRectangle)
{
if (comphelper::LibreOfficeKit::isDialogPainting() || !comphelper::LibreOfficeKit::isActive())