Fix cursor freezes in GAL

Launching right click context menu overrides the cursor position, so all
actions executed by the tools will be performed in the right click
position. It created an issue, as the overridden cursor settings were
saved into wrong context if there was another tool activated in the
meantime.

Current implementation saves cursor settings for all tools and restores
them once the right click context menu disappears.

Fixes: lp:1745981
* https://bugs.launchpad.net/kicad/+bug/1745981

Fixes: lp:1746507
* https://bugs.launchpad.net/kicad/+bug/1746507
This commit is contained in:
Maciej Suminski 2018-02-02 16:20:14 +01:00
parent b497673b89
commit 95864780e2
4 changed files with 79 additions and 31 deletions

View File

@ -195,7 +195,9 @@ TOOL_MANAGER::TOOL_MANAGER() :
m_viewControls( NULL ),
m_editFrame( NULL ),
m_passEvent( false ),
m_menuActive( false )
m_menuActive( false ),
m_menuOwner( -1 ),
m_activeState( nullptr )
{
m_actionMgr = new ACTION_MANAGER( this );
}
@ -369,6 +371,7 @@ bool TOOL_MANAGER::runTool( TOOL_BASE* aTool )
return false;
}
m_activeState = m_toolIdIndex[id];
aTool->Reset( TOOL_INTERACTIVE::RUN );
// Add the tool on the front of the processing queue (it gets events first)
@ -414,6 +417,7 @@ void TOOL_MANAGER::ResetTools( TOOL_BASE::RESET_REASON aReason )
for( auto& state : m_toolState )
{
m_activeState = state.second;
TOOL_BASE* tool = state.first;
tool->Reset( aReason );
@ -430,6 +434,7 @@ void TOOL_MANAGER::InitTools()
TOOL_BASE* tool = it->first;
TOOL_STATE* state = it->second;
++it; // keep the iterator valid if the element is going to be erased
m_activeState = state;
if( !tool->Init() )
{
@ -541,6 +546,7 @@ void TOOL_MANAGER::dispatchInternal( const TOOL_EVENT& aEvent )
if( st->cofunc )
{
applyViewControls( st );
m_activeState = st;
bool end = !st->cofunc->Resume();
saveViewControls( st );
@ -637,9 +643,6 @@ bool TOOL_MANAGER::dispatchActivation( const TOOL_EVENT& aEvent )
void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
{
// Store the current tool ID to decide whether to restore the cursor position
TOOL_ID activeTool = GetCurrentToolId();
for( TOOL_ID toolId : m_activeTools )
{
TOOL_STATE* st = m_toolIdIndex[toolId];
@ -661,14 +664,23 @@ void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
if( st->contextMenuTrigger == CMENU_NOW )
st->contextMenuTrigger = CMENU_OFF;
VECTOR2D cursor = m_viewControls->GetCursorPosition();
// Temporarily store the cursor position, so the tools could execute actions
// Store the cursor position, so the tools could execute actions
// using the point where the user has invoked a context menu
if( m_viewControls->ForcedCursorPosition() )
m_origCursor = cursor;
m_menuCursor = m_viewControls->GetCursorPosition();
m_viewControls->ForceCursorPosition( true, cursor );
// Save all tools cursor settings, as they will be overridden
for( auto idState : m_toolIdIndex )
{
TOOL_STATE* s = idState.second;
const auto& vc = s->vcSettings;
if( vc.m_forceCursorPosition )
m_cursorSettings[idState.first] = vc.m_forcedPosition;
else
m_cursorSettings[idState.first] = NULLOPT;
}
m_viewControls->ForceCursorPosition( true, m_menuCursor );
// Display a copy of menu
std::unique_ptr<CONTEXT_MENU> menu( m->Clone() );
@ -681,13 +693,11 @@ void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
auto frame = dynamic_cast<wxFrame*>( m_editFrame );
if( frame )
{
frame->PopupMenu( menu.get() );
}
// Warp the cursor as long as the menu wasn't clicked out of
if( menu->GetSelected() >= 0 )
m_viewControls->WarpCursor( cursor, true, false );
m_viewControls->WarpCursor( m_menuCursor, true, false );
// Otherwise notify the tool of a cancelled menu
else
{
@ -704,14 +714,21 @@ void TOOL_MANAGER::dispatchContextMenu( const TOOL_EVENT& aEvent )
m_menuActive = false;
m_menuOwner = -1;
// Restore the cursor settings if the tool is still active
if( activeTool == GetCurrentToolId() )
// Restore cursor settings
for( auto cursorSetting : m_cursorSettings )
{
m_viewControls->ForceCursorPosition( (bool) m_origCursor,
m_origCursor ? *m_origCursor : VECTOR2D( 0, 0 ) );
auto it = m_toolIdIndex.find( cursorSetting.first );
wxASSERT( it != m_toolIdIndex.end() );
if( it == m_toolIdIndex.end() )
continue;
KIGFX::VC_SETTINGS& vc = it->second->vcSettings;
vc.m_forceCursorPosition = (bool) cursorSetting.second;
vc.m_forcedPosition = cursorSetting.second ? *cursorSetting.second : VECTOR2D( 0, 0 );
}
m_origCursor = NULLOPT;
m_cursorSettings.clear();
break;
}
}
@ -721,6 +738,9 @@ TOOL_MANAGER::ID_LIST::iterator TOOL_MANAGER::finishTool( TOOL_STATE* aState )
{
auto it = std::find( m_activeTools.begin(), m_activeTools.end(), aState->theTool->GetId() );
if( aState == m_activeState )
m_activeState = nullptr;
if( !aState->Pop() )
{
// Deactivate the tool if there are no other contexts saved on the stack
@ -843,26 +863,49 @@ void TOOL_MANAGER::saveViewControls( TOOL_STATE* aState )
{
aState->vcSettings = m_viewControls->GetSettings();
// If context menu has overridden the cursor position, restore the original position
// (see dispatchContextMenu())
if( m_menuActive )
{
if( m_origCursor )
// Context menu is active, so the cursor settings are overridden (see dispatchContextMenu())
auto it = m_cursorSettings.find( aState->theTool->GetId() );
if( it != m_cursorSettings.end() )
{
const KIGFX::VC_SETTINGS& curr = m_viewControls->GetSettings();
// Tool has overridden the cursor position, so store the new settings
if( !curr.m_forceCursorPosition || curr.m_forcedPosition != m_menuCursor )
{
if( !curr.m_forceCursorPosition )
it->second = NULLOPT;
else
it->second = curr.m_forcedPosition;
}
else
{
OPT<VECTOR2D> cursor = it->second;
if( cursor )
{
aState->vcSettings.m_forceCursorPosition = true;
aState->vcSettings.m_forcedPosition = *m_origCursor;
aState->vcSettings.m_forcedPosition = *cursor;
}
else
{
aState->vcSettings.m_forceCursorPosition = false;
}
}
}
}
}
void TOOL_MANAGER::applyViewControls( TOOL_STATE* aState )
{
m_viewControls->ApplySettings( aState->vcSettings );
// Override the cursor position if menu is active
if( m_menuActive )
m_viewControls->ForceCursorPosition( true, m_menuCursor );
}
@ -887,6 +930,7 @@ bool TOOL_MANAGER::processEvent( const TOOL_EVENT& aEvent )
return false;
}
bool TOOL_MANAGER::IsToolActive( TOOL_ID aId ) const
{
auto it = m_toolIdIndex.find( aId );

View File

@ -502,7 +502,7 @@ private:
ACTION_MANAGER* m_actionMgr;
/// Original cursor position, if overridden by the context menu handler
OPT<VECTOR2D> m_origCursor;
std::map<TOOL_ID, OPT<VECTOR2D>> m_cursorSettings;
EDA_ITEM* m_model;
KIGFX::VIEW* m_view;
@ -515,11 +515,17 @@ private:
/// Flag saying if the currently processed event should be passed to other tools.
bool m_passEvent;
/// Right click context menu position.
VECTOR2D m_menuCursor;
/// Flag indicating whether a context menu is currently displayed.
bool m_menuActive;
/// Tool currently displaying a popup menu. It is negative when there is no menu displayed.
TOOL_ID m_menuOwner;
/// Pointer to the state object corresponding to the currently executed tool.
TOOL_STATE* m_activeState;
};
#endif

View File

@ -1120,8 +1120,6 @@ int ROUTER_TOOL::InlineDrag( const TOOL_EVENT& aEvent )
if( m_router->RoutingInProgress() )
m_router->StopRouting();
controls()->SetAutoPan( false );
controls()->ShowCursor( false );
frame()->UndoRedoBlock( false );
return 0;

View File

@ -288,7 +288,7 @@ int POINT_EDITOR::OnSelectionChange( const TOOL_EVENT& aEvent )
return 0;
view->Add( m_editPoints.get() );
m_editedPoint = NULL;
setEditedPoint( nullptr );
bool modified = false;
bool revert = false;