From b285cf49dffe8a74f0ba54b88b781db6a68a4f3b Mon Sep 17 00:00:00 2001 From: László Németh Date: Sat, 8 Jun 2024 23:39:13 +0200 Subject: tdf#157833 tdf#155692 sw: fix hit area to resize row/col MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix 1) missing or too narrow hit area (tdf#157833), and 2) not recognized click inside hit area (tdf#155692). It was impossible or very hard to resize text table columns and rows using the mouse: 1) Double arrow, i.e. "resize row/col" mouse pointer shows the hit area of text table row/column borders. This was missing or was very narrow with normal zoom, because of an obsolete constant for low resolution displays. Change this constant used in IsSame() with scale-dependent values to support the same hit area on different zoom levels and with different screen resolutions. 2) Especially on bigger zoom, "resize row/col" mouse pointer, i. e. the visible hit area didn't guarantee drag & drop any more because of too small hit area in pixels in the svruler code base: clicking on not exactly center of the hit area resulted selection of cells or the cell content instead of drag & drop the border, violating WYSIWYG. Enlarge the default 1 pixel to 5 pixels to cover the whole hit area. Note: only for resizing table borders inside the document, not on the horizontal and vertical rulers. Change-Id: I398a0de782040b0ad18835658ed625117a6e0e06 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/168605 Tested-by: Jenkins Reviewed-by: László Németh --- include/svtools/ruler.hxx | 11 ++- svtools/source/control/ruler.cxx | 20 +++--- sw/qa/extras/uiwriter/uiwriter6.cxx | 134 ++++++++++++++++++++++++++++++++++++ sw/source/core/frmedt/fetab.cxx | 43 ++++++++---- sw/source/uibase/docvw/edtwin3.cxx | 10 ++- 5 files changed, 187 insertions(+), 31 deletions(-) diff --git a/include/svtools/ruler.hxx b/include/svtools/ruler.hxx index 8acc70aa14c1..a7a0a581f941 100644 --- a/include/svtools/ruler.hxx +++ b/include/svtools/ruler.hxx @@ -691,8 +691,12 @@ private: SVT_DLLPRIVATE bool ImplDoHitTest( const Point& rPosition, RulerSelection* pHitTest, bool bRequiredStyle = false, - RulerIndentStyle nRequiredStyle = RulerIndentStyle::Top ) const; - SVT_DLLPRIVATE bool ImplDocHitTest( const Point& rPos, RulerType eDragType, RulerSelection* pHitTest ) const; + RulerIndentStyle nRequiredStyle = RulerIndentStyle::Top, + tools::Long nTolerance = 1 ) const; + SVT_DLLPRIVATE bool ImplDocHitTest( const Point& rPos, + RulerType eDragType, + RulerSelection* pHitTest, + tools::Long nTolerance = 1 ) const; SVT_DLLPRIVATE bool ImplStartDrag( RulerSelection const * pHitTest, sal_uInt16 nModifier ); SVT_DLLPRIVATE void ImplDrag( const Point& rPos ); SVT_DLLPRIVATE void ImplEndDrag(); @@ -743,7 +747,8 @@ public: void SetExtraType( RulerExtra eNewExtraType, sal_uInt16 nStyle = 0 ); bool StartDocDrag( const MouseEvent& rMEvt, - RulerType eDragType ); + RulerType eDragType, + tools::Long nTolerance = 1 ); RulerType GetDragType() const { return meDragType; } tools::Long GetDragPos() const { return mnDragPos; } sal_uInt16 GetDragAryPos() const { return mnDragAryPos; } diff --git a/svtools/source/control/ruler.cxx b/svtools/source/control/ruler.cxx index b5b49047a064..074df6afabcc 100644 --- a/svtools/source/control/ruler.cxx +++ b/svtools/source/control/ruler.cxx @@ -1416,7 +1416,8 @@ void Ruler::ImplUpdate( bool bMustCalc ) } bool Ruler::ImplDoHitTest( const Point& rPos, RulerSelection* pHitTest, - bool bRequireStyle, RulerIndentStyle nRequiredStyle ) const + bool bRequireStyle, RulerIndentStyle nRequiredStyle, + tools::Long nTolerance ) const { sal_Int32 i; sal_uInt16 nStyle; @@ -1557,7 +1558,7 @@ bool Ruler::ImplDoHitTest( const Point& rPos, RulerSelection* pHitTest, } // test the borders - int nBorderTolerance = 1; + int nBorderTolerance = nTolerance; if(pHitTest->bExpandTest) { nBorderTolerance++; @@ -1573,7 +1574,6 @@ bool Ruler::ImplDoHitTest( const Point& rPos, RulerSelection* pHitTest, { n1 -= nBorderTolerance; n2 += nBorderTolerance; - } if ( (nX >= n1) && (nX <= n2) ) @@ -1707,7 +1707,7 @@ bool Ruler::ImplDoHitTest( const Point& rPos, RulerSelection* pHitTest, } bool Ruler::ImplDocHitTest( const Point& rPos, RulerType eDragType, - RulerSelection* pHitTest ) const + RulerSelection* pHitTest, tools::Long nTolerance ) const { Point aPos = rPos; bool bRequiredStyle = false; @@ -1731,7 +1731,7 @@ bool Ruler::ImplDocHitTest( const Point& rPos, RulerType eDragType, else aPos.setX( RULER_OFF + 1 ); - if ( ImplDoHitTest( aPos, pHitTest, bRequiredStyle, nRequiredStyle ) ) + if ( ImplDoHitTest( aPos, pHitTest, bRequiredStyle, nRequiredStyle, nTolerance ) ) { if ( (pHitTest->eType == eDragType) || (eDragType == RulerType::DontKnow) ) return true; @@ -1747,7 +1747,7 @@ bool Ruler::ImplDocHitTest( const Point& rPos, RulerType eDragType, else aPos.setX( mnWidth - RULER_OFF - 1 ); - if ( ImplDoHitTest( aPos, pHitTest, bRequiredStyle, nRequiredStyle ) ) + if ( ImplDoHitTest( aPos, pHitTest, bRequiredStyle, nRequiredStyle, nTolerance ) ) { if ( (pHitTest->eType == eDragType) || (eDragType == RulerType::DontKnow) ) return true; @@ -1762,7 +1762,7 @@ bool Ruler::ImplDocHitTest( const Point& rPos, RulerType eDragType, else aPos.setX( RULER_OFF + (mnVirHeight / 2) ); - if ( ImplDoHitTest( aPos, pHitTest ) ) + if ( ImplDoHitTest( aPos, pHitTest, false, RulerIndentStyle::Top, nTolerance ) ) { if ( (pHitTest->eType == eDragType) || (eDragType == RulerType::DontKnow) ) return true; @@ -2235,7 +2235,7 @@ void Ruler::Deactivate() mbActive = false; } -bool Ruler::StartDocDrag( const MouseEvent& rMEvt, RulerType eDragType ) +bool Ruler::StartDocDrag( const MouseEvent& rMEvt, RulerType eDragType, tools::Long nTolerance ) { if ( !mbDrag ) { @@ -2261,7 +2261,7 @@ bool Ruler::StartDocDrag( const MouseEvent& rMEvt, RulerType eDragType ) if ( nMouseClicks == 1 ) { - if ( ImplDocHitTest( aMousePos, eDragType, &aHitTest ) ) + if ( ImplDocHitTest( aMousePos, eDragType, &aHitTest, nTolerance ) ) { PointerStyle aPtr = PointerStyle::Arrow; @@ -2285,7 +2285,7 @@ bool Ruler::StartDocDrag( const MouseEvent& rMEvt, RulerType eDragType ) } else if ( nMouseClicks == 2 ) { - if ( ImplDocHitTest( aMousePos, eDragType, &aHitTest ) ) + if ( ImplDocHitTest( aMousePos, eDragType, &aHitTest, nTolerance ) ) { mnDragPos = aHitTest.nPos; mnDragAryPos = aHitTest.nAryPos; diff --git a/sw/qa/extras/uiwriter/uiwriter6.cxx b/sw/qa/extras/uiwriter/uiwriter6.cxx index 256a779b2fe2..6560181ab6a7 100644 --- a/sw/qa/extras/uiwriter/uiwriter6.cxx +++ b/sw/qa/extras/uiwriter/uiwriter6.cxx @@ -1489,6 +1489,140 @@ CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf44773) CPPUNIT_ASSERT_GREATER(tools::Long(750), pCellA1->getFrameArea().Height()); } +CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf157833) +{ + // allow resizing table rows & columns using a minimal hit area + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // insert an empty paragraph + pWrtShell->SplitNode(); + + // create a table + SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0); + (void)&pWrtShell->InsertTable(TableOpt, 2, 1); + + // the cursor is not inside the table + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + + uno::Reference xTablesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference xTableNames = xTablesSupplier->getTextTables(); + CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); + uno::Reference xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1->getRows()->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable1->getColumns()->getCount()); + + Scheduler::ProcessEventsToIdle(); + + // set table row height by drag & drop + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + + SwFrame* pPage = pLayout->Lower(); + SwFrame* pBody = pPage->GetLower(); + SwFrame* pTable = pBody->GetLower()->GetNext(); + SwFrame* pRow1 = pTable->GetLower(); + CPPUNIT_ASSERT(pRow1->IsRowFrame()); + SwFrame* pCellA1 = pRow1->GetLower(); + const SwRect& rCellA1Rect = pCellA1->getFrameArea(); + auto nRowHeight = rCellA1Rect.Height(); + // select center of the bottom border of the first table cell + Point ptFrom(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + nRowHeight); + // double the row height + Point ptTo(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + 2 * nRowHeight); + vcl::Window& rEditWin = pDoc->GetDocShell()->GetView()->GetEditWin(); + Point aFrom = rEditWin.LogicToPixel(ptFrom); + Point aArea(aFrom.X(), aFrom.Y() + 2); + MouseEvent aClickEvent(aArea, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + rEditWin.MouseButtonDown(aClickEvent); + Point aTo = rEditWin.LogicToPixel(ptTo); + MouseEvent aMoveEvent(aTo, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + TrackingEvent aTEvt(aMoveEvent, TrackingEventFlags::Repeat); + // drag & drop of cell border inside the document (and outside the table) + // still based on the ruler code, use that to simulate dragging + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt); + TrackingEvent aTEvt2(aMoveEvent, TrackingEventFlags::End); + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt2); + Scheduler::ProcessEventsToIdle(); + rEditWin.CaptureMouse(); + rEditWin.ReleaseMouse(); + + // this was 396 (not modified row height previously, when clicking only in a 2-pixel distance + // from the center of the border) + CPPUNIT_ASSERT_GREATER(tools::Long(750), pCellA1->getFrameArea().Height()); +} + +CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf155692) +{ + // allow resizing table rows & columns using a normal hit area + createSwDoc(); + SwDoc* pDoc = getSwDoc(); + CPPUNIT_ASSERT(pDoc); + SwWrtShell* pWrtShell = pDoc->GetDocShell()->GetWrtShell(); + CPPUNIT_ASSERT(pWrtShell); + + // insert an empty paragraph + pWrtShell->SplitNode(); + + // create a table + SwInsertTableOptions TableOpt(SwInsertTableFlags::DefaultBorder, 0); + (void)&pWrtShell->InsertTable(TableOpt, 2, 1); + + // the cursor is not inside the table + CPPUNIT_ASSERT(!pWrtShell->IsCursorInTable()); + + uno::Reference xTablesSupplier(mxComponent, uno::UNO_QUERY); + uno::Reference xTableNames = xTablesSupplier->getTextTables(); + CPPUNIT_ASSERT(xTableNames->hasByName("Table1")); + uno::Reference xTable1(xTableNames->getByName("Table1"), uno::UNO_QUERY); + CPPUNIT_ASSERT_EQUAL(sal_Int32(2), xTable1->getRows()->getCount()); + CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xTable1->getColumns()->getCount()); + + Scheduler::ProcessEventsToIdle(); + + // set table row height by drag & drop + SwRootFrame* pLayout = pDoc->getIDocumentLayoutAccess().GetCurrentLayout(); + + SwFrame* pPage = pLayout->Lower(); + SwFrame* pBody = pPage->GetLower(); + SwFrame* pTable = pBody->GetLower()->GetNext(); + SwFrame* pRow1 = pTable->GetLower(); + CPPUNIT_ASSERT(pRow1->IsRowFrame()); + SwFrame* pCellA1 = pRow1->GetLower(); + const SwRect& rCellA1Rect = pCellA1->getFrameArea(); + auto nRowHeight = rCellA1Rect.Height(); + // select center of the bottom border of the first table cell + Point ptFrom(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + nRowHeight); + // double the row height + Point ptTo(rCellA1Rect.Left() + rCellA1Rect.Width() / 2, rCellA1Rect.Top() + 2 * nRowHeight); + vcl::Window& rEditWin = pDoc->GetDocShell()->GetView()->GetEditWin(); + Point aFrom = rEditWin.LogicToPixel(ptFrom); + Point aArea(aFrom.X(), aFrom.Y() + 5); + MouseEvent aClickEvent(aArea, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + rEditWin.MouseButtonDown(aClickEvent); + Point aTo = rEditWin.LogicToPixel(ptTo); + MouseEvent aMoveEvent(aTo, 1, MouseEventModifiers::SIMPLECLICK | MouseEventModifiers::SELECT, + MOUSE_LEFT); + TrackingEvent aTEvt(aMoveEvent, TrackingEventFlags::Repeat); + // drag & drop of cell border inside the document (and outside the table) + // still based on the ruler code, use that to simulate dragging + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt); + TrackingEvent aTEvt2(aMoveEvent, TrackingEventFlags::End); + pDoc->GetDocShell()->GetView()->GetVRuler().Tracking(aTEvt2); + Scheduler::ProcessEventsToIdle(); + rEditWin.CaptureMouse(); + rEditWin.ReleaseMouse(); + + // this was 396 (not modified row height previously, when clicking only in a 5-pixel distance + // from the center of the border) + CPPUNIT_ASSERT_GREATER(tools::Long(750), pCellA1->getFrameArea().Height()); +} + CPPUNIT_TEST_FIXTURE(SwUiWriterTest6, testTdf160842) { createSwDoc("tdf160842.fodt"); diff --git a/sw/source/core/frmedt/fetab.cxx b/sw/source/core/frmedt/fetab.cxx index e0606e085f5e..696915947bf1 100644 --- a/sw/source/core/frmedt/fetab.cxx +++ b/sw/source/core/frmedt/fetab.cxx @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -71,7 +72,19 @@ using namespace ::com::sun::star; // also see swtable.cxx #define COLFUZZY 20L -static bool IsSame( tools::Long nA, tools::Long nB ) { return std::abs(nA-nB) <= COLFUZZY; } +static bool IsSame( SwDoc & rDoc, tools::Long nA, tools::Long nB ) +{ + const SwViewShell *pVSh = rDoc.getIDocumentLayoutAccess().GetCurrentViewShell(); + if( !pVSh ) + return std::abs(nA-nB) <= COLFUZZY; + + SdrView* pDrawView = const_cast(pVSh->GetDrawView()); + const auto nOld = pDrawView->GetHitTolerancePixel(); + pDrawView->SetHitTolerancePixel( COLFUZZY/4 ); + bool bRet = std::abs(nA-nB) <= pDrawView->getHitTolLog(); + pDrawView->SetHitTolerancePixel( nOld ); + return bRet; +} namespace { @@ -1556,11 +1569,11 @@ size_t SwFEShell::GetCurTabColNum() const const tools::Long nRight = aTabCols.GetLeftMin() + aTabCols.GetRight(); - if ( !::IsSame( nX, nRight ) ) + if ( !::IsSame( *GetDoc(), nX, nRight ) ) { nX = nRight - nX + aTabCols.GetLeft(); for ( size_t i = 0; i < aTabCols.Count(); ++i ) - if ( ::IsSame( nX, aTabCols[i] ) ) + if ( ::IsSame( *GetDoc(), nX, aTabCols[i] ) ) { nRet = i + 1; break; @@ -1574,10 +1587,10 @@ size_t SwFEShell::GetCurTabColNum() const const tools::Long nLeft = aTabCols.GetLeftMin(); - if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) ) + if ( !::IsSame( *GetDoc(), nX, nLeft + aTabCols.GetLeft() ) ) { for ( size_t i = 0; i < aTabCols.Count(); ++i ) - if ( ::IsSame( nX, nLeft + aTabCols[i] ) ) + if ( ::IsSame( *GetDoc(), nX, nLeft + aTabCols[i] ) ) { nRet = i + 1; break; @@ -1611,7 +1624,7 @@ static const SwFrame *lcl_FindFrameInTab( const SwLayoutFrame *pLay, const Point return nullptr; } -static const SwCellFrame *lcl_FindFrame( const SwLayoutFrame *pLay, const Point &rPt, +static const SwCellFrame *lcl_FindFrame( SwDoc & rDoc, const SwLayoutFrame *pLay, const Point &rPt, SwTwips nFuzzy, bool* pbRow, bool* pbCol ) { // bMouseMoveRowCols : @@ -1771,17 +1784,17 @@ static const SwCellFrame *lcl_FindFrame( const SwLayoutFrame *pLay, const Point const SwTwips nMouseTop = aRectFnSet.IsVert() ? rPt.X() : rPt.Y(); // Do not allow to drag upper table border: - if ( !::IsSame( nTabTop, nMouseTop ) ) + if ( !::IsSame( rDoc, nTabTop, nMouseTop ) ) { - if ( ::IsSame( pFrame->getFrameArea().Left(), rPt.X() ) || - ::IsSame( pFrame->getFrameArea().Right(),rPt.X() ) ) + if ( ::IsSame( rDoc, pFrame->getFrameArea().Left(), rPt.X() ) || + ::IsSame( rDoc, pFrame->getFrameArea().Right(),rPt.X() ) ) { if ( pbRow ) *pbRow = false; pRet = pFrame; break; } - if ( ::IsSame( pFrame->getFrameArea().Top(), rPt.Y() ) || - ::IsSame( pFrame->getFrameArea().Bottom(),rPt.Y() ) ) + if ( ::IsSame( rDoc, pFrame->getFrameArea().Top(), rPt.Y() ) || + ::IsSame( rDoc, pFrame->getFrameArea().Bottom(),rPt.Y() ) ) { if ( pbRow ) *pbRow = true; pRet = pFrame; @@ -1838,14 +1851,14 @@ const SwFrame* SwFEShell::GetBox( const Point &rPt, bool* pbRow, bool* pbCol ) c SwAnchoredObject* pObj = (*pPage->GetSortedObjs())[i]; if ( auto pFlyFrame = pObj->DynCastFlyFrame() ) { - pFrame = lcl_FindFrame( pFlyFrame, rPt, nFuzzy, pbRow, pbCol ); + pFrame = lcl_FindFrame( *GetDoc(), pFlyFrame, rPt, nFuzzy, pbRow, pbCol ); } } } const SwLayoutFrame *pLay = static_cast(pPage->Lower()); while ( pLay && !pFrame ) { - pFrame = lcl_FindFrame( pLay, rPt, nFuzzy, pbRow, pbCol ); + pFrame = lcl_FindFrame( *GetDoc(), pLay, rPt, nFuzzy, pbRow, pbCol ); pLay = static_cast(pLay->GetNext()); } } @@ -2274,10 +2287,10 @@ size_t SwFEShell::GetCurMouseTabColNum( const Point &rPt ) const const tools::Long nLeft = aTabCols.GetLeftMin(); - if ( !::IsSame( nX, nLeft + aTabCols.GetLeft() ) ) + if ( !::IsSame( *GetDoc(), nX, nLeft + aTabCols.GetLeft() ) ) { for ( size_t i = 0; i < aTabCols.Count(); ++i ) - if ( ::IsSame( nX, nLeft + aTabCols[i] ) ) + if ( ::IsSame( *GetDoc(), nX, nLeft + aTabCols[i] ) ) { nRet = i + 1; break; diff --git a/sw/source/uibase/docvw/edtwin3.cxx b/sw/source/uibase/docvw/edtwin3.cxx index 2b9d1a7f1e03..423b6a53847e 100644 --- a/sw/source/uibase/docvw/edtwin3.cxx +++ b/sw/source/uibase/docvw/edtwin3.cxx @@ -84,10 +84,14 @@ void FrameNotify( SwViewShell* pVwSh, FlyMode eMode ) // Notify for page number update bool SwEditWin::RulerColumnDrag( const MouseEvent& rMEvt, bool bVerticalMode) { + // Especially on bigger zoom, changed mouse pointer didn't guarantee + // drag & drop any more because of too small hit area in pixel. + // Enlarge it 5 pixels to cover the whole hit area. + tools::Long nTol = 5L; SvxRuler& rRuler = bVerticalMode ? m_rView.GetVRuler() : m_rView.GetHRuler(); - return (!rRuler.StartDocDrag( rMEvt, RulerType::Border ) && - !rRuler.StartDocDrag( rMEvt, RulerType::Margin1) && - !rRuler.StartDocDrag( rMEvt, RulerType::Margin2)); + return (!rRuler.StartDocDrag( rMEvt, RulerType::Border, nTol ) && + !rRuler.StartDocDrag( rMEvt, RulerType::Margin1, nTol ) && + !rRuler.StartDocDrag( rMEvt, RulerType::Margin2, nTol )); } // #i23726# -- cgit