Break out tools' common menu functions and ownership

Add a class TOOL_MENU, which provides a management class for a
CONDITIONAL_MENU and a set of CONTEXT_MENUs. The aim of this is to
provide a central place where all TOOL_INTERACTIVEs can get a "basic"
context menu that either they or other tools can register new items and
sub-menus against.

This means that "top-level" tools no longer need to manage the lifetimes
of any CONTEXT_MENUs that they add, and can also delegate simple menu
display functions.
This commit is contained in:
John Beard 2017-01-12 01:22:53 +08:00 committed by Maciej Suminski
parent 3d7a509046
commit d7db84e282
7 changed files with 307 additions and 98 deletions

View File

@ -294,6 +294,7 @@ set( PCBNEW_CLASS_SRCS
tools/picker_tool.cpp
tools/zoom_tool.cpp
tools/tools_common.cpp
tools/tool_menu.cpp
tools/grid_menu.cpp
tools/zoom_menu.cpp

View File

@ -43,6 +43,7 @@
#include <router/direction.h>
#include <ratsnest_data.h>
#include <board_commit.h>
#include <scoped_set_reset.h>
#include <class_board.h>
#include <class_edge_mod.h>
@ -51,11 +52,6 @@
#include <class_zone.h>
#include <class_module.h>
#include <scoped_set_reset.h>
#include "zoom_menu.h"
#include "grid_menu.h"
#include <tools/selection_tool.h>
@ -66,8 +62,7 @@ DRAWING_TOOL::DRAWING_TOOL() :
m_view( nullptr ), m_controls( nullptr ),
m_board( nullptr ), m_frame( nullptr ), m_mode( MODE::NONE ),
m_lineWidth( 1 ),
m_menu( this ), m_contextMenu( nullptr ),
m_gridMenu( nullptr), m_zoomMenu( nullptr)
m_menu( *this )
{
}
@ -80,19 +75,7 @@ DRAWING_TOOL::~DRAWING_TOOL()
bool DRAWING_TOOL::Init()
{
// Drawing type-specific options will be added by the PCB control tool
m_menu.AddItem( COMMON_ACTIONS::zoomCenter, SELECTION_CONDITIONS::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomIn, SELECTION_CONDITIONS::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomOut , SELECTION_CONDITIONS::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomFitScreen , SELECTION_CONDITIONS::ShowAlways, 1000 );
PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
m_zoomMenu = new ZOOM_MENU( frame );
m_menu.AddMenu( m_zoomMenu, _( "Zoom" ), false, SELECTION_CONDITIONS::ShowAlways, 1000 );
m_gridMenu = new GRID_MENU( frame );
m_menu.AddMenu( m_gridMenu, _( "Grid" ), false, SELECTION_CONDITIONS::ShowAlways, 1000 );
m_menu.AddStandardSubMenus( *getEditFrame<PCB_BASE_FRAME>() );
return true;
}
@ -268,7 +251,7 @@ int DRAWING_TOOL::PlaceText( const TOOL_EVENT& aEvent )
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
@ -442,7 +425,7 @@ int DRAWING_TOOL::DrawDimension( const TOOL_EVENT& aEvent )
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
@ -658,7 +641,7 @@ int DRAWING_TOOL::PlaceDXF( const TOOL_EVENT& aEvent )
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
@ -790,7 +773,7 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
}
else if ( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsCancel() || evt->IsActivate() )
break;
@ -807,17 +790,6 @@ int DRAWING_TOOL::SetAnchor( const TOOL_EVENT& aEvent )
}
void DRAWING_TOOL::showContextMenu()
{
// Dummy selection - the drawing tool doesn't depend on a selection
SELECTION aSelection;
m_contextMenu = m_menu.Generate( aSelection );
if( m_contextMenu->GetMenuItemCount() > 0 )
SetContextMenu( m_contextMenu, CMENU_NOW );
}
bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
boost::optional<VECTOR2D> aStartingPoint )
{
@ -898,7 +870,7 @@ bool DRAWING_TOOL::drawSegment( int aShape, DRAWSEGMENT*& aGraphic,
}
else if( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
{
@ -1047,7 +1019,7 @@ bool DRAWING_TOOL::drawArc( DRAWSEGMENT*& aGraphic )
}
else if( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) )
{
@ -1260,7 +1232,7 @@ int DRAWING_TOOL::drawZone( bool aKeepout )
}
else if( evt->IsClick( BUT_RIGHT ) )
{
showContextMenu();
m_menu.ShowContextMenu();
}
else if( evt->IsClick( BUT_LEFT ) || evt->IsDblClick( BUT_LEFT ) )
{

View File

@ -28,7 +28,7 @@
#include <tools/pcb_tool.h>
#include <boost/optional.hpp>
#include "conditional_menu.h"
#include "tool_menu.h"
namespace KIGFX
{
@ -38,9 +38,6 @@ namespace KIGFX
class BOARD;
class PCB_BASE_EDIT_FRAME;
class DRAWSEGMENT;
class CONTEXT_MENU;
class GRID_MENU;
class ZOOM_MENU;
/**
* Class DRAWING_TOOL
@ -63,7 +60,7 @@ public:
///> Get the DRAWING_TOOL top-level context menu
inline CONDITIONAL_MENU& GetMenu()
{
return m_menu;
return m_menu.GetMenu();
}
///> The possible drawing modes of DRAWING_TOOL
@ -208,13 +205,8 @@ private:
/// Stores the current line width for multisegment drawing.
unsigned int m_lineWidth;
/// Menu displayed by the tool.
CONDITIONAL_MENU m_menu;
/// Pointers to context menus
CONTEXT_MENU* m_contextMenu;
GRID_MENU* m_gridMenu;
ZOOM_MENU* m_zoomMenu;
/// Menu model displayed by the tool.
TOOL_MENU m_menu;
// How does line width change after one -/+ key press.
static const int WIDTH_STEP;

View File

@ -50,8 +50,6 @@ using namespace std::placeholders;
#include "selection_tool.h"
#include "selection_area.h"
#include "zoom_menu.h"
#include "grid_menu.h"
#include "bright_box.h"
#include "common_actions.h"
@ -70,8 +68,7 @@ public:
SELECTION_TOOL::SELECTION_TOOL() :
PCB_TOOL( "pcbnew.InteractiveSelection" ),
m_frame( NULL ), m_additive( false ), m_multiple( false ),
m_locked( true ), m_menu( this ), m_contextMenu( NULL ), m_selectMenu( NULL ),
m_zoomMenu( NULL ), m_gridMenu( NULL )
m_locked( true ), m_menu( *this )
{
// Do not leave uninitialized members:
m_preliminary = false;
@ -81,11 +78,6 @@ SELECTION_TOOL::SELECTION_TOOL() :
SELECTION_TOOL::~SELECTION_TOOL()
{
getView()->Remove( &m_selection );
delete m_contextMenu;
delete m_selectMenu;
delete m_zoomMenu;
delete m_gridMenu;
}
@ -96,25 +88,17 @@ bool SELECTION_TOOL::Init()
auto showSelectMenuFunctor = ( S_C::OnlyType( PCB_VIA_T ) || S_C::OnlyType( PCB_TRACE_T ) )
&& S_C::Count( 1 );
m_selectMenu = new SELECT_MENU;
m_selectMenu->SetTool( this );
auto selectMenu = std::make_shared<SELECT_MENU>();
selectMenu->SetTool( this );
m_menu.AddSubMenu( selectMenu );
m_menu.AddMenu( m_selectMenu, _( "Select..." ), false, showSelectMenuFunctor );
auto& menu = m_menu.GetMenu();
menu.AddMenu( selectMenu.get(), _( "Select..." ), false, showSelectMenuFunctor );
// only show separator if there is a Select menu to show above it
GetMenu().AddSeparator( showSelectMenuFunctor, 1000 );
menu.AddSeparator( showSelectMenuFunctor, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomCenter, S_C::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomIn, S_C::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomOut , S_C::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomFitScreen , S_C::ShowAlways, 1000 );
PCB_BASE_FRAME* frame = getEditFrame<PCB_BASE_FRAME>();
m_zoomMenu = new ZOOM_MENU( frame );
m_menu.AddMenu( m_zoomMenu, _( "Zoom" ), false, S_C::ShowAlways, 1000 );
m_gridMenu = new GRID_MENU( frame );
m_menu.AddMenu( m_gridMenu, _( "Grid" ), false, S_C::ShowAlways, 1000 );
m_menu.AddStandardSubMenus( *getEditFrame<PCB_BASE_FRAME>() );
return true;
}
@ -177,11 +161,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( emptySelection )
selectPoint( evt->Position() );
delete m_contextMenu;
m_contextMenu = m_menu.Generate( m_selection );
if( m_contextMenu->GetMenuItemCount() > 0 )
SetContextMenu( m_contextMenu, CMENU_NOW );
m_menu.ShowContextMenu( m_selection );
m_preliminary = emptySelection;
}
@ -288,11 +268,7 @@ int SELECTION_TOOL::Main( const TOOL_EVENT& aEvent )
if( m_preliminary )
clearSelection();
if( evt->Parameter<CONTEXT_MENU*>() == m_contextMenu )
{
delete m_contextMenu;
m_contextMenu = NULL;
}
m_menu.CloseContextMenu( evt );
}
}

View File

@ -33,15 +33,12 @@
#include <tool/context_menu.h>
#include "selection_conditions.h"
#include "conditional_menu.h"
#include "tool_menu.h"
class PCB_BASE_FRAME;
class SELECTION_AREA;
class BOARD_ITEM;
class GENERAL_COLLECTOR;
class SELECT_MENU;
class ZOOM_MENU;
class GRID_MENU;
namespace KIGFX
{
@ -198,7 +195,7 @@ public:
inline CONDITIONAL_MENU& GetMenu()
{
return m_menu;
return m_menu.GetMenu();
}
///> Checks if the user has agreed to modify locked items for the given selection.
@ -392,14 +389,8 @@ private:
/// Determines if the selection is preliminary or final.
bool m_preliminary;
/// Menu displayed by the tool.
CONDITIONAL_MENU m_menu;
/// Pointers to context menus
CONTEXT_MENU* m_contextMenu;
SELECT_MENU* m_selectMenu;
ZOOM_MENU* m_zoomMenu;
GRID_MENU* m_gridMenu;
/// Menu model displayed by the tool.
TOOL_MENU m_menu;
};
#endif

104
pcbnew/tools/tool_menu.cpp Normal file
View File

@ -0,0 +1,104 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 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 2
* 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/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "tool_menu.h"
#include <tool/context_menu.h>
#include "common_actions.h"
#include "zoom_menu.h"
#include "grid_menu.h"
#include "selection_tool.h" // For SELECTION
TOOL_MENU::TOOL_MENU( TOOL_INTERACTIVE& aTool ) :
m_menu( &aTool ),
m_tool( aTool )
{
}
TOOL_MENU::~TOOL_MENU()
{
}
CONDITIONAL_MENU& TOOL_MENU::GetMenu()
{
return m_menu;
}
void TOOL_MENU::AddSubMenu( std::shared_ptr<CONTEXT_MENU> aSubMenu )
{
// store a copy of the menu (keeps a reference)
m_subMenus.push_back( std::move( aSubMenu ) );
}
void TOOL_MENU::ShowContextMenu( SELECTION& aSelection )
{
m_contextMenu = std::unique_ptr<CONTEXT_MENU>(
m_menu.Generate( aSelection ) );
if( m_contextMenu->GetMenuItemCount() > 0 )
{
m_tool.SetContextMenu( m_contextMenu.get(), CMENU_NOW );
}
}
void TOOL_MENU::ShowContextMenu()
{
SELECTION dummySelection;
ShowContextMenu( dummySelection );
}
void TOOL_MENU::CloseContextMenu( OPT_TOOL_EVENT& evt )
{
// m_contextMenu can be null here, that's OK
if( evt->Parameter<CONTEXT_MENU*>() == m_contextMenu.get() )
{
m_contextMenu = nullptr;
}
}
// This makes the factory functions a bit less verbose
using S_C = SELECTION_CONDITIONS;
void TOOL_MENU::AddStandardSubMenus( EDA_DRAW_FRAME& aFrame )
{
m_menu.AddItem( COMMON_ACTIONS::zoomCenter, S_C::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomIn, S_C::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomOut, S_C::ShowAlways, 1000 );
m_menu.AddItem( COMMON_ACTIONS::zoomFitScreen, S_C::ShowAlways, 1000 );
m_menu.AddMenu( createOwnSubMenu<ZOOM_MENU>( &aFrame ).get(),
_( "Zoom" ), false, S_C::ShowAlways, 1000 );
m_menu.AddMenu( createOwnSubMenu<GRID_MENU>( &aFrame ).get(),
_( "Grid" ), false, S_C::ShowAlways, 1000 );
}

173
pcbnew/tools/tool_menu.h Normal file
View File

@ -0,0 +1,173 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2017 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 2
* 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/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef TOOLS_TOOL_MENU__H_
#define TOOLS_TOOL_MENU__H_
#include "conditional_menu.h"
#include "pcb_tool.h"
#include <vector>
#include <memory>
class CONTEXT_MENU;
/**
* Class TOOL_MENU
*
* Manages a CONDITIONAL_MENU and some number of
* CONTEXT_MENUs as sub-menus
*
* Each "top-level" interactive tool can have one of these,
* and other tools can contribute CONTEXT_MENUS to it.
*
* There are also helper functions for adding common sets of
* menu items, for example zoom and grid controls.
*/
class TOOL_MENU
{
public:
/**
* Function TOOL_MENU
*
* Construct a new TOOL_MENU for a specific tool. This menu
* will be empty - it's up to the caller to add the relevant
* items. This can be done directy, using the reference returned
* by TOOL_MENU::GetMenu(), or the helpers for common command sets
* can be used, or a combination of the two.
*/
TOOL_MENU( TOOL_INTERACTIVE& aTool );
/**
* Destructor, also destructs any submenus created with
* TOOL_MENU::CreateSubMenu().
*/
~TOOL_MENU();
/**
* Function GetMenu
*
* @return reference to the CONDITIONAL_MENU model, which can be
* used tby tools to add their own commands to the menu.
*/
CONDITIONAL_MENU& GetMenu();
/**
* Function CreateSubMenu
*
* Store a submenu of this menu model. This can be shared with
* other menu models.
*
* It is the callers responsibility to add the submenu to
* m_menu (via GetMenu() ) in the right way, as well
* as to set the tool with SetTool(), since it's not a given
* that the menu's tool is the tool that directly owns this
* TOOL_MENU
*
* @param aSubMenu: a sub menu to add
*/
void AddSubMenu( std::shared_ptr<CONTEXT_MENU> aSubMenu );
/**
* Function ShowContextMenu
*
* Helper function to set and immediately show a CONTEXT_MENU
* based on the internal CONDITIONAL_MENU in concert with
* the given SELECTION
*
* You don't have to use this function, if the caller has a
* different way to show the menu, it can create one from
* the reference returned by TOOL_MENU::GetMenu(), but it will
* have to be managed externally to this class.
*/
void ShowContextMenu( SELECTION& aSelection );
/**
* Function ShowContextMenu
*
* Helper function to show a context menu without any selection
* for tools that can't make selections.
*/
void ShowContextMenu();
/**
* Function CloseContextMenu
*
* Helper function to close a menu previously opened with
* ShowContextMenu(), if a suitable event is received
*/
void CloseContextMenu( OPT_TOOL_EVENT& evt );
/**
* Function CreateBasicMenu
*
* Construct a "basic" menu for a tool, containing only items
* that apply to all tools (e.g. zoom and grid)
*/
void AddStandardSubMenus( EDA_DRAW_FRAME& aFrame );
private:
/*!
* Helper function for factories to abe able to easily add
* their own new sub menus. This sets the tool to the TOOL_MENUs
* owner and adds to the store.
*
* Note, this won't share the menu between multiple invocations
* of the factory. But if different top-level tools are using the
* same factory, which one would be used for SetTool()?
*/
template <typename T, typename ... Args>
std::shared_ptr<T> createOwnSubMenu( Args&& ... args )
{
auto subMenuPtr = std::make_shared<T>( args ... );
subMenuPtr->SetTool( &m_tool );
AddSubMenu( subMenuPtr );
return subMenuPtr;
}
/**
* The conditional model of the menu displayed by the tool
*/
CONDITIONAL_MENU m_menu;
/**
* The actual menu displayed by the tool
*/
std::unique_ptr<CONTEXT_MENU> m_contextMenu;
/**
* The tool that owns this menu
*/
TOOL_INTERACTIVE& m_tool;
/**
* Lifetime-managing container of submenus
*/
std::vector<std::shared_ptr<CONTEXT_MENU> > m_subMenus;
};
#endif // TOOLS_TOOL_MENU__H_