Allow panning with selectable key

Add optins to mouse dialog to allow modifier key-based mouse movement
panning

Fixes https://gitlab.com/kicad/code/kicad/-/issues/2492
This commit is contained in:
Seth Hillbrand 2025-08-11 13:18:18 -07:00
parent 833a5ee3ab
commit a7e6fa8198
11 changed files with 291 additions and 82 deletions

View File

@ -111,6 +111,14 @@ bool PANEL_MOUSE_SETTINGS::TransferDataFromWindow()
default: break; default: break;
} }
switch( m_choicePanMoveKey->GetSelection() )
{
case 1: cfg->m_Input.motion_pan_modifier = WXK_ALT; break;
case 2: cfg->m_Input.motion_pan_modifier = WXK_CONTROL; break;
case 3: cfg->m_Input.motion_pan_modifier = WXK_SHIFT; break;
default: cfg->m_Input.motion_pan_modifier = 0; break;
}
cfg->m_Input.center_on_zoom = m_checkZoomCenter->GetValue(); cfg->m_Input.center_on_zoom = m_checkZoomCenter->GetValue();
cfg->m_Input.auto_pan = m_checkAutoPan->GetValue(); cfg->m_Input.auto_pan = m_checkAutoPan->GetValue();
cfg->m_Input.auto_pan_acceleration = m_autoPanSpeed->GetValue(); cfg->m_Input.auto_pan_acceleration = m_autoPanSpeed->GetValue();
@ -178,6 +186,14 @@ void PANEL_MOUSE_SETTINGS::applySettingsToPanel( const COMMON_SETTINGS& aSetting
default: break; default: break;
} }
switch( aSettings.m_Input.motion_pan_modifier )
{
case WXK_ALT: m_choicePanMoveKey->SetSelection( 1 ); break;
case WXK_CONTROL: m_choicePanMoveKey->SetSelection( 2 ); break;
case WXK_SHIFT: m_choicePanMoveKey->SetSelection( 3 ); break;
default: m_choicePanMoveKey->SetSelection( 0 ); break;
}
m_currentScrollMod.zoom = aSettings.m_Input.scroll_modifier_zoom; m_currentScrollMod.zoom = aSettings.m_Input.scroll_modifier_zoom;
m_currentScrollMod.panh = aSettings.m_Input.scroll_modifier_pan_h; m_currentScrollMod.panh = aSettings.m_Input.scroll_modifier_pan_h;
m_currentScrollMod.panv = aSettings.m_Input.scroll_modifier_pan_v; m_currentScrollMod.panv = aSettings.m_Input.scroll_modifier_pan_v;

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) // C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/ // http://www.wxformbuilder.org/
// //
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!
@ -156,6 +156,19 @@ PANEL_MOUSE_SETTINGS_BASE::PANEL_MOUSE_SETTINGS_BASE( wxWindow* parent, wxWindow
fgSizer1->Add( m_choiceRightButtonDrag, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 ); fgSizer1->Add( m_choiceRightButtonDrag, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
fgSizer1->Add( 0, 0, 1, wxEXPAND, 5 );
m_panMoveKeyLabel = new wxStaticText( this, wxID_ANY, _("Pan on mouse movement with key:"), wxDefaultPosition, wxDefaultSize, 0 );
m_panMoveKeyLabel->Wrap( -1 );
fgSizer1->Add( m_panMoveKeyLabel, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT, 5 );
wxString m_choicePanMoveKeyChoices[] = { _("None"), _("Alt"), _("Ctrl"), _("Shift") };
int m_choicePanMoveKeyNChoices = sizeof( m_choicePanMoveKeyChoices ) / sizeof( wxString );
m_choicePanMoveKey = new wxChoice( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, m_choicePanMoveKeyNChoices, m_choicePanMoveKeyChoices, 0 );
m_choicePanMoveKey->SetSelection( 0 );
fgSizer1->Add( m_choicePanMoveKey, 0, wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT, 5 );
fgSizer1->Add( 0, 0, 1, wxEXPAND, 5 ); fgSizer1->Add( 0, 0, 1, wxEXPAND, 5 );

View File

@ -934,11 +934,11 @@
<property name="width">0</property> <property name="width">0</property>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="false"> <object class="sizeritem" expanded="true">
<property name="border">5</property> <property name="border">5</property>
<property name="flag">wxEXPAND|wxALL</property> <property name="flag">wxEXPAND|wxALL</property>
<property name="proportion">1</property> <property name="proportion">1</property>
<object class="wxFlexGridSizer" expanded="false"> <object class="wxFlexGridSizer" expanded="true">
<property name="cols">3</property> <property name="cols">3</property>
<property name="flexible_direction">wxHORIZONTAL</property> <property name="flexible_direction">wxHORIZONTAL</property>
<property name="growablecols">2</property> <property name="growablecols">2</property>
@ -1361,6 +1361,143 @@
<property name="width">0</property> <property name="width">0</property>
</object> </object>
</object> </object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxStaticText" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="label">Pan on mouse movement with key:</property>
<property name="markup">0</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_panMoveKeyLabel</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
<property name="wrap">-1</property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxALIGN_CENTER_VERTICAL|wxBOTTOM|wxEXPAND|wxLEFT|wxRIGHT</property>
<property name="proportion">0</property>
<object class="wxChoice" expanded="false">
<property name="BottomDockable">1</property>
<property name="LeftDockable">1</property>
<property name="RightDockable">1</property>
<property name="TopDockable">1</property>
<property name="aui_layer">0</property>
<property name="aui_name"></property>
<property name="aui_position">0</property>
<property name="aui_row">0</property>
<property name="best_size"></property>
<property name="bg"></property>
<property name="caption"></property>
<property name="caption_visible">1</property>
<property name="center_pane">0</property>
<property name="choices">&quot;None&quot; &quot;Alt&quot; &quot;Ctrl&quot; &quot;Shift&quot;</property>
<property name="close_button">1</property>
<property name="context_help"></property>
<property name="context_menu">1</property>
<property name="default_pane">0</property>
<property name="dock">Dock</property>
<property name="dock_fixed">0</property>
<property name="docking">Left</property>
<property name="drag_accept_files">0</property>
<property name="enabled">1</property>
<property name="fg"></property>
<property name="floatable">1</property>
<property name="font"></property>
<property name="gripper">0</property>
<property name="hidden">0</property>
<property name="id">wxID_ANY</property>
<property name="max_size"></property>
<property name="maximize_button">0</property>
<property name="maximum_size"></property>
<property name="min_size"></property>
<property name="minimize_button">0</property>
<property name="minimum_size"></property>
<property name="moveable">1</property>
<property name="name">m_choicePanMoveKey</property>
<property name="pane_border">1</property>
<property name="pane_position"></property>
<property name="pane_size"></property>
<property name="permission">protected</property>
<property name="pin_button">1</property>
<property name="pos"></property>
<property name="resize">Resizable</property>
<property name="selection">0</property>
<property name="show">1</property>
<property name="size"></property>
<property name="style"></property>
<property name="subclass">; ; forward_declare</property>
<property name="toolbar_pane">0</property>
<property name="tooltip"></property>
<property name="validator_data_type"></property>
<property name="validator_style">wxFILTER_NONE</property>
<property name="validator_type">wxDefaultValidator</property>
<property name="validator_variable"></property>
<property name="window_extra_style"></property>
<property name="window_name"></property>
<property name="window_style"></property>
</object>
</object>
<object class="sizeritem" expanded="false">
<property name="border">5</property>
<property name="flag">wxEXPAND</property>
<property name="proportion">1</property>
<object class="spacer" expanded="false">
<property name="height">0</property>
<property name="permission">protected</property>
<property name="width">0</property>
</object>
</object>
</object> </object>
</object> </object>
</object> </object>

View File

@ -1,5 +1,5 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6) // C++ code generated with wxFormBuilder (version 4.2.1-0-g80c4cb6a-dirty)
// http://www.wxformbuilder.org/ // http://www.wxformbuilder.org/
// //
// PLEASE DO *NOT* EDIT THIS FILE! // PLEASE DO *NOT* EDIT THIS FILE!
@ -61,6 +61,8 @@ class PANEL_MOUSE_SETTINGS_BASE : public RESETTABLE_PANEL
wxChoice* m_choiceMiddleButtonDrag; wxChoice* m_choiceMiddleButtonDrag;
wxStaticText* m_staticText31; wxStaticText* m_staticText31;
wxChoice* m_choiceRightButtonDrag; wxChoice* m_choiceRightButtonDrag;
wxStaticText* m_panMoveKeyLabel;
wxChoice* m_choicePanMoveKey;
wxStaticText* m_scrollLabel; wxStaticText* m_scrollLabel;
wxStaticLine* m_staticline2; wxStaticLine* m_staticline2;
wxStaticText* m_staticText21; wxStaticText* m_staticText21;

View File

@ -716,6 +716,7 @@ KIGFX::VC_SETTINGS EDA_DRAW_PANEL_GAL::GetVcSettings()
vcSettings.m_scrollModifierZoom = cfg->m_Input.scroll_modifier_zoom; vcSettings.m_scrollModifierZoom = cfg->m_Input.scroll_modifier_zoom;
vcSettings.m_scrollModifierPanH = cfg->m_Input.scroll_modifier_pan_h; vcSettings.m_scrollModifierPanH = cfg->m_Input.scroll_modifier_pan_h;
vcSettings.m_scrollModifierPanV = cfg->m_Input.scroll_modifier_pan_v; vcSettings.m_scrollModifierPanV = cfg->m_Input.scroll_modifier_pan_v;
vcSettings.m_motionPanModifier = cfg->m_Input.motion_pan_modifier;
vcSettings.m_dragLeft = cfg->m_Input.drag_left; vcSettings.m_dragLeft = cfg->m_Input.drag_left;
vcSettings.m_dragMiddle = cfg->m_Input.drag_middle; vcSettings.m_dragMiddle = cfg->m_Input.drag_middle;
vcSettings.m_dragRight = cfg->m_Input.drag_right; vcSettings.m_dragRight = cfg->m_Input.drag_right;

View File

@ -269,6 +269,9 @@ COMMON_SETTINGS::COMMON_SETTINGS() :
m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_v", m_params.emplace_back( new PARAM<int>( "input.scroll_modifier_pan_v",
&m_Input.scroll_modifier_pan_v, WXK_SHIFT ) ); &m_Input.scroll_modifier_pan_v, WXK_SHIFT ) );
m_params.emplace_back( new PARAM<int>( "input.motion_pan_modifier",
&m_Input.motion_pan_modifier, 0 ) );
m_params.emplace_back( new PARAM<bool>( "input.reverse_scroll_zoom", m_params.emplace_back( new PARAM<bool>( "input.reverse_scroll_zoom",
&m_Input.reverse_scroll_zoom, false ) ); &m_Input.reverse_scroll_zoom, false ) );

