toolkit/source/hatchwindow/ipwin.cxx | 222 ++++++++++++++++++++++------------- toolkit/source/hatchwindow/ipwin.hxx | 12 + 2 files changed, 149 insertions(+), 85 deletions(-)
New commits: commit 2ae6a6b7e4f81254b0b9c94310aa4e0318f3baae Author: Laurent Balland <laurent.ball...@mailo.fr> AuthorDate: Sat Nov 30 12:25:47 2024 +0100 Commit: Laurent Balland <laurent.ball...@mailo.fr> CommitDate: Sun May 4 10:48:43 2025 +0200 tdf#163816 Keep ratio of OLE object (chart...) When an OLE object (for instance a chart in Calc) is edited, resizing should behave like in non-edited mode: handles in corner resize proportionaly (Widht/Height ratio is preserved) whereas handles on edges modify Width/Height ratio Press Shift key to toggle behavior When draging object with border, press shift key to move object only horizontal/vertical/45° Change-Id: I66df55d3170a094bbd63eff9f6b93915cc57e060 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/177588 Tested-by: Jenkins Reviewed-by: Laurent Balland <laurent.ball...@mailo.fr> diff --git a/toolkit/source/hatchwindow/ipwin.cxx b/toolkit/source/hatchwindow/ipwin.cxx index 3315cd910089..32ab1a7418b7 100644 --- a/toolkit/source/hatchwindow/ipwin.cxx +++ b/toolkit/source/hatchwindow/ipwin.cxx @@ -36,6 +36,7 @@ SvResizeHelper::SvResizeHelper() : aBorder( 5, 5 ) , nGrab( -1 ) + , mofStartingRatio( std::nullopt ) { } @@ -153,14 +154,18 @@ void SvResizeHelper::InvalidateBorder( vcl::Window * pWin ) |* |* Description *************************************************************************/ -bool SvResizeHelper::SelectBegin( vcl::Window * pWin, const Point & rPos ) +bool SvResizeHelper::SelectBegin( vcl::Window * pWin, const Point & rPos, const bool bShiftPressed ) { - if( -1 == nGrab ) + if( -1 == nGrab && pWin ) { - nGrab = SelectMove( pWin, rPos ); + nGrab = SelectMove( pWin, rPos, bShiftPressed ); if( -1 != nGrab ) { aSelPos = rPos; // store start position + mofStartingRatio = std::nullopt; + const auto aWinSize = pWin->GetSizePixel(); + if( aWinSize.Height() ) + mofStartingRatio = aWinSize.Width() / static_cast<double>( aWinSize.Height() ); pWin->CaptureMouse(); return true; } @@ -173,7 +178,7 @@ bool SvResizeHelper::SelectBegin( vcl::Window * pWin, const Point & rPos ) |* |* Description *************************************************************************/ -short SvResizeHelper::SelectMove( vcl::Window * pWin, const Point & rPos ) +short SvResizeHelper::SelectMove( vcl::Window * pWin, const Point & rPos, const bool bShiftPressed ) { if( -1 == nGrab ) { @@ -189,7 +194,7 @@ short SvResizeHelper::SelectMove( vcl::Window * pWin, const Point & rPos ) } else { - tools::Rectangle aRect = pWin->PixelToLogic(GetTrackRectPixel( rPos )); + tools::Rectangle aRect = pWin->PixelToLogic(GetTrackRectPixel( rPos, bShiftPressed )); pWin->ShowTracking( aRect ); } return nGrab; @@ -263,74 +268,131 @@ Point SvResizeHelper::GetTrackPosPixel( const tools::Rectangle & rRect ) const |* |* Description *************************************************************************/ -tools::Rectangle SvResizeHelper::GetTrackRectPixel( const Point & rTrackPos ) const +tools::Rectangle SvResizeHelper::GetTrackRectPixel( const Point & rTrackPos, const bool bShiftPressed ) const { tools::Rectangle aTrackRect; - if( -1 != nGrab ) + if( -1 == nGrab ) + return aTrackRect; + Point aDiff = rTrackPos - aSelPos; + aTrackRect = aOuter; + Point aBR = aOuter.BottomRight(); + bool bRTL = AllSettings::GetLayoutRTL(); + switch( nGrab ) + { + case 0: + aTrackRect.AdjustTop(aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 1: + aTrackRect.AdjustTop(aDiff.Y() ); + break; + case 2: + aTrackRect.AdjustTop(aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 3: + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 4: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.AdjustLeft( -(aDiff.X()) ); + else + aTrackRect.SetRight( aBR.X() + aDiff.X() ); + break; + case 5: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + break; + case 6: + aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 7: + // ugly solution for resizing OLE objects in RTL + if( bRTL ) + aTrackRect.SetRight( aBR.X() - aDiff.X() ); + else + aTrackRect.AdjustLeft(aDiff.X() ); + break; + case 8: + if( bRTL ) + aDiff.setX( -aDiff.X() ); // workaround for move in RTL mode + if( bShiftPressed ) + { + const Point aDiffAbs( std::abs( aDiff.X() ), std::abs( aDiff.Y() ) ); + if( 2 * aDiffAbs.X() < aDiffAbs.Y() ) // vertical move + aDiff.setX( 0L ); + else if( aDiffAbs.X() > 2 * aDiffAbs.Y() ) // horizontal move + aDiff.setY( 0L ); + else if( aDiffAbs.X() != aDiffAbs.Y() ) // 45° move + { + if( aDiffAbs.X() > aDiffAbs.Y() ) + aDiff.setY( aDiff.Y() / aDiffAbs.Y() * aDiffAbs.X() ); + else + aDiff.setX( aDiff.X() / aDiffAbs.X() * aDiffAbs.Y() ); + } + } + aTrackRect.SetPos( aTrackRect.TopLeft() + aDiff ); + break; + } + // TODO: should use same code as other objects. See SdrTextObj::ImpDragCalcRect() and SdrObject::ImpDragCalcRect() + // uneven values are for edges + const bool bIsEdge = ( nGrab != (( nGrab / 2 ) * 2) ); + // tdf#163816: Resizing OLE object in edited mode should behave like in not-edited mode + // Corner handles are proportional by default, whereas edge handles are not proportional + // pressed Shift toggles behavior + const bool bProportional = ( bIsEdge == bShiftPressed ); + if( bProportional && nGrab != 8 && mofStartingRatio ) { - Point aDiff = rTrackPos - aSelPos; - aTrackRect = aOuter; - Point aBR = aOuter.BottomRight(); - bool bRTL = AllSettings::GetLayoutRTL(); - switch( nGrab ) + bool bChangeWidth = false; + if( nGrab == 1 || nGrab == 5 ) // top and bottom handles { - case 0: - aTrackRect.AdjustTop(aDiff.Y() ); - // ugly solution for resizing OLE objects in RTL - if( bRTL ) - aTrackRect.SetRight( aBR.X() - aDiff.X() ); - else - aTrackRect.AdjustLeft(aDiff.X() ); - break; - case 1: - aTrackRect.AdjustTop(aDiff.Y() ); - break; - case 2: - aTrackRect.AdjustTop(aDiff.Y() ); - // ugly solution for resizing OLE objects in RTL - if( bRTL ) - aTrackRect.AdjustLeft( -(aDiff.X()) ); - else - aTrackRect.SetRight( aBR.X() + aDiff.X() ); - break; - case 3: - // ugly solution for resizing OLE objects in RTL - if( bRTL ) - aTrackRect.AdjustLeft( -(aDiff.X()) ); - else - aTrackRect.SetRight( aBR.X() + aDiff.X() ); - break; - case 4: - aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); - // ugly solution for resizing OLE objects in RTL - if( bRTL ) - aTrackRect.AdjustLeft( -(aDiff.X()) ); - else - aTrackRect.SetRight( aBR.X() + aDiff.X() ); - break; - case 5: - aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); - break; - case 6: - aTrackRect.SetBottom( aBR.Y() + aDiff.Y() ); - // ugly solution for resizing OLE objects in RTL - if( bRTL ) - aTrackRect.SetRight( aBR.X() - aDiff.X() ); - else - aTrackRect.AdjustLeft(aDiff.X() ); - break; - case 7: - // ugly solution for resizing OLE objects in RTL - if( bRTL ) - aTrackRect.SetRight( aBR.X() - aDiff.X() ); - else - aTrackRect.AdjustLeft(aDiff.X() ); - break; - case 8: - if( bRTL ) - aDiff.setX( -aDiff.X() ); // workaround for move in RTL mode - aTrackRect.SetPos( aTrackRect.TopLeft() + aDiff ); - break; + bChangeWidth = true; + } + else if ( nGrab != 3 && nGrab != 7 ) + { // handles in corners: keep the largest size + bChangeWidth = aTrackRect.GetWidth() < aTrackRect.GetHeight() * mofStartingRatio.value(); + } + if( bChangeWidth ) + { + const tools::Long nNewWidth = aTrackRect.GetHeight() * mofStartingRatio.value(); + if ( nGrab == 6 || nGrab == 0 ) // corners on left side + // move left with right as reference + aTrackRect.SetLeft( aTrackRect.Right() - nNewWidth ); + else if ( nGrab == 1 || nGrab == 5 ) // top and bottom handles + // move left half of the change of width to keep the handle in the middle + aTrackRect.SetLeft( aTrackRect.Left() + ( aTrackRect.GetWidth() - nNewWidth ) / 2 ); + // other handles use left as reference, then no change needed + aTrackRect.SetWidth( nNewWidth ); + } + else + { + const tools::Long nNewHeight = aTrackRect.GetWidth() / mofStartingRatio.value(); + if ( nGrab == 2 || nGrab == 0 ) // corners on top + // move top with bottom as reference + aTrackRect.SetTop( aTrackRect.Bottom() - nNewHeight ); + else if ( nGrab == 3 || nGrab == 7 ) // right and left handles + // move top half of the change of height to keep the handle in the middle + aTrackRect.SetTop( aTrackRect.Top() + ( aTrackRect.GetHeight() - nNewHeight ) / 2 ); + // other handles use top as reference, then no change needed + aTrackRect.SetHeight( nNewHeight ); } } return aTrackRect; @@ -395,11 +457,11 @@ void SvResizeHelper::ValidateRect( tools::Rectangle & rValidate ) const |* Description *************************************************************************/ bool SvResizeHelper::SelectRelease( vcl::Window * pWin, const Point & rPos, - tools::Rectangle & rOutPosSize ) + tools::Rectangle & rOutPosSize, const bool bShiftPressed ) { if( -1 != nGrab ) { - rOutPosSize = GetTrackRectPixel( rPos ); + rOutPosSize = GetTrackRectPixel( rPos, bShiftPressed ); rOutPosSize.Normalize(); nGrab = -1; pWin->ReleaseMouse(); @@ -461,9 +523,9 @@ void SvResizeWindow::SetHatchBorderPixel( const Size & rSize ) |* |* Description *************************************************************************/ -void SvResizeWindow::SelectMouse( const Point & rPos ) +void SvResizeWindow::SelectMouse( const Point & rPos, const bool bShiftPressed ) { - short nGrab = m_aResizer.SelectMove( this, rPos ); + short nGrab = m_aResizer.SelectMove( this, rPos, bShiftPressed ); if( nGrab >= 4 ) nGrab -= 4; if( m_nMoveGrab == nGrab ) @@ -501,8 +563,8 @@ void SvResizeWindow::SelectMouse( const Point & rPos ) *************************************************************************/ void SvResizeWindow::MouseButtonDown( const MouseEvent & rEvt ) { - if( m_aResizer.SelectBegin( this, rEvt.GetPosPixel() ) ) - SelectMouse( rEvt.GetPosPixel() ); + if( m_aResizer.SelectBegin( this, rEvt.GetPosPixel(), rEvt.IsShift() ) ) + SelectMouse( rEvt.GetPosPixel(), rEvt.IsShift() ); } /************************************************************************* @@ -513,10 +575,10 @@ void SvResizeWindow::MouseButtonDown( const MouseEvent & rEvt ) void SvResizeWindow::MouseMove( const MouseEvent & rEvt ) { if( m_aResizer.GetGrab() == -1 ) - SelectMouse( rEvt.GetPosPixel() ); + SelectMouse( rEvt.GetPosPixel(), rEvt.IsShift() ); else { - tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) ); + tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel(), rEvt.IsShift() ) ); Point aDiff = GetPosPixel(); aRect.SetPos( aRect.TopLeft() + aDiff ); m_aResizer.ValidateRect( aRect ); @@ -525,7 +587,7 @@ void SvResizeWindow::MouseMove( const MouseEvent & rEvt ) aRect.SetPos( aRect.TopLeft() - aDiff ); Point aPos = m_aResizer.GetTrackPosPixel( aRect ); - SelectMouse( aPos ); + SelectMouse( aPos, rEvt.IsShift() ); } } @@ -539,7 +601,7 @@ void SvResizeWindow::MouseButtonUp( const MouseEvent & rEvt ) if( m_aResizer.GetGrab() == -1 ) return; - tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel() ) ); + tools::Rectangle aRect( m_aResizer.GetTrackRectPixel( rEvt.GetPosPixel(), rEvt.IsShift() ) ); Point aDiff = GetPosPixel(); aRect.SetPos( aRect.TopLeft() + aDiff ); // aRect -= GetAllBorderPixel(); @@ -548,7 +610,7 @@ void SvResizeWindow::MouseButtonUp( const MouseEvent & rEvt ) m_pWrapper->QueryObjAreaPixel( aRect ); tools::Rectangle aOutRect; - if( m_aResizer.SelectRelease( this, rEvt.GetPosPixel(), aOutRect ) ) + if( m_aResizer.SelectRelease( this, rEvt.GetPosPixel(), aOutRect, rEvt.IsShift() ) ) { m_nMoveGrab = -1; SetPointer( m_aOldPointer ); diff --git a/toolkit/source/hatchwindow/ipwin.hxx b/toolkit/source/hatchwindow/ipwin.hxx index 72567ce26895..36b1d78c54e4 100644 --- a/toolkit/source/hatchwindow/ipwin.hxx +++ b/toolkit/source/hatchwindow/ipwin.hxx @@ -31,6 +31,8 @@ class SvResizeHelper tools::Rectangle aOuter; short nGrab; // -1 no Grab, 0 - 7, 8 = Move, see FillHandle... Point aSelPos; + /// Initial width/height ratio when starting drag + std::optional<double> mofStartingRatio; public: SvResizeHelper(); @@ -52,12 +54,12 @@ public: std::array<tools::Rectangle,4> FillMoveRectsPixel() const; void Draw(vcl::RenderContext& rRenderContext); void InvalidateBorder( vcl::Window * ); - bool SelectBegin( vcl::Window *, const Point & rPos ); - short SelectMove( vcl::Window * pWin, const Point & rPos ); + bool SelectBegin( vcl::Window *, const Point & rPos, const bool bShiftPressed ); + short SelectMove( vcl::Window * pWin, const Point & rPos, const bool bShiftPressed ); Point GetTrackPosPixel( const tools::Rectangle & rRect ) const; - tools::Rectangle GetTrackRectPixel( const Point & rTrackPos ) const; + tools::Rectangle GetTrackRectPixel( const Point & rTrackPos, const bool bShiftPressed ) const; void ValidateRect( tools::Rectangle & rValidate ) const; - bool SelectRelease( vcl::Window *, const Point & rPos, tools::Rectangle & rOutPosSize ); + bool SelectRelease( vcl::Window *, const Point & rPos, tools::Rectangle & rOutPosSize, const bool bShiftPressed ); void Release( vcl::Window * pWin ); }; @@ -77,7 +79,7 @@ public: void SetHatchBorderPixel( const Size & rSize ); - void SelectMouse( const Point & rPos ); + void SelectMouse( const Point & rPos, const bool bShiftPressed ); virtual void MouseButtonUp( const MouseEvent & rEvt ) override; virtual void MouseMove( const MouseEvent & rEvt ) override; virtual void MouseButtonDown( const MouseEvent & rEvt ) override;