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.balland@mailo.fr>
This commit is contained in:
Laurent Balland
2025-05-04 10:48:43 +02:00
parent 11d8753e03
commit 2ae6a6b7e4
2 changed files with 149 additions and 85 deletions
+142 -80
View File
@@ -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 )
{
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 )
{
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 );
+7 -5
View File
@@ -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;