View File

@ -76,6 +76,7 @@ void VC_SETTINGS::Reset()
m_scrollModifierZoom = 0; m_scrollModifierZoom = 0;
m_scrollModifierPanH = WXK_CONTROL; m_scrollModifierPanH = WXK_CONTROL;
m_scrollModifierPanV = WXK_SHIFT; m_scrollModifierPanV = WXK_SHIFT;
m_motionPanModifier = 0;
m_dragLeft = MOUSE_DRAG_ACTION::NONE; m_dragLeft = MOUSE_DRAG_ACTION::NONE;
m_dragMiddle = MOUSE_DRAG_ACTION::PAN; m_dragMiddle = MOUSE_DRAG_ACTION::PAN;
m_dragRight = MOUSE_DRAG_ACTION::PAN; m_dragRight = MOUSE_DRAG_ACTION::PAN;

View File

@ -35,7 +35,7 @@
#include <tool/tool_dispatcher.h> #include <tool/tool_dispatcher.h>
#include <trace_helpers.h> #include <trace_helpers.h>
#include <settings/common_settings.h> #include <settings/common_settings.h>
#include <math/util.h> // for KiROUND #include <math/util.h> // for KiROUND
#include <geometry/geometry_utils.h> #include <geometry/geometry_utils.h>
#include <widgets/ui_common.h> #include <widgets/ui_common.h>
#include <class_draw_panel_gal.h> #include <class_draw_panel_gal.h>
@ -45,7 +45,7 @@
#include <wx/log.h> #include <wx/log.h>
#ifdef __WXMSW__ #ifdef __WXMSW__
#define USE_MOUSE_CAPTURE #define USE_MOUSE_CAPTURE
#endif #endif
using namespace KIGFX; using namespace KIGFX;
@ -73,9 +73,16 @@ static std::unique_ptr<ZOOM_CONTROLLER> GetZoomControllerForPlatform( bool aAcce
WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, EDA_DRAW_PANEL_GAL* aParentPanel ) : WX_VIEW_CONTROLS::WX_VIEW_CONTROLS( VIEW* aView, EDA_DRAW_PANEL_GAL* aParentPanel ) :
VIEW_CONTROLS( aView ), m_state( IDLE ), m_parentPanel( aParentPanel ), VIEW_CONTROLS( aView ),
m_scrollScale( 1.0, 1.0 ), m_cursorPos( 0, 0 ), m_updateCursor( true ), m_state( IDLE ),
m_infinitePanWorks( false ), m_gestureLastZoomFactor( 1.0 ) m_parentPanel( aParentPanel ),
m_scrollScale( 1.0, 1.0 ),
m_cursorPos( 0, 0 ),
m_updateCursor( true ),
m_metaPanning( false ),
m_metaPanStart( 0, 0 ),
m_infinitePanWorks( false ),
m_gestureLastZoomFactor( 1.0 )
{ {
LoadSettings(); LoadSettings();
@ -178,6 +185,7 @@ void WX_VIEW_CONTROLS::LoadSettings()
m_settings.m_dragRight = cfg->m_Input.drag_right; m_settings.m_dragRight = cfg->m_Input.drag_right;
m_settings.m_scrollReverseZoom = cfg->m_Input.reverse_scroll_zoom; m_settings.m_scrollReverseZoom = cfg->m_Input.reverse_scroll_zoom;
m_settings.m_scrollReversePanH = cfg->m_Input.reverse_scroll_pan_h; m_settings.m_scrollReversePanH = cfg->m_Input.reverse_scroll_pan_h;
m_settings.m_motionPanModifier = cfg->m_Input.motion_pan_modifier;
m_zoomController.reset(); m_zoomController.reset();
@ -238,6 +246,37 @@ void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
} }
} }
if( m_settings.m_motionPanModifier != WXK_NONE
&& wxGetKeyState( static_cast<wxKeyCode>( m_settings.m_motionPanModifier ) ) )
{
if( !m_metaPanning )
{
m_metaPanning = true;
m_metaPanStart = mousePos;
aEvent.StopPropagation();
}
else
{
VECTOR2D d = m_metaPanStart - mousePos;
m_metaPanStart = mousePos;
VECTOR2D delta = m_view->ToWorld( d, false );
m_view->SetCenter( m_view->GetCenter() + delta );
aEvent.StopPropagation();
}
if( m_updateCursor )
m_cursorPos = GetClampedCoords( m_view->ToWorld( mousePos ) );
else
m_updateCursor = true;
aEvent.Skip();
return;
}
else
{
m_metaPanning = false;
}
if( m_state != DRAG_PANNING && m_state != DRAG_ZOOMING ) if( m_state != DRAG_PANNING && m_state != DRAG_ZOOMING )
handleCursorCapture( x, y ); handleCursorCapture( x, y );
@ -249,15 +288,15 @@ void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
if( m_state == DRAG_PANNING ) if( m_state == DRAG_PANNING )
{ {
static bool justWarped = false; static bool justWarped = false;
int warpX = 0; int warpX = 0;
int warpY = 0; int warpY = 0;
wxSize parentSize = m_parentPanel->GetClientSize(); wxSize parentSize = m_parentPanel->GetClientSize();
if( x < 0 ) if( x < 0 )
{ {
warpX = parentSize.x; warpX = parentSize.x;
} }
else if(x >= parentSize.x ) else if( x >= parentSize.x )
{ {
warpX = -parentSize.x; warpX = -parentSize.x;
} }
@ -284,8 +323,7 @@ void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
{ {
if( !justWarped ) if( !justWarped )
{ {
if( m_infinitePanWorks if( m_infinitePanWorks && KIPLATFORM::UI::WarpPointer( m_parentPanel, x + warpX, y + warpY ) )
&& KIPLATFORM::UI::WarpPointer( m_parentPanel, x + warpX, y + warpY ) )
{ {
m_dragStartPoint += VECTOR2D( warpX, warpY ); m_dragStartPoint += VECTOR2D( warpX, warpY );
justWarped = true; justWarped = true;
@ -304,8 +342,8 @@ void WX_VIEW_CONTROLS::onMotion( wxMouseEvent& aEvent )
else if( m_state == DRAG_ZOOMING ) else if( m_state == DRAG_ZOOMING )
{ {
static bool justWarped = false; static bool justWarped = false;
int warpY = 0; int warpY = 0;
wxSize parentSize = m_parentPanel->GetClientSize(); wxSize parentSize = m_parentPanel->GetClientSize();
if( y < 0 ) if( y < 0 )
{ {
@ -394,8 +432,7 @@ void WX_VIEW_CONTROLS::onWheel( wxMouseEvent& aEvent )
// as vertical scroll events and confuse the user. // as vertical scroll events and confuse the user.
if( modifiers == m_settings.m_scrollModifierZoom && axis == wxMOUSE_WHEEL_VERTICAL ) if( modifiers == m_settings.m_scrollModifierZoom && axis == wxMOUSE_WHEEL_VERTICAL )
{ {
const int rotation = const int rotation = aEvent.GetWheelRotation() * ( m_settings.m_scrollReverseZoom ? -1 : 1 );
aEvent.GetWheelRotation() * ( m_settings.m_scrollReverseZoom ? -1 : 1 );
const double zoomScale = m_zoomController->GetScaleForRotation( rotation ); const double zoomScale = m_zoomController->GetScaleForRotation( rotation );
if( IsCursorWarpingEnabled() ) if( IsCursorWarpingEnabled() )
@ -477,8 +514,8 @@ void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent )
{ {
case IDLE: case IDLE:
case AUTO_PANNING: case AUTO_PANNING:
if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::PAN ) || if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::PAN )
( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::PAN ) ) || ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::PAN ) )
{ {
m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() ); m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
setState( DRAG_PANNING ); setState( DRAG_PANNING );
@ -489,10 +526,10 @@ void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent )
m_parentPanel->CaptureMouse(); m_parentPanel->CaptureMouse();
#endif #endif
} }
else if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::ZOOM ) || else if( ( aEvent.MiddleDown() && m_settings.m_dragMiddle == MOUSE_DRAG_ACTION::ZOOM )
( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::ZOOM ) ) || ( aEvent.RightDown() && m_settings.m_dragRight == MOUSE_DRAG_ACTION::ZOOM ) )
{ {
m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() ); m_dragStartPoint = VECTOR2D( aEvent.GetX(), aEvent.GetY() );
m_zoomStartPoint = m_dragStartPoint; m_zoomStartPoint = m_dragStartPoint;
setState( DRAG_ZOOMING ); setState( DRAG_ZOOMING );
@ -503,7 +540,7 @@ void WX_VIEW_CONTROLS::onButton( wxMouseEvent& aEvent )
} }
if( aEvent.LeftUp() ) if( aEvent.LeftUp() )
setState( IDLE ); // Stop autopanning when user release left mouse button setState( IDLE ); // Stop autopanning when user release left mouse button
break; break;
@ -619,7 +656,7 @@ void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
// For a small mouse cursor dist to area, just use the distance. // For a small mouse cursor dist to area, just use the distance.
// But for a dist > borderSize / 2, use an accelerated pan value // But for a dist > borderSize / 2, use an accelerated pan value
if( dir.EuclideanNorm() >= borderSize ) // far from area limits if( dir.EuclideanNorm() >= borderSize ) // far from area limits
dir = dir.Resize( borderSize * accel ); dir = dir.Resize( borderSize * accel );
else if( dir.EuclideanNorm() > borderSize / 2 ) // Near from area limits else if( dir.EuclideanNorm() > borderSize / 2 ) // Near from area limits
dir = dir.Resize( borderSize ); dir = dir.Resize( borderSize );
@ -633,10 +670,9 @@ void WX_VIEW_CONTROLS::onTimer( wxTimerEvent& aEvent )
} }
break; break;
case IDLE: // Just remove unnecessary warnings case IDLE: // Just remove unnecessary warnings
case DRAG_PANNING: case DRAG_PANNING:
case DRAG_ZOOMING: case DRAG_ZOOMING: break;
break;
} }
} }
@ -685,12 +721,11 @@ void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
if( type == wxEVT_SCROLLWIN_THUMBTRACK ) if( type == wxEVT_SCROLLWIN_THUMBTRACK )
{ {
auto center = m_view->GetCenter(); auto center = m_view->GetCenter();
const auto& boundary = m_view->GetBoundary(); const auto& boundary = m_view->GetBoundary();
// Flip scroll direction in flipped view // Flip scroll direction in flipped view
const double xstart = ( m_view->IsMirroredX() ? const double xstart = ( m_view->IsMirroredX() ? boundary.GetRight() : boundary.GetLeft() );
boundary.GetRight() : boundary.GetLeft() );
const double xdelta = ( m_view->IsMirroredX() ? -1 : 1 ); const double xdelta = ( m_view->IsMirroredX() ? -1 : 1 );
if( dir == wxHORIZONTAL ) if( dir == wxHORIZONTAL )
@ -700,9 +735,7 @@ void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
m_view->SetCenter( center ); m_view->SetCenter( center );
} }
else if( type == wxEVT_SCROLLWIN_THUMBRELEASE || else if( type == wxEVT_SCROLLWIN_THUMBRELEASE || type == wxEVT_SCROLLWIN_TOP || type == wxEVT_SCROLLWIN_BOTTOM )
type == wxEVT_SCROLLWIN_TOP ||
type == wxEVT_SCROLLWIN_BOTTOM )
{ {
// Do nothing on thumb release, we don't care about it. // Do nothing on thumb release, we don't care about it.
// We don't have a concept of top or bottom in our viewport, so ignore those events. // We don't have a concept of top or bottom in our viewport, so ignore those events.
@ -737,7 +770,7 @@ void WX_VIEW_CONTROLS::onScroll( wxScrollWinEvent& aEvent )
double scrollX = 0.0; double scrollX = 0.0;
double scrollY = 0.0; double scrollY = 0.0;
if ( dir == wxHORIZONTAL ) if( dir == wxHORIZONTAL )
scrollX = -scroll.x; scrollX = -scroll.x;
else else
scrollY = -scroll.y; scrollY = -scroll.y;
@ -769,8 +802,7 @@ void WX_VIEW_CONTROLS::CaptureCursor( bool aEnabled )
// Calling it without calling ReleaseMouse() is not accepted by wxWidgets (MSW specific) // Calling it without calling ReleaseMouse() is not accepted by wxWidgets (MSW specific)
m_parentPanel->m_MouseCapturedLost = false; m_parentPanel->m_MouseCapturedLost = false;
} }
else if( !aEnabled && m_parentPanel->HasCapture() else if( !aEnabled && m_parentPanel->HasCapture() && m_state != DRAG_PANNING && m_state != DRAG_ZOOMING )
&& m_state != DRAG_PANNING && m_state != DRAG_ZOOMING )
{ {
m_parentPanel->ReleaseMouse(); m_parentPanel->ReleaseMouse();
@ -793,12 +825,14 @@ void WX_VIEW_CONTROLS::CancelDrag()
m_parentPanel->ReleaseMouse(); m_parentPanel->ReleaseMouse();
#endif #endif
} }
m_metaPanning = false;
} }
VECTOR2D WX_VIEW_CONTROLS::GetMousePosition( bool aWorldCoordinates ) const VECTOR2D WX_VIEW_CONTROLS::GetMousePosition( bool aWorldCoordinates ) const
{ {
wxPoint msp = getMouseScreenPosition(); wxPoint msp = getMouseScreenPosition();
VECTOR2D screenPos( msp.x, msp.y ); VECTOR2D screenPos( msp.x, msp.y );
return aWorldCoordinates ? GetClampedCoords( m_view->ToWorld( screenPos ) ) : screenPos; return aWorldCoordinates ? GetClampedCoords( m_view->ToWorld( screenPos ) ) : screenPos;
@ -833,8 +867,8 @@ VECTOR2D WX_VIEW_CONTROLS::GetCursorPosition( bool aEnableSnapping ) const
} }
void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpView, void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpView, bool aTriggeredByArrows,
bool aTriggeredByArrows, long aArrowCommand ) long aArrowCommand )
{ {
m_updateCursor = false; m_updateCursor = false;
@ -860,8 +894,7 @@ void WX_VIEW_CONTROLS::SetCursorPosition( const VECTOR2D& aPosition, bool aWarpV
} }
void WX_VIEW_CONTROLS::SetCrossHairCursorPosition( const VECTOR2D& aPosition, void WX_VIEW_CONTROLS::SetCrossHairCursorPosition( const VECTOR2D& aPosition, bool aWarpView = true )
bool aWarpView = true )
{ {
m_updateCursor = false; m_updateCursor = false;
@ -878,8 +911,7 @@ void WX_VIEW_CONTROLS::SetCrossHairCursorPosition( const VECTOR2D& aPosition,
} }
void WX_VIEW_CONTROLS::WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCoordinates, void WX_VIEW_CONTROLS::WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCoordinates, bool aWarpView )
bool aWarpView )
{ {
if( aWorldCoordinates ) if( aWorldCoordinates )
{ {
@ -915,7 +947,7 @@ void WX_VIEW_CONTROLS::WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCo
void WX_VIEW_CONTROLS::CenterOnCursor() void WX_VIEW_CONTROLS::CenterOnCursor()
{ {
const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize(); const VECTOR2I& screenSize = m_view->GetGAL()->GetScreenPixelSize();
VECTOR2D screenCenter( screenSize / 2 ); VECTOR2D screenCenter( screenSize / 2 );
if( GetMousePosition( false ) != screenCenter ) if( GetMousePosition( false ) != screenCenter )
{ {
@ -937,8 +969,7 @@ void WX_VIEW_CONTROLS::PinCursorInsideNonAutoscrollArea( bool aWarpMouseCursor )
border += 2; border += 2;
VECTOR2D topLeft( border, border ); VECTOR2D topLeft( border, border );
VECTOR2D botRight( m_view->GetScreenPixelSize().x - border, VECTOR2D botRight( m_view->GetScreenPixelSize().x - border, m_view->GetScreenPixelSize().y - border );
m_view->GetScreenPixelSize().y - border );
topLeft = m_view->ToWorld( topLeft ); topLeft = m_view->ToWorld( topLeft );
botRight = m_view->ToWorld( botRight ); botRight = m_view->ToWorld( botRight );
@ -965,7 +996,7 @@ void WX_VIEW_CONTROLS::PinCursorInsideNonAutoscrollArea( bool aWarpMouseCursor )
bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent ) bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent )
{ {
VECTOR2I p( aEvent.GetX(), aEvent.GetY() ); VECTOR2I p( aEvent.GetX(), aEvent.GetY() );
VECTOR2I pKey( m_view->ToScreen(m_settings.m_lastKeyboardCursorPosition ) ); VECTOR2I pKey( m_view->ToScreen( m_settings.m_lastKeyboardCursorPosition ) );
if( m_cursorWarped || ( m_settings.m_lastKeyboardCursorPositionValid && p == pKey ) ) if( m_cursorWarped || ( m_settings.m_lastKeyboardCursorPositionValid && p == pKey ) )
{ {
@ -1028,8 +1059,7 @@ bool WX_VIEW_CONTROLS::handleAutoPanning( const wxMouseEvent& aEvent )
return false; return false;
case DRAG_PANNING: case DRAG_PANNING:
case DRAG_ZOOMING: case DRAG_ZOOMING: return false;
return false;
} }
wxCHECK_MSG( false, false, wxT( "This line should never be reached" ) ); wxCHECK_MSG( false, false, wxT( "This line should never be reached" ) );
@ -1042,7 +1072,7 @@ void WX_VIEW_CONTROLS::handleCursorCapture( int x, int y )
{ {
if( m_settings.m_cursorCaptured ) if( m_settings.m_cursorCaptured )
{ {
bool warp = false; bool warp = false;
wxSize parentSize = m_parentPanel->GetClientSize(); wxSize parentSize = m_parentPanel->GetClientSize();
if( x < 0 ) if( x < 0 )
@ -1077,7 +1107,7 @@ void WX_VIEW_CONTROLS::refreshMouse( bool aSetModifiers )
{ {
// Notify tools that the cursor position has changed in the world coordinates // Notify tools that the cursor position has changed in the world coordinates
wxMouseEvent moveEvent( EVT_REFRESH_MOUSE ); wxMouseEvent moveEvent( EVT_REFRESH_MOUSE );
wxPoint msp = getMouseScreenPosition(); wxPoint msp = getMouseScreenPosition();
moveEvent.SetX( msp.x ); moveEvent.SetX( msp.x );
moveEvent.SetY( msp.y ); moveEvent.SetY( msp.y );
@ -1104,20 +1134,18 @@ wxPoint WX_VIEW_CONTROLS::getMouseScreenPosition() const
void WX_VIEW_CONTROLS::UpdateScrollbars() void WX_VIEW_CONTROLS::UpdateScrollbars()
{ {
const BOX2D viewport = m_view->GetViewport(); const BOX2D viewport = m_view->GetViewport();
const BOX2D& boundary = m_view->GetBoundary(); const BOX2D& boundary = m_view->GetBoundary();
m_scrollScale.x = 2e3 / viewport.GetWidth(); // TODO it does not have to be updated so often m_scrollScale.x = 2e3 / viewport.GetWidth(); // TODO it does not have to be updated so often
m_scrollScale.y = 2e3 / viewport.GetHeight(); m_scrollScale.y = 2e3 / viewport.GetHeight();
VECTOR2I newScroll( ( viewport.Centre().x - boundary.GetLeft() ) * m_scrollScale.x, VECTOR2I newScroll( ( viewport.Centre().x - boundary.GetLeft() ) * m_scrollScale.x,
( viewport.Centre().y - boundary.GetTop() ) * m_scrollScale.y ); ( viewport.Centre().y - boundary.GetTop() ) * m_scrollScale.y );
// We add the width of the scroll bar thumb to the range because the scroll range is given by // We add the width of the scroll bar thumb to the range because the scroll range is given by
// the full bar while the position is given by the left/top position of the thumb // the full bar while the position is given by the left/top position of the thumb
VECTOR2I newRange( m_scrollScale.x * boundary.GetWidth() + VECTOR2I newRange( m_scrollScale.x * boundary.GetWidth() + m_parentPanel->GetScrollThumb( wxSB_HORIZONTAL ),
m_parentPanel->GetScrollThumb( wxSB_HORIZONTAL ), m_scrollScale.y * boundary.GetHeight() + m_parentPanel->GetScrollThumb( wxSB_VERTICAL ) );
m_scrollScale.y * boundary.GetHeight() +
m_parentPanel->GetScrollThumb( wxSB_VERTICAL ) );
// Flip scroll direction in flipped view // Flip scroll direction in flipped view
if( m_view->IsMirroredX() ) if( m_view->IsMirroredX() )
@ -1126,10 +1154,9 @@ void WX_VIEW_CONTROLS::UpdateScrollbars()
// Adjust scrollbars only if it is needed. Otherwise there are cases when canvas is continuously // Adjust scrollbars only if it is needed. Otherwise there are cases when canvas is continuously
// refreshed (Windows) // refreshed (Windows)
if( m_scrollPos != newScroll || newRange.x != m_parentPanel->GetScrollRange( wxSB_HORIZONTAL ) if( m_scrollPos != newScroll || newRange.x != m_parentPanel->GetScrollRange( wxSB_HORIZONTAL )
|| newRange.y != m_parentPanel->GetScrollRange( wxSB_VERTICAL ) ) || newRange.y != m_parentPanel->GetScrollRange( wxSB_VERTICAL ) )
{ {
m_parentPanel->SetScrollbars( 1, 1, newRange.x, newRange.y, newScroll.x, newScroll.y, m_parentPanel->SetScrollbars( 1, 1, newRange.x, newRange.y, newScroll.x, newScroll.y, true );
true );
m_scrollPos = newScroll; m_scrollPos = newScroll;
#if !defined( __APPLE__ ) && !defined( WIN32 ) #if !defined( __APPLE__ ) && !defined( WIN32 )

View File

@ -96,6 +96,8 @@ public:
int scroll_modifier_pan_h; int scroll_modifier_pan_h;
int scroll_modifier_pan_v; int scroll_modifier_pan_v;
int motion_pan_modifier;
MOUSE_DRAG_ACTION drag_left; MOUSE_DRAG_ACTION drag_left;
MOUSE_DRAG_ACTION drag_middle; MOUSE_DRAG_ACTION drag_middle;
MOUSE_DRAG_ACTION drag_right; MOUSE_DRAG_ACTION drag_right;

View File

@ -109,6 +109,9 @@ struct GAL_API VC_SETTINGS
/// What modifier key to enable vertical with the (vertical) scroll wheel. /// What modifier key to enable vertical with the (vertical) scroll wheel.
int m_scrollModifierPanV; int m_scrollModifierPanV;
/// What modifier key pans the view when the mouse moves with it held.
int m_motionPanModifier;
MOUSE_DRAG_ACTION m_dragLeft; MOUSE_DRAG_ACTION m_dragLeft;
MOUSE_DRAG_ACTION m_dragMiddle; MOUSE_DRAG_ACTION m_dragMiddle;
MOUSE_DRAG_ACTION m_dragRight; MOUSE_DRAG_ACTION m_dragRight;

View File

@ -89,15 +89,14 @@ public:
/// @copydoc VIEW_CONTROLS::GetRawCursorPosition() /// @copydoc VIEW_CONTROLS::GetRawCursorPosition()
VECTOR2D GetRawCursorPosition( bool aSnappingEnabled = true ) const override; VECTOR2D GetRawCursorPosition( bool aSnappingEnabled = true ) const override;
void SetCursorPosition( const VECTOR2D& aPosition, bool warpView, void SetCursorPosition( const VECTOR2D& aPosition, bool warpView, bool aTriggeredByArrows,
bool aTriggeredByArrows, long aArrowCommand ) override; long aArrowCommand ) override;
/// @copydoc VIEW_CONTROLS::SetCrossHairCursorPosition() /// @copydoc VIEW_CONTROLS::SetCrossHairCursorPosition()
void SetCrossHairCursorPosition( const VECTOR2D& aPosition, bool aWarpView ) override; void SetCrossHairCursorPosition( const VECTOR2D& aPosition, bool aWarpView ) override;
/// @copydoc VIEW_CONTROLS::CursorWarp() /// @copydoc VIEW_CONTROLS::CursorWarp()
void WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCoordinates = false, void WarpMouseCursor( const VECTOR2D& aPosition, bool aWorldCoordinates = false, bool aWarpView = false ) override;
bool aWarpView = false ) override;
/// @copydoc VIEW_CONTROLS::CenterOnCursor() /// @copydoc VIEW_CONTROLS::CenterOnCursor()
void CenterOnCursor() override; void CenterOnCursor() override;
@ -108,8 +107,7 @@ public:
/// End any mouse drag action still in progress. /// End any mouse drag action still in progress.
void CancelDrag(); void CancelDrag();
void ForceCursorPosition( bool aEnabled, void ForceCursorPosition( bool aEnabled, const VECTOR2D& aPosition = VECTOR2D( 0, 0 ) ) override;
const VECTOR2D& aPosition = VECTOR2D( 0, 0 ) ) override;
/// Applies VIEW_CONTROLS settings from the program #COMMON_SETTINGS. /// Applies VIEW_CONTROLS settings from the program #COMMON_SETTINGS.
void LoadSettings() override; void LoadSettings() override;
@ -124,10 +122,10 @@ private:
/// Possible states for WX_VIEW_CONTROLS. /// Possible states for WX_VIEW_CONTROLS.
enum STATE enum STATE
{ {
IDLE = 1, ///< Nothing is happening. IDLE = 1, ///< Nothing is happening.
DRAG_PANNING, ///< Panning with mouse button pressed. DRAG_PANNING, ///< Panning with mouse button pressed.
AUTO_PANNING, ///< Panning on approaching borders of the frame. AUTO_PANNING, ///< Panning on approaching borders of the frame.
DRAG_ZOOMING, ///< Zooming with mouse button pressed. DRAG_ZOOMING, ///< Zooming with mouse button pressed.
}; };
/** /**
@ -162,7 +160,7 @@ private:
* @param aSetModifiers If false, don't change the modifiers (they were set using the * @param aSetModifiers If false, don't change the modifiers (they were set using the
* keyboard motion). * keyboard motion).
*/ */
void refreshMouse( bool aSetModifiers); void refreshMouse( bool aSetModifiers );
/** /**
* Get the cursor position in the screen coordinates. * Get the cursor position in the screen coordinates.
@ -170,34 +168,40 @@ private:
wxPoint getMouseScreenPosition() const; wxPoint getMouseScreenPosition() const;
/// Current state of VIEW_CONTROLS. /// Current state of VIEW_CONTROLS.
STATE m_state; STATE m_state;
/// Panel that is affected by VIEW_CONTROLS. /// Panel that is affected by VIEW_CONTROLS.
EDA_DRAW_PANEL_GAL* m_parentPanel; EDA_DRAW_PANEL_GAL* m_parentPanel;
/// Store information about point where dragging has started. /// Store information about point where dragging has started.
VECTOR2D m_dragStartPoint; VECTOR2D m_dragStartPoint;
/// Current direction of panning (only autopanning mode). /// Current direction of panning (only autopanning mode).
VECTOR2D m_panDirection; VECTOR2D m_panDirection;
/// Timer responsible for handling autopanning. /// Timer responsible for handling autopanning.
wxTimer m_panTimer; wxTimer m_panTimer;
/// Ratio used for scaling world coordinates to scrollbar position. /// Ratio used for scaling world coordinates to scrollbar position.
VECTOR2D m_scrollScale; VECTOR2D m_scrollScale;
/// Current scrollbar position. /// Current scrollbar position.
VECTOR2I m_scrollPos; VECTOR2I m_scrollPos;
/// The mouse position when a drag zoom started. /// The mouse position when a drag zoom started.
VECTOR2D m_zoomStartPoint; VECTOR2D m_zoomStartPoint;
/// Current cursor position (world coordinates). /// Current cursor position (world coordinates).
VECTOR2D m_cursorPos; VECTOR2D m_cursorPos;
/// Flag deciding whether the cursor position should be calculated using the mouse position. /// Flag deciding whether the cursor position should be calculated using the mouse position.
bool m_updateCursor; bool m_updateCursor;
/// True if we are panning via the meta key.
bool m_metaPanning;
/// Last mouse position when panning via the meta key.
VECTOR2D m_metaPanStart;
/// Flag to indicate if infinite panning works on this platform. /// Flag to indicate if infinite panning works on this platform.
bool m_infinitePanWorks; bool m_infinitePanWorks;