Move selection disambiguation to long-click

This standardizes selection tools in SELECTION_TOOL class to be able to
handle a long-click as asking for a non-heuristic disambiguation menu.

Fixes https://gitlab.com/kicad/code/kicad/issues/8490
This commit is contained in:
Seth Hillbrand 2021-09-05 13:37:52 -07:00
parent 092cdc0bb5
commit 240c3b75c4
16 changed files with 352 additions and 265 deletions

View File

@ -437,6 +437,7 @@ set( COMMON_SRCS
tool/grid_menu.cpp
tool/picker_tool.cpp
tool/selection.cpp
tool/selection_tool.cpp
tool/selection_conditions.cpp
tool/tool_action.cpp
tool/tool_base.cpp

View File

@ -44,19 +44,16 @@ static PSEUDO_ACTION* g_gesturePseudoActions[] = {
new PSEUDO_ACTION( _( "Pan Up/Down" ), MD_SHIFT + PSEUDO_WXK_WHEEL ),
new PSEUDO_ACTION( _( "Finish Drawing" ), PSEUDO_WXK_DBLCLICK ),
#ifdef __WXOSX_MAC__
new PSEUDO_ACTION( _( "Show Clarify Selection Menu" ), MD_ALT + PSEUDO_WXK_CLICK ),
new PSEUDO_ACTION( _( "Add to Selection" ), MD_SHIFT + PSEUDO_WXK_CLICK ),
new PSEUDO_ACTION( _( "Toggle Selection State" ), MD_CTRL + PSEUDO_WXK_CLICK ),
new PSEUDO_ACTION( _( "Remove from Selection" ), MD_SHIFT + MD_CTRL + PSEUDO_WXK_CLICK ),
#else
new PSEUDO_ACTION( _( "Show Clarify Selection Menu" ), MD_CTRL + PSEUDO_WXK_CLICK ),
new PSEUDO_ACTION( _( "Add to Selection" ), MD_SHIFT + PSEUDO_WXK_CLICK ),
new PSEUDO_ACTION( _( "Toggle Selection State" ), MD_ALT + PSEUDO_WXK_CLICK ),
new PSEUDO_ACTION( _( "Remove from Selection" ), MD_SHIFT + MD_ALT + PSEUDO_WXK_CLICK ),
#endif
new PSEUDO_ACTION( _( "Ignore Grid Snaps" ), MD_CTRL ),
new PSEUDO_ACTION( _( "Ignore Other Snaps" ), MD_SHIFT ),
new PSEUDO_ACTION( _( "Ignore H/V/45 Constraints" ), MD_SHIFT )
};
static PSEUDO_ACTION* g_standardPlatformCommands[] = {

View File

@ -665,3 +665,5 @@ const TOOL_EVENT EVENTS::SelectedItemsMoved( TC_MESSAGE, TA_ACTION, "common.Inte
const TOOL_EVENT EVENTS::InhibitSelectionEditing( TC_MESSAGE, TA_ACTION, "common.Interactive.inhibit" );
const TOOL_EVENT EVENTS::UninhibitSelectionEditing( TC_MESSAGE, TA_ACTION, "common.Interactive.uninhibit" );
const TOOL_EVENT EVENTS::DisambiguatePoint( TC_MESSAGE, TA_ACTION, "common.Interactive.disambiguate" );

View File

@ -0,0 +1,58 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 KiCad Developers, see CHANGELOG.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <tool/selection_tool.h>
SELECTION_TOOL::SELECTION_TOOL() :
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false ),
m_highlight_modifier( false ),
m_drag_additive( false ),
m_drag_subtractive( false )
{
}
void SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
{
// Set the configuration of m_additive, m_subtractive, m_exclusive_or
// from the state of modifier keys SHIFT, CTRL, ALT
// on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
// ALT key cannot be used on MSW because of a conflict with the system menu
// for this reason, we do not use the ALT key modifier.
// Drag is more forgiving and allows either Ctrl+drag or Shift+Drag to add to the selection
m_subtractive = aCtrlState && aShiftState && !aAltState;
m_additive = !aCtrlState && aShiftState && !aAltState;
m_exclusive_or = false;
m_highlight_modifier = aCtrlState && !aShiftState && !aAltState;
m_drag_additive = ( aCtrlState || aShiftState ) && ! aAltState;
m_drag_subtractive = aCtrlState && aShiftState && !aAltState;
}

View File

@ -107,11 +107,6 @@ SELECTION_CONDITION EE_CONDITIONS::SingleMultiUnitSymbol = []( const SELECTION&
EE_SELECTION_TOOL::EE_SELECTION_TOOL() :
TOOL_INTERACTIVE( "eeschema.InteractiveSelection" ),
m_frame( nullptr ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false ),
m_nonModifiedCursor( KICURSOR::ARROW ),
m_isSymbolEditor( false ),
m_isSymbolViewer( false ),
@ -228,6 +223,9 @@ bool EE_SELECTION_TOOL::Init()
menu.AddSeparator( 1000 );
m_frame->AddStandardSubMenus( m_menu );
m_disambiguateTimer.SetOwner( this );
Connect( wxEVT_TIMER, wxTimerEventHandler( EE_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
return true;
}
@ -322,55 +320,6 @@ const KICAD_T movableSymbolAliasItems[] =
};
void EE_SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
{
// Set the configuration of m_additive, m_subtractive, m_exclusive_or
// from the state of modifier keys SHIFT, CTRL, ALT and the OS
// on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
// Due to the fact ALT key modifier cannot be used freely on Windows and Linux,
// actions are different on OSX and others OS
// Especially, ALT key cannot be used to force showing the full selection choice
// context menu (the menu is immediately closed on Windows )
//
// No modifier = select items and deselect previous selection
// ALT (on OSX) = skip heuristic and show full selection choice
// ALT (on others) = exclusive OR of selected items (inverse selection)
//
// CTRL/CMD (on OSX) = exclusive OR of selected items (inverse selection)
// CTRL (on others) = skip heuristic and show full selection choice
//
// SHIFT = add selected items to the current selection
//
// CTRL/CMD+SHIFT (on OSX) = remove selected items to the current selection
// CTRL+SHIFT (on others) = unused (can be used for a new action)
//
// CTRL/CMT+ALT (on OSX) = unused (can be used for a new action)
// CTRL+ALT (on others) = do nothing (same as no modifier)
//
// SHIFT+ALT (on OSX) = do nothing (same as no modifier)
// SHIFT+ALT (on others) = remove selected items to the current selection
#ifdef __WXOSX_MAC__
m_subtractive = aCtrlState && aShiftState && !aAltState;
m_additive = aShiftState && !aCtrlState && !aAltState;
m_exclusive_or = aCtrlState && !aShiftState && !aAltState;
m_skip_heuristics = aAltState && !aShiftState && !aCtrlState;
#else
m_subtractive = aShiftState && !aCtrlState && aAltState;
m_additive = aShiftState && !aCtrlState && !aAltState;
m_exclusive_or = !aShiftState && !aCtrlState && aAltState;
// Is the user requesting that the selection list include all possible
// items without removing less likely selection candidates
// Cannot use the Alt key on windows or the disambiguation context menu is immediately
// dismissed rendering it useless.
m_skip_heuristics = aCtrlState && !aShiftState && !aAltState;
#endif
}
int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
{
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
@ -394,9 +343,26 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
MOUSE_DRAG_ACTION drag_action = m_frame->GetDragAction();
EE_GRID_HELPER grid( m_toolMgr );
// Single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
if( evt->IsMouseDown( BUT_LEFT ) )
{
// Avoid triggering when running under other tools
if( m_toolMgr->GetCurrentTool() == this )
m_disambiguateTimer.StartOnce( 500 );
}
// Single click? Select single object
else if( evt->IsClick( BUT_LEFT ) )
{
// If the timer has stopped, then we have already run the disambiguate routine
// and we don't want to register an extra click here
if( !m_disambiguateTimer.IsRunning() )
{
evt->SetPassEvent();
continue;
}
m_disambiguateTimer.Stop();
if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
schframe->FocusOnItem( nullptr );
@ -468,6 +434,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsClick( BUT_RIGHT ) )
{
m_disambiguateTimer.Stop();
// right click? if there is any object - show the context menu
bool selectionCancelled = false;
@ -529,6 +497,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsDblClick( BUT_MIDDLE ) )
{
m_disambiguateTimer.Stop();
// Middle double click? Do zoom to fit or zoom to objects
if( evt->Modifier( MD_CTRL ) ) // Is CTRL key down?
m_toolMgr->RunAction( ACTIONS::zoomFitObjects, true );
@ -537,6 +507,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsDrag( BUT_LEFT ) )
{
m_disambiguateTimer.Stop();
// Is another tool already moving a new object? Don't allow a drag start
if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
{
@ -589,6 +561,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->Category() == TC_COMMAND && evt->Action() == TA_CHOICE_MENU_CHOICE )
{
m_disambiguateTimer.Stop();
// context sub-menu selection? Handle unit selection or bus unfolding
if( evt->GetCommandId().get() >= ID_POPUP_SCH_SELECT_UNIT_CMP
&& evt->GetCommandId().get() <= ID_POPUP_SCH_SELECT_UNIT_SYM_MAX )
@ -609,6 +583,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsCancelInteractive() )
{
m_disambiguateTimer.Stop();
if( SCH_EDIT_FRAME* schframe = dynamic_cast<SCH_EDIT_FRAME*>( m_frame ) )
schframe->FocusOnItem( nullptr );
@ -738,6 +714,8 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
}
m_disambiguateTimer.Stop();
// Shutting down; clear the selection
m_selection.Clear();
@ -745,6 +723,25 @@ int EE_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
int EE_SELECTION_TOOL::disambiguateCursor( const TOOL_EVENT& aEvent )
{
VECTOR2I pos = m_toolMgr->GetMousePosition();
m_skip_heuristics = true;
SelectPoint( pos, EE_COLLECTOR::AllItems, nullptr, nullptr, false, m_additive, m_subtractive,
m_exclusive_or );
m_skip_heuristics = false;
return 0;
}
void EE_SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
{
m_toolMgr->ProcessEvent( EVENTS::DisambiguatePoint );
}
void EE_SELECTION_TOOL::OnIdle( wxIdleEvent& aEvent )
{
if( m_frame->ToolStackIsEmpty() && !m_multiple )
@ -846,10 +843,7 @@ bool EE_SELECTION_TOOL::selectPoint( EE_COLLECTOR& aCollector, EDA_ITEM** aItem,
// If still more than one item we're going to have to ask the user.
if( aCollector.GetCount() > 1 )
{
// Must call selectionMenu via RunAction() to avoid event-loop contention
m_toolMgr->RunAction( EE_ACTIONS::selectionMenu, true, &aCollector );
if( aCollector.m_MenuCancelled )
if( !doSelectionMenu( &aCollector ) || aCollector.m_MenuCancelled )
{
if( aSelectionCancelledFlag )
*aSelectionCancelledFlag = true;
@ -1196,15 +1190,15 @@ bool EE_SELECTION_TOOL::selectMultiple()
if( evt->IsDrag( BUT_LEFT ) )
{
if( !m_additive && !m_subtractive && !m_exclusive_or )
if( !m_drag_additive && !m_drag_subtractive )
ClearSelection();
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
area.SetExclusiveOr( m_exclusive_or );
area.SetAdditive( m_drag_additive );
area.SetSubtractive( m_drag_subtractive );
area.SetExclusiveOr( false );
view->SetVisible( &area, true );
view->Update( &area );
@ -1904,6 +1898,8 @@ void EE_SELECTION_TOOL::setTransitions()
Go( &EE_SELECTION_TOOL::SelectionMenu, EE_ACTIONS::selectionMenu.MakeEvent() );
Go( &EE_SELECTION_TOOL::SelectAll, EE_ACTIONS::selectAll.MakeEvent() );
Go( &EE_SELECTION_TOOL::disambiguateCursor, EVENTS::DisambiguatePoint );
}

View File

@ -25,7 +25,7 @@
#ifndef KICAD_SCH_SELECTION_TOOL_H
#define KICAD_SCH_SELECTION_TOOL_H
#include <tool/tool_interactive.h>
#include <tool/selection_tool.h>
#include <tool/action_menu.h>
#include <tool/tool_menu.h>
#include <tools/ee_selection.h>
@ -52,7 +52,7 @@ public:
};
class EE_SELECTION_TOOL : public TOOL_INTERACTIVE
class EE_SELECTION_TOOL : public SELECTION_TOOL, public TOOL_INTERACTIVE
{
public:
EE_SELECTION_TOOL();
@ -226,6 +226,18 @@ private:
*/
bool doSelectionMenu( EE_COLLECTOR* aItems );
/**
* Start the process to show our disambiguation menu once the user has kept
* the mouse down for the minimum time
* @param aEvent
*/
void onDisambiguationExpire( wxTimerEvent& aEvent );
/**
* Handle disambiguation actions including displaying the menu.
*/
int disambiguateCursor( const TOOL_EVENT& aEvent );
/**
* Take necessary action mark an item as selected.
*
@ -272,23 +284,10 @@ private:
void setTransitions() override;
private:
/**
* Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics
* from the state of modifier keys SHIFT, CTRL, ALT and depending on the OS
*/
void setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState );
SCH_BASE_FRAME* m_frame; // Pointer to the parent frame
EE_SELECTION m_selection; // Current state of selection
bool m_additive; // Items should be added to sel (instead of replacing)
bool m_subtractive; // Items should be removed from sel
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Show disambuguation menu for all items under the
// cursor rather than trying to narrow them down first
// using heuristics
KICURSOR m_nonModifiedCursor; // Cursor in the absence of shift/ctrl/alt
bool m_isSymbolEditor; // True when the symbol editor is the parent frame

View File

@ -114,10 +114,7 @@ private:
GERBVIEW_SELECTION_TOOL::GERBVIEW_SELECTION_TOOL() :
TOOL_INTERACTIVE( "gerbview.InteractiveSelection" ),
m_frame( nullptr ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false )
m_frame( nullptr )
{
m_preliminary = true;
}
@ -194,14 +191,9 @@ int GERBVIEW_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_frame->ToolStackIsEmpty() )
m_frame->GetCanvas()->SetCurrentCursor( KICURSOR::ARROW );
m_additive = m_subtractive = m_exclusive_or = false;
if( evt->Modifier( MD_SHIFT ) && evt->Modifier( MD_CTRL ) )
m_subtractive = true;
else if( evt->Modifier( MD_SHIFT ) )
m_additive = true;
else if( evt->Modifier( MD_CTRL ) )
m_exclusive_or = true;
// on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
evt->Modifier( MD_ALT ) );
// single click? Select single object
if( evt->IsClick( BUT_LEFT ) )

View File

@ -26,6 +26,7 @@
#include <tool/tool_interactive.h>
#include <tool/action_menu.h>
#include <tool/selection_conditions.h>
#include <tool/selection_tool.h>
#include <tool/tool_menu.h>
#include <tools/gerbview_selection.h>
#include <gerbview_frame.h>
@ -42,7 +43,7 @@ namespace KIGFX
/**
* Selection tool for GerbView, based on the one in Pcbnew
*/
class GERBVIEW_SELECTION_TOOL : public TOOL_INTERACTIVE
class GERBVIEW_SELECTION_TOOL : public SELECTION_TOOL, public TOOL_INTERACTIVE
{
public:
GERBVIEW_SELECTION_TOOL();
@ -150,9 +151,6 @@ private:
GERBVIEW_FRAME* m_frame; // Pointer to the parent frame.
GERBVIEW_SELECTION m_selection; // Current state of selection.
bool m_additive; // Items should be added to selection (instead of replacing)
bool m_subtractive; // Items should be removed from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_preliminary; // Determines if the selection is preliminary or final.
};

View File

@ -209,6 +209,9 @@ public:
///< Used to inform tools that the selection should temporarily be non-editable
const static TOOL_EVENT InhibitSelectionEditing;
const static TOOL_EVENT UninhibitSelectionEditing;
///< Used to inform tool that it should display the disambiguation menu
const static TOOL_EVENT DisambiguatePoint;
};
#endif // __ACTIONS_H

View File

@ -0,0 +1,60 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 KiCad Developers, see CHANGELOG.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, you may find one here:
* http://www.gnu.org/licenses/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#pragma once
#ifndef INCLUDE_TOOL_SELECTION_TOOL_H_
#define INCLUDE_TOOL_SELECTION_TOOL_H_
#include <tool/tool_interactive.h>
#include <wx/timer.h>
class SELECTION_TOOL : public wxEvtHandler
{
public:
SELECTION_TOOL();
~SELECTION_TOOL(){};
protected:
/**
* Set the configuration of m_additive, m_subtractive, m_exclusive_or, m_skip_heuristics
* from the state of modifier keys SHIFT, CTRL, ALT and depending on the OS
*/
void setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState );
bool m_additive; // Items should be added to sel (instead of replacing)
bool m_subtractive; // Items should be removed from sel
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Show disambuguation menu for all items under the
// cursor rather than trying to narrow them down first
// using heuristics
bool m_highlight_modifier;// select highlight net on left click
bool m_drag_additive; // Add multiple items to selection
bool m_drag_subtractive; // Remove multiple from selection
wxTimer m_disambiguateTimer; // Timer to show the disambiguate menu
};
#endif /* INCLUDE_TOOL_SELECTION_TOOL_H_ */

View File

@ -287,6 +287,11 @@ public:
return m_actions == TA_MOUSE_DRAG && ( m_mouseButtons & aButtonMask ) == m_mouseButtons;
}
bool IsMouseDown( int aButtonMask = BUT_ANY ) const
{
return m_actions == TA_MOUSE_DOWN && ( m_mouseButtons & aButtonMask ) == m_mouseButtons;
}
bool IsMouseUp( int aButtonMask = BUT_ANY ) const
{
return m_actions == TA_MOUSE_UP && ( m_mouseButtons & aButtonMask ) == m_mouseButtons;

View File

@ -50,12 +50,7 @@
PL_SELECTION_TOOL::PL_SELECTION_TOOL() :
TOOL_INTERACTIVE( "plEditor.InteractiveSelection" ),
m_frame( nullptr ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false )
m_frame( nullptr )
{
}
@ -75,6 +70,9 @@ bool PL_SELECTION_TOOL::Init()
menu.AddSeparator( 1000 );
m_frame->AddStandardSubMenus( m_menu );
m_disambiguateTimer.SetOwner( this );
Connect( wxEVT_TIMER, wxTimerEventHandler( PL_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
return true;
}
@ -107,79 +105,36 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
while( TOOL_EVENT* evt = Wait() )
{
// on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
// Due to the fact ALT key modifier cannot be used freely on Winows and Linux,
// actions are different on OSX and others OS
// Especially, ALT key cannot be used to force showing the full selection choice
// context menu (the menu is immediately closed on Windows )
//
// No modifier = select items and deselect previous selection
// ALT (on OSX) = skip heuristic and show full selection choice
// ALT (on others) = exclusive OR of selected items (inverse selection)
//
// CTRL/CMD (on OSX) = exclusive OR of selected items (inverse selection)
// CTRL (on others) = skip heuristic and show full selection choice
//
// SHIFT = add selected items to the current selection
//
// CTRL/CMD+SHIFT (on OSX) = remove selected items to the current selection
// CTRL+SHIFT (on others) = unused (can be used for a new action)
//
// CTRL/CMT+ALT (on OSX) = unused (can be used for a new action)
// CTRL+ALT (on others) = do nothing (same as no modifier)
//
// SHIFT+ALT (on OSX) = do nothing (same as no modifier)
// SHIFT+ALT (on others) = remove selected items to the current selection
#ifdef __WXOSX_MAC__
m_subtractive = evt->Modifier( MD_CTRL ) &&
evt->Modifier( MD_SHIFT ) &&
!evt->Modifier( MD_ALT );
m_additive = evt->Modifier( MD_SHIFT ) &&
!evt->Modifier( MD_CTRL ) &&
!evt->Modifier( MD_ALT );
m_exclusive_or = evt->Modifier( MD_CTRL ) &&
!evt->Modifier( MD_SHIFT ) &&
!evt->Modifier( MD_ALT );
m_skip_heuristics = evt->Modifier( MD_ALT ) &&
!evt->Modifier( MD_SHIFT ) &&
!evt->Modifier( MD_CTRL );
#else
m_subtractive = evt->Modifier( MD_SHIFT )
&& !evt->Modifier( MD_CTRL )
&& evt->Modifier( MD_ALT );
m_additive = evt->Modifier( MD_SHIFT )
&& !evt->Modifier( MD_CTRL )
&& !evt->Modifier( MD_ALT );
m_exclusive_or = !evt->Modifier( MD_SHIFT )
&& !evt->Modifier( MD_CTRL )
&& evt->Modifier( MD_ALT );
// Is the user requesting that the selection list include all possible
// items without removing less likely selection candidates
// Cannot use the Alt key on windows or the disambiguation context menu is immediately
// dismissed rendering it useless.
m_skip_heuristics = evt->Modifier( MD_CTRL )
&& !evt->Modifier( MD_SHIFT )
&& !evt->Modifier( MD_ALT );
#endif
setModifiersState( evt->Modifier( MD_SHIFT ), evt->Modifier( MD_CTRL ),
evt->Modifier( MD_ALT ) );
bool modifier_enabled = m_subtractive || m_additive || m_exclusive_or;
// Single click? Select single object
if( evt->IsClick( BUT_LEFT ) )
if( evt->IsMouseDown( BUT_LEFT ) )
{
// Avoid triggering when running under other tools
if( m_toolMgr->GetCurrentTool() == this )
m_disambiguateTimer.StartOnce( 500 );
}
// Single click? Select single object
else if( evt->IsClick( BUT_LEFT ) )
{
// If the timer has stopped, then we have already run the disambiguate routine
// and we don't want to register an extra click here
if( !m_disambiguateTimer.IsRunning() )
{
evt->SetPassEvent();
continue;
}
m_disambiguateTimer.Stop();
SelectPoint( evt->Position() );
}
// right click? if there is any object - show the context menu
else if( evt->IsClick( BUT_RIGHT ) )
{
m_disambiguateTimer.Stop();
bool selectionCancelled = false;
if( m_selection.Empty() )
@ -201,6 +156,8 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// drag with LMB? Select multiple objects (or at least draw a selection box) or drag them
else if( evt->IsDrag( BUT_LEFT ) )
{
m_disambiguateTimer.Stop();
if( modifier_enabled || m_selection.Empty() )
{
selectMultiple();
@ -229,6 +186,7 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
else if( evt->IsCancelInteractive() )
{
m_disambiguateTimer.Stop();
ClearSelection();
}
@ -269,6 +227,24 @@ int PL_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
int PL_SELECTION_TOOL::disambiguateCursor( const TOOL_EVENT& aEvent )
{
VECTOR2I pos = m_toolMgr->GetMousePosition();
m_skip_heuristics = true;
SelectPoint( pos );
m_skip_heuristics = false;
return 0;
}
void PL_SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
{
m_toolMgr->ProcessEvent( EVENTS::DisambiguatePoint );
}
PL_SELECTION& PL_SELECTION_TOOL::GetSelection()
{
return m_selection;
@ -302,8 +278,7 @@ void PL_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, bool* aSelectionCan
// If still more than one item we're going to have to ask the user.
if( collector.GetCount() > 1 )
{
// Must call selectionMenu via RunAction() to avoid event-loop contention
m_toolMgr->RunAction( PL_ACTIONS::selectionMenu, true, &collector );
doSelectionMenu( &collector );
if( collector.m_MenuCancelled )
{
@ -314,12 +289,18 @@ void PL_SELECTION_TOOL::SelectPoint( const VECTOR2I& aWhere, bool* aSelectionCan
}
}
if( !m_additive && !m_subtractive && !m_exclusive_or )
ClearSelection();
bool anyAdded = false;
bool anySubtracted = false;
if( !m_additive && !m_subtractive && !m_exclusive_or )
{
if( collector.GetCount() == 0 )
anySubtracted = true;
ClearSelection();
}
if( collector.GetCount() > 0 )
{
for( int i = 0; i < collector.GetCount(); ++i )
@ -407,15 +388,15 @@ bool PL_SELECTION_TOOL::selectMultiple()
if( evt->IsDrag( BUT_LEFT ) )
{
if( !m_additive && !m_subtractive && !m_exclusive_or )
if( !m_drag_additive && !m_drag_subtractive )
ClearSelection();
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
area.SetExclusiveOr( m_exclusive_or );
area.SetAdditive( m_drag_additive );
area.SetSubtractive( m_drag_subtractive );
area.SetExclusiveOr( false );
view->SetVisible( &area, true );
view->Update( &area );
@ -835,4 +816,6 @@ void PL_SELECTION_TOOL::setTransitions()
Go( &PL_SELECTION_TOOL::RemoveItemFromSel, PL_ACTIONS::removeItemFromSel.MakeEvent() );
Go( &PL_SELECTION_TOOL::RemoveItemsFromSel, PL_ACTIONS::removeItemsFromSel.MakeEvent() );
Go( &PL_SELECTION_TOOL::SelectionMenu, PL_ACTIONS::selectionMenu.MakeEvent() );
Go( &PL_SELECTION_TOOL::disambiguateCursor, EVENTS::DisambiguatePoint );
}

View File

@ -25,6 +25,7 @@
#ifndef PL_SELECTION_TOOL_H
#define PL_SELECTION_TOOL_H
#include <tool/selection_tool.h>
#include <tool/tool_interactive.h>
#include <tool/tool_menu.h>
#include "tools/pl_selection.h"
@ -39,7 +40,7 @@ namespace KIGFX
}
class PL_SELECTION_TOOL : public TOOL_INTERACTIVE
class PL_SELECTION_TOOL : public SELECTION_TOOL, public TOOL_INTERACTIVE
{
public:
PL_SELECTION_TOOL();
@ -133,6 +134,18 @@ private:
*/
bool doSelectionMenu( COLLECTOR* aItems );
/**
* Start the process to show our disambiguation menu once the user has kept
* the mouse down for the minimum time
* @param aEvent
*/
void onDisambiguationExpire( wxTimerEvent& aEvent );
/**
* Handle disambiguation actions including displaying the menu.
*/
int disambiguateCursor( const TOOL_EVENT& aEvent );
/**
* Takes necessary action mark an item as selected.
*
@ -176,12 +189,6 @@ private:
private:
PL_EDITOR_FRAME* m_frame; // Pointer to the parent frame
PL_SELECTION m_selection; // Current state of selection
bool m_additive; // Items should be added to selection (instead of replacing)
bool m_subtractive; // Items should be removed from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Heuristics are not allowed when choosing item under cursor
};
#endif //PL_SELECTION_TOOL_H

View File

@ -106,8 +106,6 @@ public:
}
private:
///< Event handler to recalculate dynamic ratsnest.
void ratsnestTimer( wxTimerEvent& aEvent );
///< Recalculate dynamic ratsnest for the current selection.
void calculateSelectionRatsnest( const VECTOR2I& aDelta );

View File

@ -55,6 +55,8 @@ using namespace std::placeholders;
#include <connectivity/connectivity_data.h>
#include <footprint_viewer_frame.h>
#include <id.h>
#include <wx/event.h>
#include <wx/timer.h>
#include <wx/log.h>
#include "tool_event_utils.h"
#include "pcb_selection_tool.h"
@ -102,12 +104,6 @@ public:
PCB_SELECTION_TOOL::PCB_SELECTION_TOOL() :
PCB_TOOL_BASE( "pcbnew.InteractiveSelection" ),
m_frame( nullptr ),
m_additive( false ),
m_subtractive( false ),
m_exclusive_or( false ),
m_multiple( false ),
m_skip_heuristics( false ),
m_highlight_modifier( false ),
m_nonModifiedCursor( KICURSOR::ARROW ),
m_enteredGroup( nullptr ),
m_priv( std::make_unique<PRIV>() )
@ -130,6 +126,8 @@ PCB_SELECTION_TOOL::~PCB_SELECTION_TOOL()
{
getView()->Remove( &m_selection );
getView()->Remove( &m_enteredGroupOverlay );
Disconnect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
}
@ -176,6 +174,9 @@ bool PCB_SELECTION_TOOL::Init()
if( frame )
frame->AddStandardSubMenus( m_menu );
m_disambiguateTimer.SetOwner( this );
Connect( wxEVT_TIMER, wxTimerEventHandler( PCB_SELECTION_TOOL::onDisambiguationExpire ), nullptr, this );
return true;
}
@ -212,55 +213,9 @@ void PCB_SELECTION_TOOL::Reset( RESET_REASON aReason )
}
void PCB_SELECTION_TOOL::setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState )
void PCB_SELECTION_TOOL::onDisambiguationExpire( wxTimerEvent& aEvent )
{
// Set the configuration of m_additive, m_subtractive, m_exclusive_or
// from the state of modifier keys SHIFT, CTRL, ALT and the OS
// on left click, a selection is made, depending on modifiers ALT, SHIFT, CTRL:
// Due to the fact ALT key modifier cannot be used freely on Winows and Linux,
// actions are different on OSX and others OS
// Especially, ALT key cannot be used to force showing the full selection choice
// context menu (the menu is immediately closed on Windows )
//
// No modifier = select items and deselect previous selection
// ALT (on OSX) = skip heuristic and show full selection choice
// ALT (on others) = exclusive OR of selected items (inverse selection)
//
// CTRL (on OSX) = exclusive OR of selected items (inverse selection)
// CTRL (on others) = skip heuristic and show full selection choice
//
// SHIFT = add selected items to the current selection
//
// CTRL+SHIFT (on OSX) = remove selected items to the current selection
// CTRL+SHIFT (on others) = highlight net
//
// CTRL+ALT (on OSX) = highlight net
// CTRL+ALT (on others) = do nothing (same as no modifier)
//
// SHIFT+ALT (on OSX) = do nothing (same as no modifier)
// SHIFT+ALT (on others) = remove selected items to the current selection
#ifdef __WXOSX_MAC__
m_subtractive = aCtrlState && aShiftState && !aAltState;
m_additive = aShiftState && !aCtrlState && !aAltState;
m_exclusive_or = aCtrlState && !aShiftState && !aAltState;
m_skip_heuristics = aAltState && !aShiftState && !aCtrlState;
m_highlight_modifier = aCtrlState && aAltState && !aShiftState;
#else
m_subtractive = aShiftState && !aCtrlState && aAltState;
m_additive = aShiftState && !aCtrlState && !aAltState;
m_exclusive_or = !aShiftState && !aCtrlState && aAltState;
// Is the user requesting that the selection list include all possible
// items without removing less likely selection candidates
// Cannot use the Alt key on windows or the disambiguation context menu is immediately
// dismissed rendering it useless.
m_skip_heuristics = aCtrlState && !aShiftState && !aAltState;
m_highlight_modifier = aCtrlState && aShiftState && !aAltState;
#endif
m_toolMgr->ProcessEvent( EVENTS::DisambiguatePoint );
}
@ -307,19 +262,34 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
{
evt->SetPassEvent();
}
else if( evt->IsMouseDown( BUT_LEFT ) )
{
// Avoid triggering when running under other tools
if( m_toolMgr->GetCurrentTool() == this )
m_disambiguateTimer.StartOnce( 500 );
}
else if( evt->IsClick( BUT_LEFT ) )
{
// Single click? Select single object
if( m_highlight_modifier && brd_editor )
m_toolMgr->RunAction( PCB_ACTIONS::highlightNet, true );
else
// If there is no disambiguation, this routine is still running and will
// register a `click` event when released
if( m_disambiguateTimer.IsRunning() )
{
m_frame->FocusOnItem( nullptr );
selectPoint( evt->Position() );
m_disambiguateTimer.Stop();
// Single click? Select single object
if( m_highlight_modifier && brd_editor )
m_toolMgr->RunAction( PCB_ACTIONS::highlightNet, true );
else
{
m_frame->FocusOnItem( nullptr );
selectPoint( evt->Position() );
}
}
}
else if( evt->IsClick( BUT_RIGHT ) )
{
m_disambiguateTimer.Stop();
// Right click? if there is any object - show the context menu
bool selectionCancelled = false;
@ -359,6 +329,8 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsDrag( BUT_LEFT ) )
{
m_disambiguateTimer.Stop();
// Is another tool already moving a new object? Don't allow a drag start
if( !m_selection.Empty() && m_selection[0]->HasFlag( IS_NEW | IS_MOVING ) )
{
@ -439,6 +411,7 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
}
else if( evt->IsCancel() )
{
m_disambiguateTimer.Stop();
m_frame->FocusOnItem( nullptr );
if( m_enteredGroup )
@ -472,6 +445,7 @@ int PCB_SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
// Shutting down; clear the selection
m_selection.Clear();
m_disambiguateTimer.Stop();
return 0;
}
@ -822,7 +796,7 @@ bool PCB_SELECTION_TOOL::selectMultiple()
if( evt->IsDrag( BUT_LEFT ) )
{
if( !m_additive && !m_subtractive && !m_exclusive_or )
if( !m_drag_additive && !m_drag_subtractive )
{
if( m_selection.GetSize() > 0 )
{
@ -834,9 +808,9 @@ bool PCB_SELECTION_TOOL::selectMultiple()
// Start drawing a selection box
area.SetOrigin( evt->DragOrigin() );
area.SetEnd( evt->Position() );
area.SetAdditive( m_additive );
area.SetSubtractive( m_subtractive );
area.SetExclusiveOr( m_exclusive_or );
area.SetAdditive( m_drag_additive );
area.SetSubtractive( m_drag_subtractive );
area.SetExclusiveOr( false );
view->SetVisible( &area, true );
view->Update( &area );
@ -919,6 +893,19 @@ bool PCB_SELECTION_TOOL::selectMultiple()
}
int PCB_SELECTION_TOOL::disambiguateCursor( const TOOL_EVENT& aEvent )
{
VECTOR2I pos = m_toolMgr->GetMousePosition();
m_skip_heuristics = true;
selectPoint( pos );
m_skip_heuristics = false;
return 0;
}
int PCB_SELECTION_TOOL::CursorSelection( const TOOL_EVENT& aEvent )
{
CLIENT_SELECTION_FILTER aClientFilter = aEvent.Parameter<CLIENT_SELECTION_FILTER>();
@ -2587,7 +2574,7 @@ void PCB_SELECTION_TOOL::FilterCollectorForHierarchy( GENERAL_COLLECTOR& aCollec
}
// Footprints are a bit easier as they can't be nested.
if( parent && parent->GetFlags() & TEMP_SELECTED )
if( parent && ( parent->GetFlags() & TEMP_SELECTED ) )
{
// Remove children of selected items
aCollector.Remove( item );
@ -2654,4 +2641,6 @@ void PCB_SELECTION_TOOL::setTransitions()
Go( &PCB_SELECTION_TOOL::updateSelection, EVENTS::SelectedItemsMoved );
Go( &PCB_SELECTION_TOOL::SelectAll, ACTIONS::selectAll.MakeEvent() );
Go( &PCB_SELECTION_TOOL::disambiguateCursor, EVENTS::DisambiguatePoint );
}

View File

@ -33,6 +33,7 @@
#include <math/vector2d.h>
#include <project/board_project_settings.h>
#include <tool/action_menu.h>
#include <tool/selection_tool.h>
#include <tool/tool_menu.h>
#include <tools/pcb_selection_conditions.h>
#include <tools/pcb_tool_base.h>
@ -60,7 +61,7 @@ typedef void (*CLIENT_SELECTION_FILTER)( const VECTOR2I&, GENERAL_COLLECTOR&, PC
* - takes into account high-contrast & layer visibility settings
* - invokes InteractiveEdit tool when user starts to drag selected items
*/
class PCB_SELECTION_TOOL : public PCB_TOOL_BASE
class PCB_SELECTION_TOOL : public SELECTION_TOOL, public PCB_TOOL_BASE
{
public:
PCB_SELECTION_TOOL();
@ -206,12 +207,6 @@ public:
void FilterCollectedItems( GENERAL_COLLECTOR& aCollector );
private:
/**
* Set the configuration of m_additive, m_subtractive, m_exclusive_or,
* m_skip_heuristics and m_highlight_modifier
* from the state of modifier keys SHIFT, CTRL, ALT and depending on the OS
*/
void setModifiersState( bool aShiftState, bool aCtrlState, bool aAltState );
/**
* Select an item pointed by the parameter \a aWhere.
@ -259,6 +254,18 @@ private:
*/
bool doSelectionMenu( GENERAL_COLLECTOR* aItems );
/**
* Start the process to show our disambiguation menu once the user has kept
* the mouse down for the minimum time
* @param aEvent
*/
void onDisambiguationExpire( wxTimerEvent& aEvent );
/**
* Handle disambiguation actions including displaying the menu.
*/
int disambiguateCursor( const TOOL_EVENT& aEvent );
/**
* Expand the current track selection to the next boundary (junctions, pads, or all)
*/
@ -374,14 +381,6 @@ private:
SELECTION_FILTER_OPTIONS m_filter;
bool m_additive; // Add to selection (instead of replacing)
bool m_subtractive; // Remove from selection
bool m_exclusive_or; // Items' selection state should be toggled
bool m_multiple; // Multiple selection mode is active
bool m_skip_heuristics; // Heuristics are not allowed when choosing
// item under cursor
bool m_highlight_modifier; // select highlight net on left click
KICURSOR m_nonModifiedCursor; // Cursor in the absence of shift/ctrl/alt
PCB_GROUP* m_enteredGroup; // If non-null, selections are limited to