diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index dea6f4036a..8e3e66057d 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -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 diff --git a/common/hotkey_store.cpp b/common/hotkey_store.cpp index 46f1aa083a..e11cd1ffee 100644 --- a/common/hotkey_store.cpp +++ b/common/hotkey_store.cpp @@ -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[] = { diff --git a/common/tool/actions.cpp b/common/tool/actions.cpp index 94bc8502bb..1c6f8aded2 100644 --- a/common/tool/actions.cpp +++ b/common/tool/actions.cpp @@ -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" ); + diff --git a/common/tool/selection_tool.cpp b/common/tool/selection_tool.cpp new file mode 100644 index 0000000000..6153aa3369 --- /dev/null +++ b/common/tool/selection_tool.cpp @@ -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 + + + +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; +} diff --git a/eeschema/tools/ee_selection_tool.cpp b/eeschema/tools/ee_selection_tool.cpp index 23c0a964b9..b444bb1343 100644 --- a/eeschema/tools/ee_selection_tool.cpp +++ b/eeschema/tools/ee_selection_tool.cpp @@ -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( 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( 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 ); } diff --git a/eeschema/tools/ee_selection_tool.h b/eeschema/tools/ee_selection_tool.h index e4d25d0b66..93359c467f 100644 --- a/eeschema/tools/ee_selection_tool.h +++ b/eeschema/tools/ee_selection_tool.h @@ -25,7 +25,7 @@ #ifndef KICAD_SCH_SELECTION_TOOL_H #define KICAD_SCH_SELECTION_TOOL_H -#include +#include #include #include #include @@ -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 diff --git a/gerbview/tools/gerbview_selection_tool.cpp b/gerbview/tools/gerbview_selection_tool.cpp index 21a8681bfa..629cae2ec3 100644 --- a/gerbview/tools/gerbview_selection_tool.cpp +++ b/gerbview/tools/gerbview_selection_tool.cpp @@ -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 ) ) diff --git a/gerbview/tools/gerbview_selection_tool.h b/gerbview/tools/gerbview_selection_tool.h index e93835de7c..d33c0ea76e 100644 --- a/gerbview/tools/gerbview_selection_tool.h +++ b/gerbview/tools/gerbview_selection_tool.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -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. }; diff --git a/include/tool/actions.h b/include/tool/actions.h index 7e24e9c6f7..9b46d23f51 100644 --- a/include/tool/actions.h +++ b/include/tool/actions.h @@ -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 diff --git a/include/tool/selection_tool.h b/include/tool/selection_tool.h new file mode 100644 index 0000000000..ec7cc25a17 --- /dev/null +++ b/include/tool/selection_tool.h @@ -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 +#include + +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_ */ diff --git a/include/tool/tool_event.h b/include/tool/tool_event.h index c75603d1dd..601977e977 100644 --- a/include/tool/tool_event.h +++ b/include/tool/tool_event.h @@ -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; diff --git a/pagelayout_editor/tools/pl_selection_tool.cpp b/pagelayout_editor/tools/pl_selection_tool.cpp index 0be8941b47..16ddd2fb1d 100644 --- a/pagelayout_editor/tools/pl_selection_tool.cpp +++ b/pagelayout_editor/tools/pl_selection_tool.cpp @@ -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 ); } diff --git a/pagelayout_editor/tools/pl_selection_tool.h b/pagelayout_editor/tools/pl_selection_tool.h index 2f795c8b92..fbdf117fb9 100644 --- a/pagelayout_editor/tools/pl_selection_tool.h +++ b/pagelayout_editor/tools/pl_selection_tool.h @@ -25,6 +25,7 @@ #ifndef PL_SELECTION_TOOL_H #define PL_SELECTION_TOOL_H +#include #include #include #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 diff --git a/pcbnew/tools/board_inspection_tool.h b/pcbnew/tools/board_inspection_tool.h index beed2eb67a..4788d2e899 100644 --- a/pcbnew/tools/board_inspection_tool.h +++ b/pcbnew/tools/board_inspection_tool.h @@ -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 ); diff --git a/pcbnew/tools/pcb_selection_tool.cpp b/pcbnew/tools/pcb_selection_tool.cpp index 9f115ee2e5..2adbe76861 100644 --- a/pcbnew/tools/pcb_selection_tool.cpp +++ b/pcbnew/tools/pcb_selection_tool.cpp @@ -55,6 +55,8 @@ using namespace std::placeholders; #include #include #include +#include +#include #include #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() ) @@ -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(); @@ -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 ); } diff --git a/pcbnew/tools/pcb_selection_tool.h b/pcbnew/tools/pcb_selection_tool.h index b131dd35a4..514194929f 100644 --- a/pcbnew/tools/pcb_selection_tool.h +++ b/pcbnew/tools/pcb_selection_tool.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -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